Re: [boost] variant - off topic
On 4/3/19 11:27 PM, Robert Ramey via Boost wrote:
d) insert your own use case here. That is, look at your own usages of assignment to these types and see if there is not an easy way to re-factor your code to avoid assignment.
Such refactoring is difficult for state machines where you use a variant to denote the current state.
On 4/7/19 2:47 AM, Bjorn Reese via Boost wrote:
On 4/3/19 11:27 PM, Robert Ramey via Boost wrote:
d) insert your own use case here. That is, look at your own usages of assignment to these types and see if there is not an easy way to re-factor your code to avoid assignment.
Such refactoring is difficult for state machines where you use a variant to denote the current state.
Actually I see now that the real issue is mutable vs immutable data. I've found that often I had been using mutable data when it was easily possible to use immutable data. This actually surprised me and I changed my style to embrace it which has made my programming better. It's a new style. Non-the-less, it's clear that there are some instances where it's not at all natural to try to eliminate mutable data. So now my outlook as it relates to the usage of variant is a little more nuanced. a) Often/usually, I prefer using immutable data and so the issue of assignment never comes up. Any variant will do the job with pretty much no efficiency issues. b) When necessary to use assignment, problems only appear if one of types can throw on construction (?). If non of my constructors can do this - no problem same as a) above. c) Otherwise I have to dig into all these design issues. At that point I might redesign something so that a) or b) applies. Sooo... I'm thinking a good strategy for me in using a variant type would be simlar to: static_assert(is_trivially_constructible<T1, Args ...>, ""); static_assert(is_trivially_constructible<T2, Args ...>, ""); template<class T ...> using my_variant = variant<T1, T2>; my_variant<int, char> x; Of course seeing this, I'm motivated to make something more elaborate: template<T ...> struct safe_variant : public std::variant<T...> { // for each T static assert that is_trivially_constructible<T> == true }; Which gets trickier. But would be cool if someone wanted to make this. Robert Ramey
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Sun, 7 Apr 2019 at 18:44, Robert Ramey via Boost <boost@lists.boost.org> wrote:
template<T ...> struct safe_variant : public std::variant<T...> { // for each T static assert that is_trivially_constructible<T> == true };
Which gets trickier. But would be cool if someone wanted to make this.
#include <type_traits> #include <variant> struct Foo { Foo ( ) = delete; }; template<typename... Args> struct safe_variant : public std::variant<Args...> { static_assert ( std::conjunction<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); }; int main ( ) { safe_variant<int, double, bool> sv1; // compiles // safe_variant<int, double, bool, Foo> sv2; // does not compile } Unless I mis-understand what you mean, this does the trick. degski -- *Microsoft, please kill Paint3D*
On 4/7/19 10:59 PM, degski via Boost wrote:
On Sun, 7 Apr 2019 at 18:44, Robert Ramey via Boost <boost@lists.boost.org> wrote:
template<T ...> struct safe_variant : public std::variant<T...> { // for each T static assert that is_trivially_constructible<T> == true };
Which gets trickier. But would be cool if someone wanted to make this.
#include <type_traits> #include <variant>
struct Foo { Foo ( ) = delete; };
template<typename... Args> struct safe_variant : public std::variant<Args...> { static_assert ( std::conjunction<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); };
int main ( ) {
safe_variant<int, double, bool> sv1; // compiles // safe_variant<int, double, bool, Foo> sv2; // does not compile }
Unless I mis-understand what you mean, this does the trick.
degski
LOL - Looks good to me! So now we have std::variant that is guaranteed to have a valid state with no double buffer BS and no extra overhead. I'll be curious to hear what otherothers have to say about this. Robert Ramey
On Mon, 8 Apr 2019 at 06:30, Robert Ramey via Boost <boost@lists.boost.org> wrote:
LOL - Looks good to me! So now we have std::variant that is guaranteed to have a valid state with no double buffer BS and no extra overhead.
I'll be curious to hear what otherothers have to say about this.
And here's the boost version as well: #include <type_traits> #include <variant> #include <boost/mp11.hpp> #include <boost/variant.hpp> namespace mp11 = boost::mp11; struct Foo { Foo ( ) = delete; }; template<typename... Args> struct safe_std_variant : public std::variant<Args...> { static_assert ( std::conjunction<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); }; template<typename... Args> struct safe_boost_variant : public boost::variant<Args...> { static_assert ( mp11::mp_all<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); }; int main ( ) { safe_std_variant<int, double, bool> sv1; // compiles safe_boost_variant<int, double, bool> sv2; // compiles safe_std_variant<int, double, bool, Foo> sv3; // does not compile safe_boost_variant<int, double, bool, Foo> sv4; // does not compile } degski -- *Microsoft, please kill Paint3D*
On 8/04/2019 18:29, Robert Ramey wrote:
LOL - Looks good to me! So now we have std::variant that is guaranteed to have a valid state with no double buffer BS and no extra overhead.
It's restricted to only trivial types, however, which sharply limits its usability. (Mostly just to primitives and PODs, though that's an oversimplification.) Or did you mean to check is_nothrow_constructible rather than is_trivially_constructible? (Also, for robustness you should check assignment as well as construction.)
On Mon, 8 Apr 2019 at 07:30, Gavin Lambert via Boost <boost@lists.boost.org> wrote:
Or did you mean to check is_nothrow_constructible rather than is_trivially_constructible?
(Also, for robustness you should check assignment as well as construction.)
Just now I'm at it: #include <type_traits> #include <variant> #include <boost/mp11.hpp> #include <boost/variant.hpp> namespace mp11 = boost::mp11; struct Foo { Foo ( ) = delete; }; template<typename... Args> struct safe_std_variant : public std::variant<Args...> { static_assert ( std::conjunction<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); }; template<typename... Args> struct safe_boost_variant : public boost::variant<Args...> { static_assert ( mp11::mp_all<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); }; template<typename... Args> struct evensafer_std_variant : public std::variant<Args...> { static_assert ( std::conjunction<std::conjunction<std::is_nothrow_constructible<Args>...>, std::conjunction<std::is_nothrow_copy_assignable<Args>...>>::value, "is not trivially constructible or copy assignable" ); }; template<typename... Args> struct evensafer_boost_variant : public boost::variant<Args...> { static_assert ( mp11::mp_all<mp11::mp_all<std::is_nothrow_constructible<Args>...>, mp11::mp_all<std::is_nothrow_copy_assignable<Args>...>>::value, "is not trivially constructible or copy assignable" ); }; int main ( ) { safe_std_variant<int, double, bool> sv1; // compiles safe_boost_variant<int, double, bool> sv2; // compiles evensafer_std_variant<int, double, bool> sv3; // compiles evensafer_boost_variant<int, double, bool> sv4; // compiles // safe_std_variant<int, double, bool, Foo> sv5; // does not compile // safe_boost_variant<int, double, bool, Foo> sv6; // does not compile // evensafer_std_variant<int, double, bool, Foo> sv7; // does not compile // evensafer_boost_variant<int, double, bool, Foo> sv8; // does not compile } degski -- *Microsoft, please kill Paint3D*
On Mon, 8 Apr 2019 at 07:45, degski <degski@gmail.com> wrote:
Just now I'm at it:
Well, a bit too quick maybe: #include <type_traits> #include <variant> #include <boost/mp11.hpp> #include <boost/variant.hpp> namespace mp11 = boost::mp11; struct Foo { Foo ( ) = delete; }; struct Bar { Bar & operator= ( const Bar & ) = delete; }; template<typename... Args> struct safe_std_variant : public std::variant<Args...> { static_assert ( std::conjunction<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); }; template<typename... Args> struct safe_boost_variant : public boost::variant<Args...> { static_assert ( mp11::mp_all<std::is_trivially_constructible<Args>...>::value, "is not trivially constructible" ); }; template<typename... Args> struct evensafer_std_variant : public std::variant<Args...> { static_assert ( std::conjunction<std::conjunction<std::is_nothrow_constructible<Args>...>, std::conjunction<std::is_nothrow_copy_assignable<Args>...>>::value, "is not nothrow constructible or not nothrow copy assignable" ); }; template<typename... Args> struct evensafer_boost_variant : public boost::variant<Args...> { static_assert ( mp11::mp_all<mp11::mp_all<std::is_nothrow_constructible<Args>...>, mp11::mp_all<std::is_nothrow_copy_assignable<Args>...>>::value, "is not nothrow constructible or not nothrow copy assignable" ); }; int main ( ) { safe_std_variant<int, double, bool> sv1; // compiles safe_boost_variant<int, double, bool> sv2; // compiles evensafer_std_variant<int, double, bool> sv3; // compiles evensafer_boost_variant<int, double, bool> sv4; // compiles safe_std_variant<int, double, bool, Foo> sv5; // does not compile safe_boost_variant<int, double, bool, Foo> sv6; // does not compile evensafer_std_variant<int, double, bool, Foo> sv7; // does not compile evensafer_boost_variant<int, double, bool, Foo> sv8; // does not compile safe_std_variant<int, double, bool, Bar> sv9; // does compile safe_boost_variant<int, double, bool, Bar> sv10; // does compile evensafer_std_variant<int, double, bool, Bar> sv11; // does not compile evensafer_boost_variant<int, double, bool, Bar> sv12; // does not compile } degski -- *Microsoft, please kill Paint3D*
On Mon, 8 Apr 2019 at 08:02, degski <degski@gmail.com> wrote:
Well, a bit too quick maybe:
Looking at the docs of boost::mp11. In order for the Boost- and the std-versions to be equivalent (apart from the difference in the implementation of the variants) , mp11::mp_all (does not short circuit) needs to be replaced with mp11::mp_and (short circuits). Needless to say that the Boost version only requires C++11, while the std version requires C++17. degski -- *Microsoft, please kill Paint3D*
On 4/8/19 12:29 AM, Gavin Lambert via Boost wrote:
On 8/04/2019 18:29, Robert Ramey wrote:
LOL - Looks good to me! So now we have std::variant that is guaranteed to have a valid state with no double buffer BS and no extra overhead.
It's restricted to only trivial types, however, which sharply limits its usability. (Mostly just to primitives and PODs, though that's an oversimplification.)
Or did you mean to check is_nothrow_constructible rather than is_trivially_constructible?
I looked at both nothrow and trivial and couldn't figure out the difference between them with spending more time than I wanted to at the time. After, I was just throwing out an idea for discussion. Note that this approach is not really a rival to making a new variant. It's to explicit limited the usage a variant to those situations where there are none of the problems and tradeoffs that implementations of variants have to address. Basicall, I'm giving up on trying to understand all this stuff. I just need something that "just works" in an efficient simple way or "just fails(tm)" if that's not possible. It it just fails, I'll fall back to the "fancy variant" (and spend a lot of time investigating the tradeoffs and error/exception handling requirements) or alter my types so the safe_variant just works.
(Also, for robustness you should check assignment as well as construction.)
Hmmm - interesting point. My goal was to make it impossible for safe_variant to end up in the the dreaded stateless state. I was gathering from the discussions that assignment to variant entailed construction of a new instance of one of the constituent types was key place where the stateless problem can occur. So you're absolutely right in that the checks have to be implemented for all operations which might be used. Robert Ramey
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (4)
-
Bjorn Reese
-
degski
-
Gavin Lambert
-
Robert Ramey