News for hackers. Stuff that matters.
Re:Const all the things? by lelanthran on Monday January 24, 2022 @04:37AM (#30055290)
> OP says this like this is an industry rule of sorts but I've pretty much always seen non-const references for this. Using pointers is terrible because now you have to add a check for null inside your function, as its API contract says "I can accept null values", while references make it clear that it does not make sense to call if you don't have a correct object to pass it.

The other side of the coin is that it hurts readability if you are passing non-const references. When a function takes a non-const reference, the caller looks like this:

    foo (bar); // Have to read the header to know if bar is modified.
When a function takes a pointer, the caller looks like this:

    foo (&bar); // Immediately clear that we cannot gloss over this line in code-review.
Since I read code much more than I write it, it's easier for me to determine that local non-const variables that are used as arguments to a function are not getting modified.

A better (but not best) solution might be to have the language recognise and enforce nullability on arguments. Still not perfect (pointer may be non-null but invalid), but better.

One other benefit pointer parameters have over reference parameters is that the API contract is more flexible. For example, I have a function that accepts a network connection,

    mynet_accept(int listener_fd, char **remote_ip, uint16_t *remote_port);
Having the caller specify NULL for `remote_ip` means that the callee just ignores the parameter. Same for `remote_port`.

If that was changed to use references, the caller will have to declare variables just to satisfy the parameter list, even if they never intend to use them.

Re:Const all the things? by jcelerier on Monday January 24, 2022 @04:49AM (#30055386)
> The other side of the coin is that it hurts readability if you are passing non-const references. When a function takes a non-const reference, the caller looks like this:

This has honestly never ever been an issue for me. I don't remember the last time where I passed an argument to a C++ function and actually had to wonder if the argument was modified or not. Like, what if they are being modified if that's the called function has to do anyways ? And even if it's const, the implementation can just remove the const which while not a very nice thing to do, is legal in many cases.

    $ cd
    $ rg "const_cast" -g '*.cpp' | wc -l 
    2004
(and that does not even cover the c-style casts)

> mynet_accept(int listener_fd, char *remote_ip, uint16_t *remote_port);

For this exact case my go-to solution nowadays is

    struct connection_info { 
      int fd;
      std::string ip;
      uint16_t port;
    };  

    mynet_accept({ .fd = the_socket, .port = 4556 });
which is I think much more readable than any of the alternatives ( actual example for pretty much this exact case: https://github.com/ossia/libossia/blob/master/examples/Netwo... )
Re:Const all the things? by lelanthran on Monday January 24, 2022 @06:14AM (#30056124)
> I don't remember the last time where I passed an argument to a C++ function and actually had to wonder if the argument was modified or not. Like, what if they are being modified if that's the called function has to do anyways

That's not what I was complaining about. I am saying that, as a reader of the code, it is easier to reason about what happens in this situation:

    foo (&bar);
    ...
    if (bar) { do something }
than in this situation:

    foo (bar);
    ...
    if (bar) { do something }


> mynet_accept({ .fd = the_socket, .port = 4556 });

That's not functionally the same as the example I posted. In any case, I was using the example to point out that using pointers provides a more flexible API contract.

When using pointers, the contract can state that any parameters that are NULL are ignored, whether they are out parameters or not.

Re:Const all the things? by tsimionescu on Monday January 24, 2022 @05:14AM (#30055614)
> Like, what if they are being modified if that's the called function has to do anyways ?

The idea is that you have some code like this:

  x = some_func();
  do_something(x);
  if x == 0 {
    do_something_else();
  }
You're trying to track down why do_something_else() wasn't called, let's say. Do you have to look at the definition of do_something()? If your coding standards say "no non-const references", then no. If do_something() was modifying x, it would have looked differently, so you can ignore it.
Re:Const all the things? by gpderetta on Monday January 24, 2022 @09:31AM (#30058006)
To stay on topic, if x was declared const, you wouldn't have to guess. The compiler would tell you.
Re:Const all the things? by lelanthran on Monday January 24, 2022 @11:50AM (#30060204)
> To stay on topic, if x was declared const, you wouldn't have to guess. The compiler would tell you.

Frequently it can't be declared constant, because it is being changed in that scope as it runs.

Re:Const all the things? by tsimionescu on Monday January 24, 2022 @04:46PM (#30064516)
This was a discussion about passing out (or inout) parameters as `T&` vs `T*`. By definition, these parameters can't be const, so const is irrelevant to this thread.
Re:Const all the things? by gpderetta on Sunday January 30, 2022 @09:46AM (#30137610)
Good point.
Re:Const all the things? by jcelerier on Monday January 24, 2022 @06:26AM (#30056221)
> You're trying to track down why do_something_else() wasn't called, let's say

Then I put a breakpoint at that line and hit f5 and don't try to guess what may or may not happen because in the end that's the only way to know what actually happens

Re:Const all the things? by lelanthran on Monday January 24, 2022 @07:08AM (#30056639)
> Then I put a breakpoint at that line and hit f5 and don't try to guess what may or may not happen because in the end that's the only way to know what actually happens

There's no need to guess if the parameter is passed by value only. When pointers are used visual inspection alone is enough to ensure that the reader knows what he value of the argument is after the callee returns.

I said that it's easier to read when pointers are used, and you propose stepping through the code as an alternative. Surely you see my point now?

Re:Const all the things? by tsimionescu on Monday January 24, 2022 @07:00AM (#30056567)
You can't always reproduce a bug. Also, do you Step Over do_something(x) or Step Into? You definitely step into if it's do_something(&x).
Re:Const all the things? by asddubs on Monday January 24, 2022 @05:31AM (#30055774)
> $ rg "const_cast" -g '*.cpp' | wc -l

not really a fair count though, since those casts may also be adding const

Re:Const all the things? by mannykannot on Monday January 24, 2022 @10:20AM (#30058678)
I am missing your readability point here: If I am reading you correctly, you seem to be saying that if you come across foo(&bar) then you need to look at what foo is doing in order to know if bar might be changed, while in the case of foo(bar), looking at foo's declaration might be sufficient. Is the latter not sometimes more readable than the former, and never less?
Re:Const all the things? by lelanthran on Tuesday January 25, 2022 @04:22AM (#30069916)
> I am missing your readability point here: If I am reading you correctly, you seem to be saying that if you come across foo(&bar) then you need to look at what foo is doing in order to know if bar might be changed, while in the case of foo(bar), looking at foo's declaration might be sufficient. Is the latter not sometimes more readable than the former, and never less?

Almost never, in my experience. During code review, when I see this:

    foo(&bar);
I know quite well the author of that code knows that bar might be changed. There are no questions to ask him here, and no feedback to be given.

When I see this:

    foo(bar);
I've no idea if the author realises that `bar` might be have a different value after `foo()` returns. I have to ask the question "Do you know that `var` might be changed by `foo`? Did you test with the different program states that result in the value that `foo` may assign to `bar`?"

In a place where the convention is to use pointers instead of mutable references I never have to ask the question - the code review of the implementation of `foo` would have changed the mutable reference parameter to a pointer parameter.

I read code much more often than I write it. References hurt readability.

Re:Const all the things? by mannykannot on Tuesday January 25, 2022 @10:16AM (#30072690)
That is an interesting point of view. I am usually reading code in order to understand what it does (or where it goes wrong) without the author being present, where the proper use of const in declarations can save a lot of time.