You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Our implementation of variant allows access via get<type>() even if the type appears several times in the variant. This can be potentially confusing and error prone, especially when type aliasing is involved. Say, you got a variant variant<int32_t, int>. Do you know what get<int>() will do? Does the architecture matter?
What makes things even more confusing is the addition of special versions of get that unwrap std::reference_wrapper and mapbox::util::recursive_wrapper on the fly. And they get accessed using the underlying type. So having a variant<recursive_wrapper<int>> you can access that int using the somewhat magical get<int>(). What happens when you access a variant<int, recursive_wrapper<int>> using get<int>()?
I do see the convenience these choices bring, but on the other hand, these could lead to really hard to find bugs. The upcoming standards "solves" the first issue by not allowing get-access through the types if the types are not unique. You have to use index-based access then, which we don't have. It doesn't have the second problem because it doesn't have those magic wrappers.
There's another issue: visitors. Once two alternatives become the same type, a visitor overloading operator() on both won't compile. That means if you have alternatives that may be the same type on some architecture, you either need to wrap them in a tag struct, or overload visitation on type index. Neither seems ideal to me, but the former can be done even if variant forbids identical types.
"..is to maintain external API compatibility with boost::variant such that Mapbox variant can be a "drop in".
Adding more compile checks will increase compile times for not much of a value in my view. Happy to revisit this later when std variant becomes a reality.
Both our variant and boost::variant have well defined behaviour:
As long as libfoo and libbar use something unique, like a pointer to an opaque struct for their handles, everything works fine. The day either of them switches to using an int or std::string, or when they start using the same type for handle, like void*, all hell will break loose.
Sure you can write safer visitors with no template fallbacks. Then they won't compile. All in all, having same-type alternatives in variant is pretty useless in my opinion, and dangerous. It should be disallowed at class instantiation level -- unless we want to provide indexed get<I> like std.
@joto - great, will need to bench it! How about making this check an optional (default: yes) ? This way it'll protect new users and also will allow power users to turn it off for large type sequences ?
Activity
lightmare commentedon Jan 29, 2016
There's another issue: visitors. Once two alternatives become the same type, a visitor overloading
operator()on both won't compile. That means if you have alternatives that may be the same type on some architecture, you either need to wrap them in a tag struct, or overload visitation on type index. Neither seems ideal to me, but the former can be done even if variant forbids identical types.artemp commentedon Feb 1, 2016
re:
get<T>- Lets not over engineer here, at least not now :). As stated in https://github.com/mapbox/variant/blob/master/README.md the goal is :"..is to maintain external API compatibility with boost::variant such that Mapbox variant can be a "drop in".
Adding more compile checks will increase compile times for not much of a value in my view. Happy to revisit this later when std variant becomes a reality.
Both our variant and boost::variant have well defined behaviour:
lightmare commentedon Feb 1, 2016
Perhaps well-defined, but very dangerous, imo.
As long as
libfooandlibbaruse something unique, like a pointer to an opaque struct for their handles, everything works fine. The day either of them switches to using anintorstd::string, or when they start using the same type for handle, likevoid*, all hell will break loose.Sure you can write safer visitors with no template fallbacks. Then they won't compile. All in all, having same-type alternatives in variant is pretty useless in my opinion, and dangerous. It should be disallowed at class instantiation level -- unless we want to provide indexed
get<I>like std.artemp commentedon Feb 1, 2016
@lightmare - I agree, "same-type alternatives" are pretty useless. My only concern is that compile check can be relatively expensive (thinking about mapnik::expression_node -https://github.com/mapnik/mapnik/blob/master/include/mapnik/expression_node_types.hpp#L169-L196).
How would you propose checking type
uniquenessat compile time?artemp commentedon Feb 1, 2016
Found this one : http://stackoverflow.com/questions/18986560/check-variadic-templates-parameters-for-uniqueness
lightmare commentedon Feb 1, 2016
@artemp Oh, haven't thought about complexity. You're right, it'd be something like
direct_typeover 2 axes,O(n**2).artemp commentedon Feb 1, 2016
Maybe some
optimizationideas are in http://www.boost.org/doc/libs/1_60_0/libs/mpl/doc/refmanual/set.htmljoto commentedon Feb 1, 2016
The best I can come up with is:
At least the inner check isn't recursive this way. We'd probably need to benchmark compiling to be sure, though.
artemp commentedon Feb 1, 2016
@joto - great, will need to bench it! How about making this check an optional (default: yes) ? This way it'll protect new users and also will allow power users to turn it off for large type sequences ?
/cc @lightmare
artemp commentedon Jan 6, 2017
planning to bench finally
/cc @joto @springmeyer @lightmare