On 07/12/2018 05:17 PM, Richard Sandiford wrote:
> Pedro Alves <pal...@redhat.com> writes:

>> (an
>>> alternative to pointers is to return a struct with the wide int result
>>> and the overflow flag),
>>
>> +1.  I've been pushing GDB in that direction whenever possible.
> 
> I agree that can sometimes be better.  I guess it depends on the
> context.  If a function returns a bool and some other data that has no
> obvious "failure" value, it can be easier to write chained conditions if
> the function returns the bool and provides the other data via an output
> parameter.

I agree it depends on context, though your example sounds like a
case for std::optional<T>.  (We have gdb::optional<T> in gdb, since
std::optional is C++17 and gdb requires C++11.  LLVM has a similar
type, I believe.)

> 
>>> but ...
>>>
>>>> +int *elt = &amp;array[i];  // OK
>>>> +int &amp;elt = array[i];   // Please avoid
>>>
>>> ... this seems unnecessary. If the function is so long that the fact
>>> elt is a reference can easily get lost, the problem is the length of
>>> the function, not the use of a reference.
>>>
>>
>> +1000.  This seems really unnecessary.  References have the advantage
>> of implicit never-null semantics, for instance.
> 
> The nonnullness is there either way in the above example though.
> 
> I don't feel as strongly about the non-const-reference variables, but for:
> 
>      int &elt = array[i];
>      ...
>      elt = 1;
> 
> it can be easy to misread that "elt = 1" is changing more than just
> a local variable.

I think that might be just the case for people who haven't used
references before, and once you get some exposure, the effect
goes away.  See examples below.

> 
> I take Jonathan's point that it shouldn't be much of a problem if
> functions are a reasonable size, but we've not tended to be very
> good at keeping function sizes down.  I guess part of the problem
> is that even functions that start off small tend to grow over time.
> 
> We have been better at enforcing more specific rules like the ones
> in the patch.  And that's easier to do because a patch either adds
> references or it doesn't.  It's harder to force (or remember to force)
> someone to split up a function if they're adding 5 lines to one that is
> already 20 lines long.  Then for the next person it's 25 lines long, etc.
> 

> Also, the "int *elt" approach copes with cases in which "elt" might
> have to be redirected later, so we're going to need to use it in some
> cases.  

Sure, if you need to reseat, certainly use a pointer.  That actually
highlights an advantage of references -- the fact that you are sure
nothing could reseat it.  With a local pointer, you need to be mindful
of that.  "Does this loop change the pointer?"  Etc.  If the semantics
of the function call for having a variable that is not meant to be
reseated, why not allow expressing it with a reference.
"Write what you mean."  Of course, just like all code, there's going
to be a judgment call.

A place where references frequently appear is in C++11 range-for loops.
Like, random example from gdb's codebase:

 for (const symbol_search &p : symbols)

GCC doesn't yet require C++11, but it should be a matter
of time, one would hope.

Other places references appear often is in coming up with
an alias to avoid repeating long expressions, like
for example (again from gdb):

      auto &vec = objfile->per_bfd->demangled_hash_languages;
      auto it = std::lower_bound (vec.begin (), vec.end (),
                                  MSYMBOL_LANGUAGE (sym));
      if (it == vec.end () || *it != MSYMBOL_LANGUAGE (sym))
        vec.insert (it, MSYMBOL_LANGUAGE (sym));

I don't think the fact that "vec" is a reference here confuses
anyone.

That was literally a random sample (grepped for "&[a-z]* = ") so
I ended up picking one with C++11 "auto", but here's another
random example, spelling out the type name, similarly using
a reference as a shorthand alias:

  osdata_item &item = osdata->items.back ();

  item.columns.emplace_back (std::move (data->property_name),
                             std::string (body_text));

> It's then a question of whether "int &elt" is useful enough that
> it's worth accepting it too, and having a mixture of styles in the codebase.
I don't see it as a mixture of styles, as I don't see
pointers and references the exact same thing, but rather
see references as another tool in the box.

Thanks,
Pedro Alves

Reply via email to