7 ms·
C++20 Idioms for Parameter Packs
- angiosperm 3y agoI really welcome the clarity of this presentation.
- gpderetta 3y agoThat was a pretty good, if dense, presentation. I use packs almost daily but I did learn a few things. Packs are great, but more work is still needed to make them first class features of the language.
- mgaunard 3y agoPretty bad "idioms"; anything involving recursive template instantiations is a terrible idea. How to use fold expressions on operator comma isn't even touched on.
- CoastalCoder 3y agoMy 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.
- actionfromafar 3y agoYes! 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.
- nix0n 3y agoThis 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. (If you disagree, prove me wrong!)
- gpderetta 3y ago> prove me wrong 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 https://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html.
- nix0n 3y agoIs there a specific "-fdump" that you recommend?
- planede 3y agocppinsights.io shows expanded function templates as explicit specializations. It's built on LLVM, but I don't think that this tool is upstreamed. https://cppinsights.io/lnk?code=dm9pZCBmb28oYXV0bykge30KCnZvaWQgYmFyKCkgeyBmb28oMCk7IH0=&insightsOptions=cpp23&std=cpp23&rev=1.0 https://cppinsights.io/lnk?code=dm9pZCBmb28oYXV0bykge30KCnZv...
- nix0n 3y agoThank you! This is exactly the sort of thing I was looking for. I found the source at https://github.com/andreasfertig/cppinsights https://github.com/andreasfertig/cppinsights
- logicchains 3y agoYou 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.
- gpderetta 3y agoThere 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?
- corysama 3y agoIf you can use a comma fold instead of recursion, please do. They are a small hurdle to learn and, after that, much easier to follow.
- corysama 3y agoIs the section on “Comma Fold” (right after the recursion section) not what you are looking for? https://www.scs.stanford.edu/~dm/blog/param-pack.html#comma-fold https://www.scs.stanford.edu/~dm/blog/param-pack.html#comma-...
- mgaunard 3y agoYou mean way after he introduced the bad way of doing things? Looks like an afterthought.
- mazieres 3y agoObviously 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?
- mgaunard 3y agoThere is zero need for template recursion here, or even templates at all. 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.
- mazieres 3y agoActually 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.: template<typename T, size_t N> struct tuple_accessor; template<typename...T, size_t N> struct tuple_accessor<std::tuple<T...>, N> { constexpr decltype(auto) operator(auto &&t) const { return get<N>(std::forward<decltype(t)>(t)); } static constexpr const char *name = index_string<N>(); };
- mgaunard 3y agofor demonstration purposes only. #include <array> #include <tuple> constexpr std::array<char, 11> itos(unsigned n) { std::array<char, 11> s; unsigned i = 0; while (n) { s[i++] = '0' + (n % 10); n /= 10; } s[i] = '\0'; return s; } template<auto V> struct static_value { static constexpr auto value = V; }; template<class T, unsigned N> struct tuple_accessor; template<class...T, unsigned N> struct tuple_accessor<std::tuple<T...>, N> { static constexpr const char *name = static_value<itos(N)>::value.data(); };
- pavlov 3y agoSometimes 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.
- gpderetta 3y agoYou 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.
- jandrewrogers 3y agoI 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.
- foofie 3y ago> 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.
- 1024core 3y agoI 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.
- darknavi 3y ago> I wish they would just freeze the definition of C++ Language for a couple of decades. They did that already. C++03 -> C++11. Other languages marched forward and left it in the dust with things like lambdas, reflection, etc.
- Night_Thastus 3y agoIf you don't want to use it, don't use it. Target whatever C++ version you want and stick with it.
- 1024core 3y agoUnfortunately I work with other people who insist on using the latest and greatest addition that comes along. :-(
- Night_Thastus 3y agoWell, hopefully they: * 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.
- schindlabua 3y agoMy C++ is stuck in 2011 and it's crazy to see how it evolved. It's like an alien language. And, I mean folds? How cool is that!
- Night_Thastus 3y agoAm 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.
- spacechild1 3y agoBut you have certainly been using variadic functions or classes, e.g. everytime you call std::make_unique(). Someone needs to write those :)
- Night_Thastus 3y agoThat'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.
- logicchains 3y agoYou 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.
- gpderetta 3y agoJust as a simple example, Optional and containers can use variadics to forward arguments to emplace-construct their contained objects. If you do not need variadics that's fine, you do not have to force them where they don't belong.
- Georgelemental 3y agoVariadics 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)
- shadedtriangle 3y agoI 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(). using FormatArg = variant<int, float, string>; string FormatArgs(const char* fmt, const vector<FormatArg> &args); template <typename... Args> string Format(const char* fmt, Args&&... args) { vector<FormatArg> expanded_args = {args...}; return FormatArgs(fmt, expanded_args); } int main() { cout << Format("Hello {} pi {}", "world", 3.14f); } https://godbolt.org/z/v5zo99aGe https://godbolt.org/z/v5zo99aGe
- mazieres 3y agoWhat is your non-template function in this example?
- sixthDot 3y agoFortunately 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.