Review request for variant2, review manager wanted
I'd like to request a formal Boost review for the Variant2 library,
https://github.com/pdimov/variant2. Variant2 contains an implementation of a
variant
If anyone would like to step forward to manage the review, please do.
Thanks for the answers to questions on Slack earlier. Firstly, in my opinion this would be a great addition to Boost. It has a very high quality implementation, good documentation, and solves an important problem. My own personal issues with this library as currently presented: 1. I need to know, from reading the documentation, what cost benefit there is to choosing this library over the major STL std::variant's, and Boost.Variant in these areas: a) Build time impact for small and medium sized variants doing a reasonable set of operations. b) Run time impact, for the same operations. c) Optional: also for large sized variants, just so we have evidence to prove to people don't do large sized variants! d) Optional: also compare to Outcome, I obviously have a vested curiosity. 2. I see that triviality of destruction is preserved, indeed half the variant header is just to implement triviality of destruction propagation. However the other individual operations do not have their triviality preserved, so for example if all the types in the variant have a trivial copy assignment but a non-trivial copy constructor, then so ought the variant. The same would go for noexcept-ness, if all the move assignments for all the types in the variant are noexcept, then so should be the variant's. And so on. I appreciate that this is a royal pain in the ass to implement for a never-empty guaranteed variant. I know this because I implemented this for Outcome, and most of the reason that Outcome demands C++ 14 is precisely because I made implementation easy on myself using partial fragment inheritance which ICEs older compilers. You could use the same technique to reduce the implementation burden, though probably with the same cost of dropping usefulness on older compilers. But I do think it's important for high quality codegen for triviality and noexcept to be propagated for all regular operations (including swap), though I do appreciate that the build time impact for implementing this is non-trivial. WG21 also felt this important, and it was remedied in the standard: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0602r4.html 3. I did find it a bit sad that Concepts aren't used when available. It could improve build time impact a bit. But it's no showstopper for me. Anyway, if you agree to implement items 1 and 2 above, I volunteer to act as review manager, if you can't find anyone more suitable. Niall
Niall Douglas wrote:
1. I need to know, from reading the documentation, what cost benefit there is to choosing this library over the major STL std::variant's,
The main cost/benefit gain over std::variant is that if you need to support C++11 or C++14, std::variant doesn't work. :-)
and Boost.Variant in these areas:
a) Build time impact for small and medium sized variants doing a reasonable set of operations.
b) Run time impact, for the same operations.
c) Optional: also for large sized variants, just so we have evidence to prove to people don't do large sized variants!
d) Optional: also compare to Outcome, I obviously have a vested curiosity.
I may look into that, but it would help if you can give me some specific scenarios which to measure; I prefer using realistic benchmarks over synthetic ones.
2. I see that triviality of destruction is preserved, indeed half the variant header is just to implement triviality of destruction propagation.
Trivial destruction is mandatory for a literal type, and hence, for anything constexpr.
However the other individual operations do not have their triviality preserved, so for example if all the types in the variant have a trivial copy assignment but a non-trivial copy constructor, then so ought the variant. The same would go for noexcept-ness, if all the move assignments for all the types in the variant are noexcept, then so should be the variant's. And so on.
noexcept is preserved. Taking the copy constructor for example, when all types are trivially copyable, the copy constructor is constexpr and noexcept: https://github.com/pdimov/variant2/blob/develop/include/boost/variant2/varia... and when not, the copy constructor is noexcept when all are nothrow copyable: https://github.com/pdimov/variant2/blob/develop/include/boost/variant2/varia... This almost-triviality has all the benefits of real triviality except, unfortunately, one - the ability for the type to be passed and returned in registers. So, yes, real triviality is something I will look into, at some point. Not necessarily before the review though.
On Sat, Feb 23, 2019 at 4:03 AM Bjorn Reese via Boost
The main appeal for me is the well-designed avoidance of the valueless- by-exception state.
2009: Boost library components often form the basis of new standard library components 2019: Standard library components often form the basis for new, improved Boost components
The main appeal for me is the well-designed avoidance of the valueless- by-exception state.
2009: Boost library components often form the basis of new standard library components
2019: Standard library components often form the basis for new, improved Boost components
If I remember rightly, former senior Boost folk tried to stop std::variant shipping with its valueless-by-exception design, demonstrating that it could be easily avoided using a fallback to a double buffer design. WG21 were so keen to ship variant in C++ 17 that those concerns, amongst others since defect remedied, were brushed off, and we are where we are at. It could be worse. I think it's entirely right and proper that Boost ought to ship fixed standard library types where possible. In many ways, Boost always has - std::deque is not useful in portable code, whereas Boost's containers have the same behaviours everywhere. I'd just love if WG21 would deprecate the current containers, and standardise new non-allocator intrusive ones instead, perhaps with an inefficient convenience wrapper. Niall
On Sat, Feb 23, 2019, 1:34 PM Niall Douglas via Boost
If I remember rightly, former senior Boost folk tried to stop std::variant shipping with its valueless-by-exception design, demonstrating that it could be easily avoided using a fallback to a double buffer design.
Peter and I were both very vocal in committee lists about this, although I was not too fond of double-buffering specifically. There were several multi-hundred post threads leading up to C++17 about it.
If I remember rightly, former senior Boost folk tried to stop std::variant shipping with its valueless-by-exception design, demonstrating that it could be easily avoided using a fallback to a double buffer design.
Peter and I were both very vocal in committee lists about this, although I was not too fond of double-buffering specifically. There were several multi-hundred post threads leading up to C++17 about it.
As I mentioned, the outcome could have been worse still. And there are much worse mistakes-in-hindsight in the standard. Though, I guess we are actually finally getting C++ 98 exported templates - albeit somewhat reworked - in C++ 20. I look forward to seeing if that damning EDG paper on exported template link performance gets borne out in real world applications by average programmers who don't fully understand Modules. Niall
On Sat, Feb 23, 2019 at 4:03 AM Bjorn Reese via Boost
On 2/23/19 4:43 AM, Peter Dimov via Boost wrote:
The main cost/benefit gain over std::variant is that if you need to support C++11 or C++14, std::variant doesn't work. :-)
The main appeal for me is the well-designed avoidance of the valueless- by-exception state.
+1
On Sat, Feb 23, 2019, 2:03 AM Bjorn Reese via Boost
On 2/23/19 4:43 AM, Peter Dimov via Boost wrote:
The main cost/benefit gain over std::variant is that if you need to support C++11 or C++14, std::variant doesn't work. :-)
The main appeal for me is the well-designed avoidance of the valueless- by-exception state.
Agreed, and I think it's important that we have this, though I've yet to formally review the implementation. IMO, std::variant is reasonable for a sum type in the standard library only in the sense that it works and is all that we could get consensus on, but it was ultimately a compromise that was lacking in different ways for different people. From my perspective there are really two core world views here for a variant design. I won't make a value judgment for which is "better" because I think that both have their uses, and I think that the belief that there can only be one is misguided. The core difference between the two approaches, imo, are: 1) Never empty 2) A variant that has a partially-formed state, as Stepanov would refer to it ("empty" akin to a default-constructed int). Arguably this is also not "empty". std::variant does something kind of straddling of these two worlds and is less than the ideal in both cases. I would love it if we had a very solid implementation of (1), and also a solid implementation of (2). If this satisfies (1), then I'd be very happy.
On 2/22/19 9:48 AM, Peter Dimov via Boost wrote:
I'd like to request a formal Boost review for the Variant2 library, https://github.com/pdimov/variant2. Variant2 contains an implementation of a variant
type that is an almost conforming std::variant, except it doesn't require C++17 and supports C++11 and above. It's also never valueless and has a few other extensions.
I see optional, expected, outcome and .. (monad?) as just special cases
of variant. for example
template<typename T>
using optional = variant
sob., 23 lut 2019 o 01:12 Robert Ramey via Boost
On 2/22/19 9:48 AM, Peter Dimov via Boost wrote:
I'd like to request a formal Boost review for the Variant2 library, https://github.com/pdimov/variant2. Variant2 contains an implementation of a variant
type that is an almost conforming std::variant, except it doesn't require C++17 and supports C++11 and above. It's also never valueless and has a few other extensions.
I see optional, expected, outcome and .. (monad?) as just special cases of variant. for example
template<typename T> using optional = variant
; Why is it necessary to have all these type implemented? Can't there be some sort of "base" type which can be used to implement all these others?
I think it is about the interfaces and guarantees. optional provides .value_or(), .map() functions, and operators, like ->. Also, because it does not have to consider the assignment of mixed types its implementation is more lightweight and therefore compiles faster and introduces fewer symbols. Regards, &rzej;
On 2/22/19 9:48 AM, Peter Dimov via Boost wrote:
I'd like to request a formal Boost review for the Variant2 library, https://github.com/pdimov/variant2. Variant2 contains an implementation of a variant
type that is an almost conforming std::variant, except it doesn't require C++17 and supports C++11 and above. It's also never valueless and has a few other extensions.
The same library also contains an expected
type, which is like the proposed std::expected , but supports more than one error type. expected<> is not yet production-ready and has no test suite, but I will finish it if the library is accepted.
I see optional, expected, outcome and .. (monad?) as just special cases
of variant. for example
template<typename T>
using optional = variant
I see optional, expected, outcome and .. (monad?) as just special cases of variant. for example
template<typename T> using optional = variant
; Why is it necessary to have all these types separately implemented? Can't there be some sort of "base" type which can be used to implement all these others? Wouldn't this approach make things much simpler to review, maintain, and use? The reason is that there are different tradeoffs at work. I cover this a bit in https://ned14.github.io/outcome/history/#outcome-v2 where I explicitly mention variant2, but to summarise:
1. Outcome has a simple implementation with minimum SFINAE, a fixed ABI and aims for low impact on build times in order to maximise usefulness in public interfaces in large codebases. 2. variant2 has a less simple implementation, enabling more flexibility, better code density, but at the cost of build time impact. 3. A fully conforming Expected can be implemented easily using either Outcome or variant2. See https://ned14.github.io/outcome/faq/#how-far-away-from-the-proposed-std-expe.... 4. Optional is an interesting one. Outcome uses an internal optional implementation because reusing Optional wasn't efficient. Specifically, I needed more than a single bit discriminant, and Optional wasn't reusable for that with doing UB. Niall
On 2/23/19 1:54 AM, Robert Ramey via Boost wrote:
On 2/22/19 9:48 AM, Peter Dimov via Boost wrote:
I'd like to request a formal Boost review for the Variant2 library, https://github.com/pdimov/variant2. Variant2 contains an implementation of a variant
type that is an almost conforming std::variant, except it doesn't require C++17 and supports C++11 and above. It's also never valueless and has a few other extensions.
The same library also contains an expected
type, which is like the proposed std::expected , but supports more than one error type. expected<> is not yet production-ready and has no test suite, but I will finish it if the library is accepted. I see optional, expected, outcome and .. (monad?) as just special cases of variant. for example
template<typename T> using optional = variant
; Why is it necessary to have all these types separately implemented? Can't there be some sort of "base" type which can be used to implement all these others? Wouldn't this approach make things much simpler to review, maintain, and use?
Interfaces and implementation of these components are very different and optimized for their respective target use cases. They are not specializations of the same use case, so having the same base class (which could aid interchangeability) is not useful. The functionally common piece of implementation is aligned_storage, and it is present as a standalone component.
participants (10)
-
Andrey Semashev
-
Andrzej Krzemienski
-
Bjorn Reese
-
degski
-
Emil Dotchevski
-
Matt Calabrese
-
Niall Douglas
-
Peter Dimov
-
Robert Ramey
-
Vinnie Falco