Presented “named boolean” technique disable short-circuiting which lead to unnecessary code execution especially if that code is expensive.
And other thing is that, in general .count() is not the best solution for checking if container contains something, because .count() is not always O(lg n). It works for std::map, but if container will change, for example, to std::vector after some refactor, then calling .count() will hurt.
And probably better way to preserve short-circuiting and get “named boolean” readability is to extract boolean computation to lambda (or function) and call these functions in if statement.
This is the kind of reason why I don’t touch C++ if I can avoid it. I mean this is a cool hack – it’s a let binding with extra stack abuse, but it’s a cool trick. (It’s one of the things that I recommend interns & junior devs to do early on: write it like this first, otherwise you’ll forget a comparison or miss a parenthesis and you’ll chase it for days. Make it work, then squash it to get shortcircuiting).
But also it’s bloody ridiculous that a language is fourty years old and yet:
Someone is still coming up with ways to make sure that it won’t compile and execute junk…
…without someone (correctly!) pointing out that this kills like the second most common compiler optimization…
…and also that .count() is probably not a good way to get the count of a container. What in the actual fuck, Bjarne Stroustrup, why do you hate us so? What would be the correct way to get the count of a container in this day and age? Actually you know what, no, I don’t even want to know, I bet that involves at least two layers of templated code and I just don’t want to know, okay?
How do you guys ever manage to write C++ productively? I mean, in-between figuring out the right way to test if a couple of variables with long names are true and a finding a refactoring-proof method of retrieving a collection’s length, when do you, like, write the actual program?
It feels like I can hardly write auto foo = Bar::Baz() without someone pointing out that actually you want to say auto&& foo<Bar::<template::Baz>> = &Bar<auto>::Baz<typeof(&foo)>(), – you want Baz to be generic over auto-scoped templates because otherwise the default move operator subtly remains defined so if you build another Bar::Baz in the same scope it leaks memory, oh and it’s also O(n^2), or something. Since when, oh, it’s as per the latest C++ standard which was announced on Feb 17 2023 and clang is going to fully support it every minute now, but in the meantime if you’re not on clang-git running on arch-rolling here’s thirty lines of workaround for ancient C++ like C++-20 which is probably what Hammurabi used or something, what, you mean you just wrote auto foo = Bar::Baz() before like the reference manual told you to, oh, you sweet, innocent child, did you not know that the C++ language reference is just meant to keep the Printer’s Guild in business, that thing is useless before everyone on the committee got a chance to sign its name on it.
I mean yes sure it pays my bills but I feel there’s a slight discrepancy between this and how they taught me to do engineering…
and also that .count() is probably not a good way to get the count of a container
There is no such thing as a “count of a container”. There is the number of elements in a container, which you can get for every standard container with the size() member function. The count() function in question returns the number of elements with the specified key. It is only provided by associative containers.
How do you guys ever manage to write C++ productively?
By being competent, doing home work (like knowing the difference between std::map::size() and std::map::count()), and paying attention to detail.
What in the actual fuck, Bjarne Stroustrup, why do you hate us so?
I would suggest that you go to one of the C++ standards committee meetings and actually see for yourself how the sausage is made. I guarantee you will quicky realize that people involved (including Bjarne) are not at clueless as you think.
I guess it wasn’t very obvious that my post should have been taken in jest :-). I was hinting, for example, at this other comment, which cautions against two very valid problems that I’ve been very aware of for God knows how long I’ve been writing C++ for, and which I’d have really hoped we could’ve satisfactorily solved by now. I’m sorry about the confusion about the count method; I just called it “getting the count” the way I’d say “getting the flags of foo” to mean foo.flags(...) or whatever.
It’s certainly true that the folks on the committee are super smart, I’m pretty sure I could never take any of their places. It’s also certainly true that I’ve heard “don’t use this standard function, it’s bad” so many times, and for seemingly every standard function, that I am, as my post above said, quite exhausted.
Edit: sadly, after all the COVID medicine I poured into me these days, I’m afraid to drink for a few weeks :-(. I think Vitamin C brings out the worst in me…
I agree with the concept, but one should be wary of mixing this with the practice of defining variables at the top of your code, because this will move logic further away from it’s place of use. It also may introduce bugs when the variables aren’t set as expected at the top of the code block, and may result in a performance hit for expensive checks that may not be executed with the previous code paths.
one should be wary of mixing this with the practice of defining variables at the top of your code
One should be wary of this practice itself. Defining variables at the top of our code is a relic from old school C (before C99), and is harmful for the very reason you state: it moves logic further from the place of use, and that’s just bad. (We could also not initialise those variables, but that’s bad too, even though thankfully compilers have appropriate warnings.)
If am in an environment where this harmful style is used, I break rank. I rarely do, but this one’s too much. And if someone demands that I conform, I add pairs of brackets whenever appropriate:
void my_fun()
{
/* Some declarations */
/* Some code */
{
/* Some more declarations */
/* Some more code */
}
}
And if they object to that, I school them on the fine points of C89, and point out that really, their obsolete and misguided style doesn’t ask us to declare everything at the beginning of a function, but at the beginning of a scope. The automated tools in legacy mode even agree with me.
I usually like this because it makes the code more readable and understandable, but you often have someone come along and “optimize” it, or reject it because it could be ‘optimized’ further.
I’m on the fence with this as the focus on bools is wrong here. The issue here is that there are a lot of conditions making the line harder to read, making it possible to miss the incorrect placement.
Of course “managing the complexity of nested expressions prevents bugs in all languages” doesn’t produce the same snappy title.
A more reasonable rule on booleans is “avoid unnamed booleans”, which is both manageable as a rule, can be enforced, and is applicable to many languages. This is basically “don’t use a bool, use a case specific enum, for anything that doesn’t have a completely unambiguous name” - eg function paramarters that don’t name the parameters at call sites, anonymous fields (tuples, etc). That was one thing I was taught very early on when I started working in production software, that I happily continue many years later.
I fix the same bug by keeping my lines short. Here I would just hit [enter] and [tab] (or whatever gets my code automatically indented and aligned):
if ((_someLongNamedVar != FooLongNameEnum::Unknown &&
_someLongNamedMap.count(_someLongNamedVar)) == 0)
Whoops, the alignment is off here. I expected this one instead:
if ((_someLongNamedVar != FooLongNameEnum::Unknown &&
_someLongNamedMap.count(_someLongNamedVar)) == 0)
Ah, I see, wrong parentheses. The intent was most probably this:
if ((_someLongNamedVar != FooLongNameEnum::Unknown) &&
(_someLongNamedMap.count(_someLongNamedVar) == 0))
But that’s only if I put parentheses. And in this case I just wouldn’t:
if (_someLongNamedVar != FooLongNameEnum::Unknown &&
_someLongNamedMap.count(_someLongNamedVar) == 0)
Comparisons have higher priority than boolean operators, and I make that even clearer with the newline. This also makes me immune to this particular bug. Variations thereof can still bite me, just not this one.
When there are more booleans in my if, I often also combine those into another named boolean, trying to communicate the intent even more […]
Ah, this is a good application of named booleans. Or named anything for that matter.
Presented “named boolean” technique disable short-circuiting which lead to unnecessary code execution especially if that code is expensive.
And other thing is that, in general
.count()
is not the best solution for checking if container contains something, because.count()
is not alwaysO(lg n)
. It works forstd::map
, but if container will change, for example, tostd::vector
after some refactor, then calling.count()
will hurt.And probably better way to preserve short-circuiting and get “named boolean” readability is to extract boolean computation to lambda (or function) and call these functions in
if
statement.std::vector
does not providecount()
, only associative containers do.I woke up and chose violence today :(.
This is the kind of reason why I don’t touch C++ if I can avoid it. I mean this is a cool hack – it’s a
let
binding with extra stack abuse, but it’s a cool trick. (It’s one of the things that I recommend interns & junior devs to do early on: write it like this first, otherwise you’ll forget a comparison or miss a parenthesis and you’ll chase it for days. Make it work, then squash it to get shortcircuiting).But also it’s bloody ridiculous that a language is fourty years old and yet:
.count()
is probably not a good way to get the count of a container. What in the actual fuck, Bjarne Stroustrup, why do you hate us so? What would be the correct way to get the count of a container in this day and age? Actually you know what, no, I don’t even want to know, I bet that involves at least two layers of templated code and I just don’t want to know, okay?How do you guys ever manage to write C++ productively? I mean, in-between figuring out the right way to test if a couple of variables with long names are true and a finding a refactoring-proof method of retrieving a collection’s length, when do you, like, write the actual program?
It feels like I can hardly write
auto foo = Bar::Baz()
without someone pointing out that actually you want to sayauto&& foo<Bar::<template::Baz>> = &Bar<auto>::Baz<typeof(&foo)>()
, – you wantBaz
to be generic overauto
-scoped templates because otherwise the default move operator subtly remains defined so if you build anotherBar::Baz
in the same scope it leaks memory, oh and it’s alsoO(n^2)
, or something. Since when, oh, it’s as per the latest C++ standard which was announced on Feb 17 2023 and clang is going to fully support it every minute now, but in the meantime if you’re not on clang-git running on arch-rolling here’s thirty lines of workaround for ancient C++ like C++-20 which is probably what Hammurabi used or something, what, you mean you just wroteauto foo = Bar::Baz()
before like the reference manual told you to, oh, you sweet, innocent child, did you not know that the C++ language reference is just meant to keep the Printer’s Guild in business, that thing is useless before everyone on the committee got a chance to sign its name on it.I mean yes sure it pays my bills but I feel there’s a slight discrepancy between this and how they taught me to do engineering…
Perhaps still a bit drunk also?
There is no such thing as a “count of a container”. There is the number of elements in a container, which you can get for every standard container with the
size()
member function. Thecount()
function in question returns the number of elements with the specified key. It is only provided by associative containers.By being competent, doing home work (like knowing the difference between
std::map::size()
andstd::map::count()
), and paying attention to detail.I would suggest that you go to one of the C++ standards committee meetings and actually see for yourself how the sausage is made. I guarantee you will quicky realize that people involved (including Bjarne) are not at clueless as you think.
I guess it wasn’t very obvious that my post should have been taken in jest :-). I was hinting, for example, at this other comment, which cautions against two very valid problems that I’ve been very aware of for God knows how long I’ve been writing C++ for, and which I’d have really hoped we could’ve satisfactorily solved by now. I’m sorry about the confusion about the
count
method; I just called it “getting the count” the way I’d say “getting the flags of foo” to meanfoo.flags(...)
or whatever.It’s certainly true that the folks on the committee are super smart, I’m pretty sure I could never take any of their places. It’s also certainly true that I’ve heard “don’t use this standard function, it’s bad” so many times, and for seemingly every standard function, that I am, as my post above said, quite exhausted.
Edit: sadly, after all the COVID medicine I poured into me these days, I’m afraid to drink for a few weeks :-(. I think Vitamin C brings out the worst in me…
I agree with the concept, but one should be wary of mixing this with the practice of defining variables at the top of your code, because this will move logic further away from it’s place of use. It also may introduce bugs when the variables aren’t set as expected at the top of the code block, and may result in a performance hit for expensive checks that may not be executed with the previous code paths.
One should be wary of this practice itself. Defining variables at the top of our code is a relic from old school C (before C99), and is harmful for the very reason you state: it moves logic further from the place of use, and that’s just bad. (We could also not initialise those variables, but that’s bad too, even though thankfully compilers have appropriate warnings.)
If am in an environment where this harmful style is used, I break rank. I rarely do, but this one’s too much. And if someone demands that I conform, I add pairs of brackets whenever appropriate:
And if they object to that, I school them on the fine points of C89, and point out that really, their obsolete and misguided style doesn’t ask us to declare everything at the beginning of a function, but at the beginning of a scope. The automated tools in legacy mode even agree with me.
I usually like this because it makes the code more readable and understandable, but you often have someone come along and “optimize” it, or reject it because it could be ‘optimized’ further.
I’m on the fence with this as the focus on bools is wrong here. The issue here is that there are a lot of conditions making the line harder to read, making it possible to miss the incorrect placement.
Of course “managing the complexity of nested expressions prevents bugs in all languages” doesn’t produce the same snappy title.
A more reasonable rule on booleans is “avoid unnamed booleans”, which is both manageable as a rule, can be enforced, and is applicable to many languages. This is basically “don’t use a bool, use a case specific enum, for anything that doesn’t have a completely unambiguous name” - eg function paramarters that don’t name the parameters at call sites, anonymous fields (tuples, etc). That was one thing I was taught very early on when I started working in production software, that I happily continue many years later.
I fix the same bug by keeping my lines short. Here I would just hit [enter] and [tab] (or whatever gets my code automatically indented and aligned):
Whoops, the alignment is off here. I expected this one instead:
Ah, I see, wrong parentheses. The intent was most probably this:
But that’s only if I put parentheses. And in this case I just wouldn’t:
Comparisons have higher priority than boolean operators, and I make that even clearer with the newline. This also makes me immune to this particular bug. Variations thereof can still bite me, just not this one.
Ah, this is a good application of named booleans. Or named anything for that matter.