Almost Always Auto Advocates Advise Automatic Application
The idea of almost-always-auto has been around for a few years and has fairly divided the C++ community. The effective result is that concrete types appear rarely in code, and variables are declared as ‘auto’ instead.
Some of the reasons to use auto include
- It’s correct – No accidental type conversions
- It’s general – Hiding types encourages easier maintenance
- It’s quieter – Less type noise is higher readability
- It’s safe – You can’t forget to initialize it
I implemented an automated clang-based tool to port an existing codebase to use auto extensively, and I used it to automate the port of Qt.
Assessing Approved Alternative Alias
In applying these rules by default, declarations which break the rule stand out much more. The reason for non-use of auto could be type conversion, which may be accidental or deliberate. That can lead a human to assess why the rule was broken and perhaps make the code more explicit about that.
Advocates of using AAA suggest using auto by default everywhere it can be used. This results in the type being on the right hand side instead of the left side most of the time where it is written explicitly at all.
auto myString = getSomeString(); auto myDateTime = QDateTime{}; auto myWidget = static_cast<QWidget*>(getTabWidget());
Declarations which follow the AAA rules are much more uniform with each other as a result.
Part of the appeal is that it compares well with the cxx_alias_templates feature, which also puts the name on the left and the type on the right.
using GoodContainer = std::vector<int>;
One measure of maintainability is the amount of code which needs to be ported when something incidental changes. If we change a function return type, without changing how that return value is used, then client code does not need to be ported if we use auto. That is:
auto someWidget = someFunc(); // Returns Widget* someWidget->use();
does not change, but remains:
auto someWidget = someFunc(); // Now returns unique_ptr! someWidget->use();
under maintenance.
Axiom Awakens Alarmist Aversion
Of course, not everyone likes the AAA style, citing concerns such as needing to see the type in the code for readability and maintenance.
Hawk-eyed readers will recognize that ‘readability and maintenance’ is both a reason to use auto and a reason not to use auto.
I can only offer the tautology that people who think using auto does not make sense will not see any sense in using auto.
Ableton Applies Agreeable Advisories
For code that we write where I work, we take something of an almost-AAA approach, using auto everywhere that an = sign appears. So, we’re likely to write
auto s1 = getString(); QString s2;
instead of
auto s1 = getString(); auto s2 = QString{};
although I could imagine that changing some day.
Nevertheless, that is where I have had most of my exposure to extensive use of auto by default. I liked it so must that I wanted to apply it everywhere.
Obviously I didn’t want to do such porting by hand (that would be likely to introduce bugs for one thing), so I looked for automated tools.
Accumulate Adjustments; Applying Acrobatics
The clang tidy tool only converts the most trivial of declarations. For a Qt developer, the only effect it has is to convert use of new to assign to an auto variable of the same type.
So
QTabWidget* tabWidget = new QTabWidget; QString s = getString(); int i = 7; QVector<int>::const_iterator it = someVec.begin(); QStringList sl = getStringList(); QStringList middleStrings = sl.mid(1, 500);
will get converted to
auto tabWidget = new QTabWidget; QString s = getString(); int i = 7; QVector<int>::const_iterator it = someVec.begin(); QStringList sl = getStringList(); QStringList middleStrings = sl.mid(1, 500);
Qt containers, iterators and value types are not converted at all.
This is far too conservative for an advocate of auto.
So, I decided to create my own auto-modernizer. I thought about adding it to the clang-tidy tool, however that does not build standalone. Instead it requires to be built as part of llvm and clang, making it too inconvenient at this point in development.
Instead I based my tool on the excellent clazy from Sergio. That allows me to emit warnings where auto can be used, and FIX-ITs to automatically change the code. The unit tests showing before and after are most illustrative of what the tool does, to wit:
auto tabWidget = new QTabWidget; auto s = getString(); auto i = 7; auto it = someVec.begin(); auto sl = getStringList(); QStringList middleStrings = sl.mid(1, 500);
All opportunities to port to auto are taken, such that any remaining types in the code are the ones which stand out while reading.
The last line was not changed – do you know why? It is not a bug in the tool.
Adherence Abounds, Absent Accidents
I wrote the tool while at the same time using it on Grantlee to port those libraries to an AAA style. This was very helpful in finding edge cases, and for learning a lot about the clang tooling API, which is very difficult for a newcomer.
I also used the tool to port Qt to AAA, which again helped iron out some bugs in the tool, and in my understanding of C++. Qt is not likely to make such heavy use of AAA, but it is useful for the port to exist for the purpose of discussion of what AAA really looks like at that scale.
After those exercises the tool seems to cover a good deal of typical Qt code.
The tool is safe for use with QStrings and the QStringBuilder expression template – it won’t replace a QString which triggers a conversion from QStringBuilder with a use of auto. It only ports declarations for which the type of the left hand side matches the type of the right hand side without conversion.
It can even issue warnings for locations where the tool is not able to introduce use of auto, for example because multiple different types are declared at once (and/or implicitly converted).
Apologies About Artistic Alliteration
I realize I’m breaking a sensible rule, but sometimes opportunities are too tempting!
March 20, 2016 at 10:08 am |
[…] Aaargh! AAA – Right Good and Hygenic […]
March 20, 2016 at 8:11 pm |
Very nice! There is one addition that would be benefitial – adding const as well for the things that do not get mutated in the method, and are not returned from the method (not to kill the RVO)
March 20, 2016 at 8:13 pm |
Thanks!
I agree that adding const where it can be added would be a good thing, but it is completely orthogonal to using auto.
March 20, 2016 at 8:18 pm |
I know 🙂
March 20, 2016 at 11:05 pm |
1. Did I miss the link to this fine tool ? Or is it not publicly available?
2. The link for “QString which triggers a conversion from QStringBuilder” goes to a 404 error page.
March 20, 2016 at 11:19 pm |
Thanks, I fixed the 404.
The links to the unit tests lead you to the repo.
March 21, 2016 at 12:54 pm |
I’m also more in the camp of using auto everywhere. However, there are some caveats:
1) const – as Ivan pointed out.
2) really dangerous:
auto foo = f();
will get you a copy – even if f() returns T&. This is clearly a flaw in C++11 and I don’t see why the standard committee came up with this behavior of auto. C++14 fixes this with the ugly decltype(auto). See https://en.wikipedia.org/wiki/C%2B%2B14#Alternate_type_deduction_on_declaration
March 22, 2016 at 2:02 pm |
Awesome alliteration amazes!
March 23, 2016 at 11:35 am |
using auto always in a language with nontrivial rules of automatic type conversion inherited from prestandard versions of C may make your life very unpredictable:
http://stackoverflow.com/questions/30191924/why-auto-is-deduced-to-int-instead-of-uint16-t
March 23, 2016 at 1:30 pm |
if using AAA is indeed the way to go, it makes me wonder why we even need to say the word “auto” at all..
Surely c++ could be made to assume auto! auto just feels like superfluous text, its the argument against it that it doesn’t tell you the type, so why not get rid of it completely? After all hasn’t the for loop syntax been changed to handle
for( e : c){
}
why could we not simply remove auto everywhere?
myString = getSomeString();
myDateTime = QDateTime{};
myWidget = static_cast(getTabWidget());
April 19, 2016 at 7:00 am |
[…] Modernization […]
May 18, 2016 at 9:19 pm |
[…] the correct shared libraries and generally figuring out what the end-goal looks like. Having used clang APIs before, and having some experience with CMake, I decided to see what I could do to […]