Re:C++20 Idioms for Parameter Packs
by CoastalCoder on Tuesday February 27, 2024 @09:41AM
(#39524678)
My 2 cents: it's a bad idea because it's hard to test / debug.
C++ template / overload resolution involves some very convoluted logic. And it can take some work to even notice if / how it went differently than a programmer expected.
Programmers typically have logging, interactive debugging, etc. to investigate complicated algorithms like this. But (almost?) no C++ compiler provides such tooling for investigating the behavior of this algorithm.
IMHO it's an area where C++ tooling could serious use improvement.
Re:C++20 Idioms for Parameter Packs
by nix0n on Tuesday February 27, 2024 @10:55AM
(#39525545)
This is why the C preprocessor is superior to templates: gcc's "-E" option will show you the preprocessed source file. (I think clang also?)
My personal theory is that compiler authors have not provided a template equivalent, because they are embarrassed about the code that's being produced.
Re:C++20 Idioms for Parameter Packs
by actionfromafar on Tuesday February 27, 2024 @09:58AM
(#39524864)
Yes! I never thought of it that way, but imagine having a real nice IDE with a "compilation debugger", showing all these things unfolding in real-time as you change a function.
Re:C++20 Idioms for Parameter Packs
by logicchains on Tuesday February 27, 2024 @02:01PM
(#39528124)
You have static_assert for testing/debugging. The lovely thing about compile-time programming is you don't even need a separate test stage; the compiler runs your tests (asserts) for you. Then if the code builds you know it's correct.
This pattern is getting easier and easier in modern C++ as more and more thing are made constexpr.
Re:C++20 Idioms for Parameter Packs
by gpderetta on Tuesday February 27, 2024 @10:31AM
(#39525231)
There is in fact a whole section on comma folds.
Aside from the fact that the articles shows many use cases where packs and fold expressions obviate the need for recursion, what's so bad about recursive template instantiations?
Re:C++20 Idioms for Parameter Packs
by mazieres on Tuesday February 27, 2024 @01:28PM
(#39527617)
Obviously there's a whole section on comma folds that you didn't read. However, more importantly, there are things that you can't do without recursion. For example, how would you implement the `index_string` function in the section on recursing over template parameters without using recursion?
Re:C++20 Idioms for Parameter Packs
by mazieres on Tuesday February 27, 2024 @08:34PM
(#39532782)
Actually not. Try to implement a compile-time initialized const char* that represents some compile-time value like sizeof(X) for some data structure X. You'll see that you need recursion.
A place where this kind of pattern is useful is in having a generic accessor type for fields, and wanting to extend it to tuples. So for example accessor(x) might return x.field, and accessor.name might be "field." To make it work for tuples, you need a string for every possible tuple index. E.g.:
Re:C++20 Idioms for Parameter Packs
by mazieres on Wednesday February 28, 2024 @11:05PM
(#39546301)
Your constexpr function is not legal because it doesn't initialize all the array elements, even though the compiler might let you get away with it. That's easily fixed. More importantly, though, your static_value::value is not a const char *.
Re:C++20 Idioms for Parameter Packs
by mgaunard on Thursday February 29, 2024 @03:14AM
(#39547377)
That restriction was lifted in 2020.
tuple_accessor::name is a const char* which is what you asked for. static_value here is an implementation detail and the type of its members is irrelevant.
Re:C++20 Idioms for Parameter Packs
by planede on Thursday February 29, 2024 @04:22AM
(#39547768)
Context. I assume you mean something like `void foo(size_t ...Idxs)`, which indeed doesn't work. It can't work, as nothing indicates that this would be a template, and it should be at least templated on the size of the pack.
<size_t ...Idxs> works because it introduces a pack that is a template parameter, so it naturally gets templated on the size of the pack (as well as on the values).
I guess an other historical issue for the grammar is that without the parameter name, `void foo(size_t ...)` is already valid grammar, and is equivalent to `void foo(size_t, ...)`, a C-style variadic.
Re:C++20 Idioms for Parameter Packs
by pavlov on Tuesday February 27, 2024 @09:44AM
(#39524703)
Sometimes I wish there was a site like "C++ for API-using dummies" which focused on explaining these fancy new features to someone like me who's probably going to encounter them in header files, but won't be actively writing code with these features.
Don't get me wrong. This article was very helpful because now, when I eventually see auto&& ...x in a function declaration, I'm hopefully going to remember that the feature is called "parameter packs", and will find this article again with a search if I can't remember what it means.
But realistically, I'm not clever enough to write code that makes use of the various implementation specifics discussed here (stuff like "init-capture packs" that let you efficiently capture a parameter pack into a lambda, and dozens of other similar cases). Having a summary article that omits all that would be more readable for me.
I've had this same feeling about most C++ features lately like coroutines and concepts. Authors take great pains to explain how I might write a library that uses coroutines, but as an app developer I'm never going to do that — I'll be using somebody else's higher-level library because that's how it works in companies with these modern C++ codebases. OTOH, that also means it's hard to write generic documentation from the user point of view because it's often so tied to specific libraries.
Re:C++20 Idioms for Parameter Packs
by jandrewrogers on Tuesday February 27, 2024 @10:45AM
(#39525425)
I think it is fair to say there are two different versions of modern C++ you can learn: one for library designers and one for everyone else. The difference in complexity between the two is not small and you can use libraries without ever learning how they work. The library version of C++ is very powerful but also not something you pick up casually.
Re:C++20 Idioms for Parameter Packs
by foofie on Tuesday February 27, 2024 @12:12PM
(#39526566)
> I think it is fair to say there are two different versions of modern C++ you can learn: one for library designers and one for everyone else.
I get what you're trying to say, but this assumption that only a subset of developers need to know how to write code intended to be consumed by third parties is the reason why stuff needlessly breaks all the time for no good reason. We can't have modularity without having code consumable by other components, and knowing how to write reusable components and reusable interfaces is a core competency in software development.
Re:C++20 Idioms for Parameter Packs
by pavlov on Tuesday February 27, 2024 @01:05PM
(#39527290)
My possibly Luddite opinion is that using the most amazing and surprising new C++ features is not the best way to build code consumed by others. It’s good for internal APIs if you have a library team and active quality control, but if you involve third party users, more conservative is often better for everyone.
For a lot of use cases, a plain C API wrapper is still very useful because it’s so easy to call from practically any other language. It can be better for maintenance to have a very narrow C API than to expose all the bells and whistles from C++ wonderland.
Re:C++20 Idioms for Parameter Packs
by Night_Thastus on Tuesday February 27, 2024 @01:30PM
(#39527654)
I agree fully, and this is what I do personally. (Though we have to use a C wrapper API for cross-platform compatibility and name mangling reasons)
But honestly, people seem to get all hung up on C++ having weird features. No-one is being forced to use a feature just because it exists. I use the sub-set of C++ that I'm familiar with and grow it when it looks like it'll save a lot of effort relative to cost of learning it.
Re:C++20 Idioms for Parameter Packs
by foofie on Wednesday February 28, 2024 @01:30AM
(#39534739)
You Rust fanboys get very tiring. No one can sneeze in a discussion on <INSERT_PROGRAMMING_LANGUAGE> without one of your types jumping in with a clueless and gratuituous "So... time to move to Rust then? "
Re:C++20 Idioms for Parameter Packs
by foofie on Wednesday February 28, 2024 @01:28AM
(#39534725)
> My possibly Luddite opinion is that using the most amazing and surprising new C++ features is not the best way to build code consumed by others.
It's ok if you opt to not jump onto the latest and greatest, specially at the interface level.
What is counterproductive is to not learn new features, and know when and when not to use them.
It makes absolutely no sense to stay stuck in C++11 when there's a heap of basic features that were since made available throughout the last decade.
It's even more critical the fact that the C++ standardization effort placed a great deal of effort on vocabulary types, which by its own definition mean types excpliticly designed to be used in interfaces.
It's perfectly ok to make a call to not use exceptions, template metaprogramming, multi threaded support, etc. What's counterproductive is staying stuck way back in 2011 because you do not know what's out there.
Re:C++20 Idioms for Parameter Packs
by jandrewrogers on Tuesday February 27, 2024 @03:03PM
(#39529030)
I think knowing how to write modular code is somewhat separate from the advanced "library writer" features in C++. Anyone can write clean and modular code without them, just as you could before they existed. You don't lose expressiveness per se but from a library user perspective it requires a great many more things to be explicit, writing a lot more boilerplate, and a larger API surface area that you have to learn. The new C++ features give the library writer the option of investing in more elegant, flexible, and concise expressions of modular code with little additional work but requiring stronger C++ skills to elevate the modularity in this way.
It is an ease-of-use improvement as much as a modularity improvement and provides some evolutionary pressure to design better interfaces to modular code.
Re:C++20 Idioms for Parameter Packs
by foofie on Wednesday February 28, 2024 @01:42AM
(#39534805)
> Anyone can write clean and modular code without them, just as you could before they existed.
I don't think so. There are multiple books dedicated to the topic of writing modular code, including books specialized in C++. Projects such as Qt are renowned for having adopted architectural traits that ensure they do not break compatibility even following a major version upgrade. I still see projects not respecting contracts and what not to put in the interface.
I think that this belief that "anyone can write clean and modular code" contrasts with reality, and suggests a certain obliviousness regarding the problem domain.
Re:C++20 Idioms for Parameter Packs
by angiosperm on Wednesday February 28, 2024 @05:08PM
(#39544107)
When you are designing an interface, and a new feature could make it better -- safer, faster, less convoluted -- but you fail to use it, you have produced a worse interface.
Re:C++20 Idioms for Parameter Packs
by gpderetta on Tuesday February 27, 2024 @10:34AM
(#39525266)
You should not be going around looking for ways to use new features; the point is not to use the features per se.
But if you know about them, maybe next time you have a problem that it is awkward to do without, you'll reach out for them. Or not if there is a simpler solution.
Re:C++20 Idioms for Parameter Packs
by 1024core on Tuesday February 27, 2024 @12:03PM
(#39526454)
I wish they would just freeze the definition of C++ Language for a couple of decades. Just because something is "neat" and "possible" does not mean that it must become a part of the language. C++ today is a syntactic hell.
Re:C++20 Idioms for Parameter Packs
by Night_Thastus on Tuesday February 27, 2024 @01:21PM
(#39527513)
Am I the only one who has never needed to do variadic anything? (minus something like flags)
I'm really not sure what type of problem variadics solve that can't be done simpler with something else, like default arguments, std::optional, containers like vector, etc.
Re:C++20 Idioms for Parameter Packs
by Night_Thastus on Tuesday February 27, 2024 @02:07PM
(#39528204)
That's a fair point. It wasn't a criticism of the feature - more confusion about what exactly it's supposed to be good at solving. I'm never clear if I should be reaching for it. make_unique() is a good example.
Re:C++20 Idioms for Parameter Packs
by logicchains on Tuesday February 27, 2024 @01:58PM
(#39528057)
You can't do type-checked multi-dimensional containers like tensors without variadics. You could only manually create Tensor1D, Tensor2D, Tensor3D... types. You also couldn't implement tuple types without them.
Re:C++20 Idioms for Parameter Packs
by Georgelemental on Tuesday February 27, 2024 @09:22PM
(#39533131)
Variadics are rarely useful, but when you need them (generally in highly generic library code) you really need them. (I don't know C++, but am familiar with the pain of not having variadics in Rust, and the piles of macros required to work around it)
Re:C++20 Idioms for Parameter Packs
by shadedtriangle on Tuesday February 27, 2024 @03:00PM
(#39528982)
I feel like this article is missing one of the most useful idioms of parameter packs which is when you want to accept a variety of parameter types to a non-template function. You can stuff a parameter pack into an array/vector of unions/variants which is quite useful! For example it's the way to implement std::format which is based off Python's string.format().
Re:C++20 Idioms for Parameter Packs
by sixthDot on Wednesday February 28, 2024 @04:06AM
(#39535681)
Fortunately D has `static foreach`, is what I thought yesterday after reading the article.
One aspect I note is that finally the point is not having variadics or not (assuming an expression can represent a type then you can use tuples instead, which is what styx-lang does). The point is that one must have a way to *use* them.
The "consume" idiom, while working, is not great as it lead to template instances explosion. Not great but not so terrible as finally `static foreach` can only work with a kind of monomorphization too.
Packs are great, but more work is still needed to make them first class features of the language.
How to use fold expressions on operator comma isn't even touched on.
https://www.scs.stanford.edu/~dm/blog/param-pack.html#comma-...
C++ template / overload resolution involves some very convoluted logic. And it can take some work to even notice if / how it went differently than a programmer expected.
Programmers typically have logging, interactive debugging, etc. to investigate complicated algorithms like this. But (almost?) no C++ compiler provides such tooling for investigating the behavior of this algorithm.
IMHO it's an area where C++ tooling could serious use improvement.
My personal theory is that compiler authors have not provided a template equivalent, because they are embarrassed about the code that's being produced.
(If you disagree, prove me wrong!)
https://cppinsights.io/lnk?code=dm9pZCBmb28oYXV0bykge30KCnZv...
I found the source at https://github.com/andreasfertig/cppinsights
Template don't "produce code". They get expanded well after parsing. There is no code to be embarrassed for.
Also you can ask GCC to dump any intermediate state: https://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html.
This pattern is getting easier and easier in modern C++ as more and more thing are made constexpr.
Aside from the fact that the articles shows many use cases where packs and fold expressions obviate the need for recursion, what's so bad about recursive template instantiations?
The functionality can be computed as a simple constexpr function.
A string as a char template parameter pack is a textbook example of an antipattern.
A place where this kind of pattern is useful is in having a generic accessor type for fields, and wanting to extend it to tuples. So for example accessor(x) might return x.field, and accessor.name might be "field." To make it work for tuples, you need a string for every possible tuple index. E.g.:
tuple_accessor::name is a const char* which is what you asked for. static_value here is an implementation detail and the type of its members is irrelevant.
https://godbolt.org/z/o53o53W5r
<size_t ...Idxs> works because it introduces a pack that is a template parameter, so it naturally gets templated on the size of the pack (as well as on the values).
I guess an other historical issue for the grammar is that without the parameter name, `void foo(size_t ...)` is already valid grammar, and is equivalent to `void foo(size_t, ...)`, a C-style variadic.
Don't get me wrong. This article was very helpful because now, when I eventually see auto&& ...x in a function declaration, I'm hopefully going to remember that the feature is called "parameter packs", and will find this article again with a search if I can't remember what it means.
But realistically, I'm not clever enough to write code that makes use of the various implementation specifics discussed here (stuff like "init-capture packs" that let you efficiently capture a parameter pack into a lambda, and dozens of other similar cases). Having a summary article that omits all that would be more readable for me.
I've had this same feeling about most C++ features lately like coroutines and concepts. Authors take great pains to explain how I might write a library that uses coroutines, but as an app developer I'm never going to do that — I'll be using somebody else's higher-level library because that's how it works in companies with these modern C++ codebases. OTOH, that also means it's hard to write generic documentation from the user point of view because it's often so tied to specific libraries.
I get what you're trying to say, but this assumption that only a subset of developers need to know how to write code intended to be consumed by third parties is the reason why stuff needlessly breaks all the time for no good reason. We can't have modularity without having code consumable by other components, and knowing how to write reusable components and reusable interfaces is a core competency in software development.
For a lot of use cases, a plain C API wrapper is still very useful because it’s so easy to call from practically any other language. It can be better for maintenance to have a very narrow C API than to expose all the bells and whistles from C++ wonderland.
But honestly, people seem to get all hung up on C++ having weird features. No-one is being forced to use a feature just because it exists. I use the sub-set of C++ that I'm familiar with and grow it when it looks like it'll save a lot of effort relative to cost of learning it.
So... time to move to Rust then?
It's ok if you opt to not jump onto the latest and greatest, specially at the interface level.
What is counterproductive is to not learn new features, and know when and when not to use them.
It makes absolutely no sense to stay stuck in C++11 when there's a heap of basic features that were since made available throughout the last decade.
It's even more critical the fact that the C++ standardization effort placed a great deal of effort on vocabulary types, which by its own definition mean types excpliticly designed to be used in interfaces.
It's perfectly ok to make a call to not use exceptions, template metaprogramming, multi threaded support, etc. What's counterproductive is staying stuck way back in 2011 because you do not know what's out there.
It is an ease-of-use improvement as much as a modularity improvement and provides some evolutionary pressure to design better interfaces to modular code.
I don't think so. There are multiple books dedicated to the topic of writing modular code, including books specialized in C++. Projects such as Qt are renowned for having adopted architectural traits that ensure they do not break compatibility even following a major version upgrade. I still see projects not respecting contracts and what not to put in the interface.
I think that this belief that "anyone can write clean and modular code" contrasts with reality, and suggests a certain obliviousness regarding the problem domain.
But if you know about them, maybe next time you have a problem that it is awkward to do without, you'll reach out for them. Or not if there is a simpler solution.
They did that already. C++03 -> C++11.
Other languages marched forward and left it in the dust with things like lambdas, reflection, etc.
* Are good at documenting
* Communicate what and why they chose it during code reviews
* Encapsulate their work behind sensible functions/APIs that don't require intimate knowledge of complex internals
And if they aren't, that's worth bringing up to them. If it's still a problem it's worth bringing up to your team/project lead or otherwise.
And, I mean folds? How cool is that!
I'm really not sure what type of problem variadics solve that can't be done simpler with something else, like default arguments, std::optional, containers like vector, etc.
If you do not need variadics that's fine, you do not have to force them where they don't belong.
One aspect I note is that finally the point is not having variadics or not (assuming an expression can represent a type then you can use tuples instead, which is what styx-lang does). The point is that one must have a way to *use* them.
The "consume" idiom, while working, is not great as it lead to template instances explosion. Not great but not so terrible as finally `static foreach` can only work with a kind of monomorphization too.