Re:C++11 Tuple Implementation Details (2012)
by CJefferson on Saturday February 06, 2021 @04:45AM
(#26045446)
Implementing C++03 Tuple was awful / hilarious.
I wrote the tuple implementation which was included in g++ for a while (technically libstdc++) -- it used some reasonably awful preprocessor to generate the different arities of tuple.
Once you get to C++11, you at least didn't need to do it with pre-processor magic.
Re:C++11 Tuple Implementation Details (2012)
by gpderetta on Saturday February 06, 2021 @08:32AM
(#26046370)
At some point in the future hopefully we will have tuples, variants and pattern matching as core language, in the meantime it is nice that the language is powerful enough to allow implementing them as a library. That adds complexity of course and it is going to be rough on the edges.
At some point there were multiple lambda libraries before we got language support for it.
Re:C++11 Tuple Implementation Details (2012)
by saurik on Friday February 05, 2021 @08:20PM
(#26043232)
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.
Re:C++11 Tuple Implementation Details (2012)
by comex on Saturday February 06, 2021 @12:02AM
(#26044259)
Not 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?
Re:C++11 Tuple Implementation Details (2012)
by Kranar on Saturday February 06, 2021 @12:41AM
(#26044413)
You 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.
Re:C++11 Tuple Implementation Details (2012)
by comex on Saturday February 06, 2021 @01:58AM
(#26044712)
So 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.
Re:C++11 Tuple Implementation Details (2012)
by Kranar on Saturday February 06, 2021 @02:09AM
(#26044755)
Yep, that's the idea. It's done in several parts of the standard library to in-place construct objects, usually via std::piecewise_construct.
>It wouldn't work if, e.g., later elements of the tuple are constructed with pointers into earlier elements...
That's right but there's no way to construct a tuple in this manner. You can construct a tuple with some default values and then after construction have elements of the tuple point to others, but you couldn't possibly construct a tuple where elements of the tuple are pointing to one another.
Re:C++11 Tuple Implementation Details (2012)
by Lvl999Noob on Saturday February 06, 2021 @12:26AM
(#26044354)
In 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.
Re:C++11 Tuple Implementation Details (2012)
by saurik on Monday February 08, 2021 @04:21PM
(#26070322)
Correct. 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.
Re:C++11 Tuple Implementation Details (2012)
by pjmlp on Saturday February 06, 2021 @05:06AM
(#26045531)
Agree, 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?
Re:C++11 Tuple Implementation Details (2012)
by hvdijk on Saturday February 06, 2021 @09:23AM
(#26046661)
Chromium 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++.
Re:C++11 Tuple Implementation Details (2012)
by DoofusOfDeath on Saturday February 06, 2021 @09:48AM
(#26046830)
Thanks 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)?
Re:C++11 Tuple Implementation Details (2012)
by hvdijk on Saturday February 06, 2021 @10:10AM
(#26047007)
I 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>, 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.
Re:C++11 Tuple Implementation Details (2012)
by gpderetta on Saturday February 06, 2021 @02:01PM
(#26048916)
I was also reaching for my pitchfork, but in this case there is probably more code in the wild that relies on the (very useful) GCC extension as opposed to the standard behaviour.
Re:C++11 Tuple Implementation Details (2012)
by hvdijk on Saturday February 06, 2021 @02:57PM
(#26049375)
I'm not too sure about the "more code in the wild" that would be broken though. GCC already disables this extension, in the cases where it conflicts with the standard, in -std=c++11 mode. I expect almost all code to either use -std=c++11 consistently, in which case it would already be broken with GCC, or to use -std=gnu++11 consistently, in which case it would continue to work with clang even after that patch. Chromium is weird in that it builds with -std=c++11 if building with clang, and it builds with -std=gnu++11 if building with GCC. (Change 11 to 14 or whatever else as needed.)
Re:C++11 Tuple Implementation Details (2012)
by hvdijk on Saturday February 06, 2021 @05:37PM
(#26050712)
Yup, I made sure my change would make clang agree with GCC as far as this particular extension is concerned, so it was fully enabled in -std=gnu++11 mode and only enabled where it didn't conflict with standard C++ in -std=c++11 mode. I felt the same way that Chromium should just use the same flags for clang as for GCC if they want to keep using this extension, but they don't.
Re:C++11 Tuple Implementation Details (2012)
by gpderetta on Monday February 08, 2021 @05:55AM
(#26062981)
it seems that their reasoning is that they, understandably, do not want more gnu-ism in gnu++11 to creep up in their code base. But it seems that they are happy for this specific gnu-ism to creep up in other teams code bases...
Re:C++11 Tuple Implementation Details (2012)
by arc-in-space on Saturday February 06, 2021 @03:14PM
(#26049525)
I hope dang forgives me my tone, and my compulsion to respond to everything.
I'm getting the sense you have no idea what's being talked about here, and what tuple is actually for. Instead of saying anything at all you give me two links, one of which is a 30 minute read(are you serious?) that seems to have little to do with the topic, and the other is literally just an explanation of what std::any is(really, are you serious?).
I'll try to be at least somewhat charitable, and explain where I'm coming from. Tuple is a statically-typed container, that is useful in a generic context - which, crucially, doesn't mean polymorphic at runtime! - because its members' types can be inspected at compile time, unlike a struct. This makes it useful for implementing all sorts of template magic. You can also use it like a anonymous struct, for a quick-and-dirty way to return multiple values from a function, though a struct is often better.
std::any, on the other hand, is, uh, not like that at all, whether it is in a vector or not. It holds a single value that is polymorphic at runtime, by erasing its type, which has nothing to do with tuples, and helps with none of the above.
I wrote the tuple implementation which was included in g++ for a while (technically libstdc++) -- it used some reasonably awful preprocessor to generate the different arities of tuple.
Once you get to C++11, you at least didn't need to do it with pre-processor magic.
At some point there were multiple lambda libraries before we got language support for it.
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_t...
[2] https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat
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?
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.
>It wouldn't work if, e.g., later elements of the tuple are constructed with pointers into earlier elements...
That's right but there's no way to construct a tuple in this manner. You can construct a tuple with some default values and then after construction have elements of the tuple point to others, but you couldn't possibly construct a tuple where elements of the tuple are pointing to one another.
> Chromium targets C++14; C++17 support is not expected before mid-2021.
[1]: <https://chromium.googlesource.com/chromium/src/+/master/styl...>
Mind sharing a link to your submission's discussion thread(s)?
Odd at the very least.
To wit, I've literally never used vector<any>, but tuple quite a lot(though often just because it's convenient for some metaprogramming hacks).
I'm getting the sense you have no idea what's being talked about here, and what tuple is actually for. Instead of saying anything at all you give me two links, one of which is a 30 minute read(are you serious?) that seems to have little to do with the topic, and the other is literally just an explanation of what std::any is(really, are you serious?).
I'll try to be at least somewhat charitable, and explain where I'm coming from. Tuple is a statically-typed container, that is useful in a generic context - which, crucially, doesn't mean polymorphic at runtime! - because its members' types can be inspected at compile time, unlike a struct. This makes it useful for implementing all sorts of template magic. You can also use it like a anonymous struct, for a quick-and-dirty way to return multiple values from a function, though a struct is often better.
std::any, on the other hand, is, uh, not like that at all, whether it is in a vector or not. It holds a single value that is polymorphic at runtime, by erasing its type, which has nothing to do with tuples, and helps with none of the above.