5 ms·
I really hate the C++11 tuple implementation as I have never figured out a way to construct it from a recursive template algorithm. I end up giving up and letti
by saurik 6y ago
I really hate the C++11 tuple implementation as I have never figured out a way to construct it from a recursive template algorithm. I end up giving up and letting the whole thing first default construct, at which point I assign into its fields; but I feel like a "high quality" data structure that understood the rest of the language would have given me at least an iterative way to construct the fields in order, wherein if I fail to provide an r-value to copy construct an element the whole thing gets rolled back (picture an internal iterator pattern giving me linear construction as if I were in some monad). Like, I appreciate in some cases I can do this using parameter pack expansion, but since execution of function arguments doesn't have defined order it doesn't really make sense for most of my use cases.
- Kranar 6y ago>I really hate the C++11 tuple implementation as I have never figured out a way to construct it from a recursive template algorithm. Use a combination of std::forward_as_tuple [1] and std::tuple_cat [2] to successively construct each element one-by-one and in place. If you have a specific use case I could show you how to use them. [1] https://en.cppreference.com/w/cpp/utility/tuple/forward_as_tuple https://en.cppreference.com/w/cpp/utility/tuple/forward_as_t... [2] https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat
- comex 6y agoNot saurik, but that would involve a whole lot of redundant move constructions (to go from each tuple to the next one) which may or may not be optimized away. But I think the same problem would apply to any class: you can't construct an object without constructing its fields. The workaround would be to wrap each field in a union so that it can be trivially default constructed, then use placement new. But then where would you track how many fields have been initialized so far? In a separate iterator object? What would the tuple's destructor do?
- Lvl999Noob 6y agoIn that case, isn't this the use case for the Builder Pattern? Use a separate type that knows how to work with partially initialized tuples and correctly destructs them.
- saurik 6y agoCorrect. The implementation I had in mind when I said "internal iterator"--and which I think I've coded before back almost two decades ago--is that the tuple one-by-one in-order does a placement new on each of its elements, calling a templated callback you provide which returns the element (so you get RVO through to the placement new); in the case of an exception, it knows which it has already done by code (not by state) and so can deconstruct the appropriate objects.
- Kranar 6y agoYou use the std::forward_as_tuple to eliminate all moves. The std::forward_as_tuple is used to collect arguments to each constructor so that at the end you can build the final tuple in place.
- comex 6y agoSo you're saying that you'd build a tuple (or some other structure) of tuples of references to construction arguments. You'd still be constructing a series of successively larger objects from smaller ones, but those objects are made up of a bunch of references, and references are trivially copyable from the implementation's perspective (though not from the standard's). So those constructions would likely be optimized away via memcpy elimination, or, even if they weren't, they'd be relatively cheap. Am I understanding correctly? I suppose that works, as long as you can determine all the construction arguments in advance. It wouldn't work if, e.g., later elements of the tuple are constructed with pointers into earlier elements, so that you'd need the earlier elements to already be constructed before computing the construction arguments for the later elements.
- Kranar 6y ago
- pjmlp 6y agoAgree, but with C++20 around the corner, most platforms now using C++17 as the default for their toolchains, is the C++11 implementation something to still worry about other than niche use cases?
- yakubin 6y agoBig projects move slowly to newer standards. E.g.[1]: > Chromium targets C++14; C++17 support is not expected before mid-2021. [1]: <https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md https://chromium.googlesource.com/chromium/src/+/master/styl...>
- pjmlp 6y agoI wouldn't consider Chromium a good example though, given how Google considers most of modern C++ not welcomed.
- hvdijk 6y agoChromium is the reason a patch I had recently submitted to LLVM to fix a conformance issue in their preprocessor was reverted: it broke Chromium, because Chromium doesn't just require a preprocessor that offers guarantees in areas the standard doesn't, it specifically requires a preprocessor that does not conform to the C++ standard. No idea on whether they're putting in the work on fixing their code to at least make it valid for some version of C++.
- DoofusOfDeath 6y agoThanks for reporting this. It's the first evidence I've heard of someone (Google?) abusing their power with LLVM in order to protect Google's products. Mind sharing a link to your submission's discussion thread(s)?
- hvdijk 6y agoI don't think it's productive to speak of the LLVM maintainer's decision as an abuse of power. The actual revert happened in contradiction of documented LLVM policies (which, according to the maintainer, do not match actual LLVM policies), so I did take issue with that, but the maintainer wanted to first make sure clang had a different conforming extension that Chromium could use instead before removing the non-conforming extension, and assuming that a non-Google project would be given the same treatment -- and I have no reason to assume otherwise -- I think that is defensible. The change was <https://reviews.llvm.org/D91913 https://reviews.llvm.org/D91913>, addressing a conflict between GCC's ,##__VA_ARGS__ extension with the C and C++ standards in the corner case where a macro, let's call it FOO, takes no named parameters and is invoked as FOO(). In standards-conforming C modes, clang already acts as required by the standard, but in meant-to-be-conforming C++ modes, it does not.