Why are you changing declarations everywhere to use auto?

Sigh – I made precisely that point earlier above
However, that’s not the issue and my example was just intended to demonstrate the issue – those things could all been in different files. Even if one line was directly after the other, you would still be seeing an error that there’s no matching function for the call to ‘bar’ and chasing that down rather than immediately detecting the real problem, i.e. the return type of foo() has changed under the covers.

1 Like

You can’t pass a parameter to a function by auto to a different file, so I’m not sure how that would happen, unless you have global variables all over the place.

You really need to start using a good IDE. CLion, Visual Studio (with Resharper), AppCode, etc, will all catch these errors even during typing.

Personally, even when reading code in a text editor, usually what I’m after isn’t the types - in a public API the types will appear anyway, and in implementations I mostly need to know what the code is trying to do, and read that without the noise of very long type names which usually adds very little information - if I can’t understand by context if this is a String or Rect, it’s very likely the type names aren’t what’s gonna help me understand it.

6 Likes

Really? Should I throw away the chisel and rock on which I was writing code? What do you do if you’re reading the code snippet on a webpage? Or in a book? Your crutches (oops, tools) aren’t always there to save you.

You are completely missing the point. The issue in your example is not that you accidentaly [sic] thought it was a different type/

Of course it was perfectly fine when you wrote it and obviously your code completion tool will have helped you find the correct method.

However, when the type returned by getContainer() changes two months later and you’re rebuilding, the error message from Xcode and any other IDE (all of which are generally using the same underlying compilers by the way and just propagate the error message produced by that compiler) will all point at vec.add() as being the problem.

Of course the real problem was due to the change to getContainer(). If you had declared the actual type, the compiler would have correctly pointed out that the type was now wrong. If someone else is dealing with your code (or you’ve forgotten what you wrote 2 months ago, a very common phenomenon), they’re wondering what the heck is wrong with vec? How do you know that the problem is not because someone deleted the add method, perhaps?

In any case, this was just one of many examples of issues that are due to the use of auto and why I don’t like that particular construct in most cases.

Fundamentally, I dislike that I cannot see what kind of object is coming back from a function call without using a tool.

2 Likes

Working on C++ projects on a daily basis now, I simply don’t see the kind of errors you are trying to describe here occuring in this context. E.g. I can remeber a lot of bugs from stupid to subtle, but I seriously can’t remember any that would have been avoided when auto would have been avoided.

Two things:
Especially when working with an existing codebase, changing to auto where possible makes the codebase better. From my experience, e.g. the number of signed/unsigned type conversion warnings when interfacing between juce and standard library types gets smaller when using auto for size types everywhere.

And if working with highly templated code, (which I tend to do more and more) typenames become really really long. Using auto just sets my focus right, just for an example, a code snippet that does not look so unrealistic from my experience:

// Some functions
const juce::AudioBuffer<float>& getInputSignalBuffer();
std::vector<float> getVec();
std::unique_ptr<SomeObjectWithLongTypeName> createFromBufferAndVec (const juce::AudioBuffer<float>& buffer, 
                                                                    const std::vector<float> vector);

void withoutAuto()
{
    const juce::AudioBuffer<float>& inputSignalBuffer = getInputSignalBuffer();
    std::vector<float> vector = getVec();
    
    int numSaps = buffer.getNumSamples();
    size_t numVecElems = vector.size();

    jassert (numVecElems == numSamps);

    std::unique_ptr<SomeObjectWithLongTypeName> obj = createFromBufferAndVec (buffer, vector);

    obj->function();

    // ...
}

void withAuto()
{
    const auto& inputSignalBuffer= getBuffer();
    auto vector = getVec();
    
    auto numSaps = inputSignalBuffer.getNumSamples();
    auto numVecElems = vector.size();

    jassert (numVecElems == numSamps);

    auto obj = createFromBufferAndVec (inputSignalBuffer, vector);

    obj->function();

    // ...
}

It might be a matter of personal preference, but I feel a lot more safe navigating through the second version. And if I got the second one for code review I would likely see much faster what’s going on in there. What’s important here are just good function names, that give you a good idea about the kind of object returned or expected here and good variable names. And then in the end it doesn’t really matter to understand the code if my non-negative size variable is signed or unsigned, it will work in either way (and I don’t want to start a discussion on signed vs unsigned size types here :wink: ), what’s much more important is to see that a variable holds this kind of information.

3 Likes

Understood - and I gave examples of such things. But those weren’t my primary reasons for not liking auto.

My issue is around reading (often someone else’s code) and/or maintaining it - there’s too much hidden information. I think perhaps people here have forgotten what it’s like for someone coming into a code base who hasn’t seen it before and is not already deeply familiar with the libraries being used.

If you don’t already know what kind of object is coming back from some function, which will most certainly be the case when somebody new is coming on board, the use of auto makes it harder to figure out what’s going on. It takes longer to understand – that, to me, is an unnecessary cost.

In any case, I recognize that discussing this is pointless.

2 Likes

I did refer to this situation about two lines after that, since I do spend a long time reading code in text editors or on GitHub, and in all these cases I think the code is significantly clearer with auto in it, since I now have less noise and can follow along code intention easier.

Let’s break down this situation. You/someone else decided to change the return type of a function used by some code to something completely different, and as expected, broke that code.

In that case, the user code will have to deal with that change anyway, and read both the return type of the function and the expected user type.

So I don’t think not using auto would save you much browsing even in that case.
But using auto would save you much browsing in the other case, which is you only changed the name of the type, or decided to return a parent class instead of an inherited class - both will not break user code that used auto (and they shouldn’t, since semantics are identical), and that will make a large scale refactoring like extracting a base class significantly easier.

Now, I can’t convince you to use or like a different style, and many people prefer an explicit style with more information in-place, even with the cost of increased noise. But let’s not make this problem bigger than what it is, as there’s a lot of code in the world that’s using auto now, and many people do understand it quite well, including the JUCE code.

Explicit type declaration does not imply more noise. In fact, now that I think about that, explicit

All those great tools you mentioned will have no problem with those refactoring operations even when type declarations are used.

Also, good luck figuring out why you have a memory leak because you can’t tell from the type that you have to be responsible for cleaning up.

I wonder if your anti-auto arguments are predicated on all code you read was written using perfectly clear and obvious function names and that is most certainly not going to be true for a huge percentage of code that gets written.

I think the fundamental disconnect we have is that I don’t consider type declarations to be a distraction - I think they’re a fundamental tool to help maintain and understand code.

3 Likes

The thing is that with modern cpp you should never return naked pointer to avoid that kind of issue, so this should not happen…

auto is great when used in the right places. This is what I mostly see in Juce code.
But sure, you can write horrible code with auto

Absolutely correct — and if I had a penny for every single thing that one shouldn’t do, I could retire immediately.

The thing is, you can’t guarantee that some other library is going to contain, or some other programmer,is going to write, perfect code.

Explicit type declarations (among other techniques such as never returning from the middle of a function, a really horrible modern trend, started, I think, by the Java crowd) help to minimize such errors.

1 Like

This has turned into a religious argument - nobody’s mind is getting changed here. I just discovered that I can’t actually delete this topic so I’m just going to stop watching it.

The debate has become pointless.

Refactoring tools are great when you refactor your own code, but obviously can’t work if you’re writing a library (like JUCE) and have no control over user code. Same if you’re using library code, which might change return types to equivalent types.

I’m guessing you refer to raw pointers? I’m not sure how knowing the return type T* helps you there, with raw pointers you’ll have to read the code and/or documentation to know how to deal with memory… To me as long as I see auto* I already know the problems as they are shared with any kind of raw pointer usage.

Sure, some code is unclear, both with or without using auto.
But, I think from my own experience auto pushes you toward correct naming of variables/functions in your own code, and increases readability as @PluginPenguin showed in his example which I found is much closer to most code I read/write where a function is only extracting some attributes from types and then passing it along using some logic.

TBH it was from the start, since it was discussed just 2 months ago, and no new arguments were presented :wink:

6 Likes

100%, because if people can’t understand why auto is a clear benefit, even when demonstrated, they are a lost cause and may as well be left in their stubborn ways of coding like it’s still 2010. :joy:

3 Likes

The “debate” over whether auto is a good thing is a bit like the “debate” over whether climate change is man-made or the “debate” over whether the earth is round or flat. OK, maybe that’s a bit of a harsh comparison, but you get my point :slight_smile:

Anyone who’s still unconvinced probably just has a bit of catching up to do. You’re not going to suddenly find a compelling anti-auto argument that wasn’t fully considered, debated and addressed by the C++ committee more than 10 years ago. Countless billions of lines of code have been written with auto since 2011, and if it caused problems, people would have just stopped using it by now.

Back in 2011 when it first came out, I was a bit sceptical myself about whether it’d make code less readable, but I read what the experts were saying, tried it out myself and realised that it really does make the language much better.

Nowadays you’re unlikely to find a respectable C++ guru who doesn’t push the pro-auto message. Is that because these “so called experts” are all part of a global conspiracy to make programming harder? Well, I can’t discuss that without breaking my oaths of allegiance to the glorious illuminati, but you probably shouldn’t worry about it too much.

15 Likes

My understanding of part of the readability/learnability defense of AAA is that there is an underlying expectation or intention that your apis are going to be designed with highly generic types/interfaces anyway, at which point the benefit of the explicit type becomes less valuable, i.e. you can’t glean much about a type beyond how it’s used. And because your type is already highly generic then including the explicit name does have a propensity to do little more than introduce noise and I believe most of the other arguments regarding naming and usages shine a bit brighter.

There are certainly times when I’m reading code and wishing that the type would have been specified and I’d guess there are usages where the api isn’t fully generic and so there is some cost in traditional readability, but when I frame it with the intentions of the underlying expectations I find myself to be more understanding of it and more willing to embrace using it myself.

I found this words (42:24) from Bjarne Stroustrup interesting…

…because i’m a lost cause with a bit of catching up to do. :grinning_face_with_smiling_eyes:

Seen on the internet:

C++: an octopus made by nailing extra legs onto a dog

4 Likes