Search code examples
c++boostboost-mplboost-variant

How to simplify type generated by make_variant_over


Boost variant has a function called make_variant_over that takes an MPL sequence (for example list<A, B, C>) and produces a variant from those types.

However if one looks carefully the type generated is never a simple variant<A, B, C>.

For example in this code,

#include<boost/variant.hpp>
int main(){
    using List = boost::mpl::list<double, int>;
    using Variant = boost::make_variant_over<List>::type;
}

Variant is boost::variant<boost::detail::variant::over_sequence<boost::mpl::list<double, int, mpl_::na, ...> >>.

It looks like it can be used interchangeability with boost::variant<double, int>, but it is not the same type. At best that can generate confusion when reading compiler errors and at worst it can make difficult to implement certain functions that rely on the exact type of the argument.

Is there a way to force a simplification in the produced variant type?


Solution

  • Got it, using boost::mpl::fold. One has to be careful with the recursion because empty template variants cannot be instantiated.

    It can be done, but we are probably not doing the compiler any favor because boost::variant<T1, T2,...> is probably still implemented in terms of boost::variant<...variant::over_sequence<T1, T2,...>>.

    The real power can be that one can use the simplified type construction to make the variants type unique.

    namespace detail{
    template <typename TList, typename T> struct ExtendTList;
    template<typename T>
    struct ExtendTList<boost::variant<void>, T>{
      using type = boost::variant<T>;
    };
    template<typename T, typename... Ts>
    struct ExtendTList<boost::variant<Ts...>, T>{
      using type = boost::variant<Ts..., T>;
    };
    }
    
    template<class Seq>
    using make_simple_variant_over = typename boost::mpl::fold<
        typename boost::mpl::fold<
            Seq,
            boost::mpl::set<>, 
            boost::mpl::insert<boost::mpl::_1, boost::mpl::_2>
        >::type,
        boost::variant<void>, 
        detail::ExtendTList<boost::mpl::_1, boost::mpl::_2>
    >;
    
    ...
    
    using variant_type = make_simple_variant_over<boost::mpl::vector<int, int, long>>::type;
    

    variant_type is now exactly boost::variant<int, long>.

    (and it is not boost::variant<boost::detail::variant::over_sequence<boost::mpl::list<int, long, mpl_::na, ...> >> nor boost::variant<boost::detail::variant::over_sequence<boost::mpl::list<int, int, long, mpl_::na, ...> >>