c++templatesboostboost-mpl

Assert if template parameter is allowed


I'm trying to limit a templated method to a given list of allowed types and their "repeated" flavour.

typedef boost::mpl::set<bool, int, double> allowedTypes;

class Foo
{
    typedef boost::mpl::set<bool, int, double> allowedTypes;
    template<class T>
    void some_templated_method()
    {
       BOOST_MPL_ASSERT((boost::mpl::has_key<allowedTypes, T>));
    }
}


// main.cpp
Foo foo;
struct restricted_type{};
foo.some_templated_method<restricted_type>(); // Compiles, why ?!

Other than that, I would like to know how to automatically filter the repeated version of the allowed types. With repeated version I mean their std::vector<T> representation without explicitating it within the mpl::set

e.g.

typedef boost::mpl::set<bool, 
                        int, 
                        double, 
                        std::vector<bool>,
                        std::vector<int>,
                        std::vector<double> > allowedTypes;

Solution

  • You could always automate the creation of a sequence that includes the original set and their container-wrapped counterparts.

    To that end we need a unary lambda:

    template <template <typename...> class Container>
    struct MakeContainerOfT {
        template <typename T>
        struct impl {
            using type = Container<T>;
        };
     };
    

    This can handle every container except maps (they need a value type) and arrays (there is a non-type template parameter). As a bonus, here is the factory to make arrays of size N:

    template <std::size_t N>
    struct ArrayOfT {
        template <typename T>
        struct impl {
            using type = std::array<T, N>;
        };
    };
    

    Now, we need a facility that applies this lambda (or any other unary lambda) on our set for any container we are given.

    template <typename Sequence, template <typename> class Transform>
    struct TransformedSequenceBuilder {
        using type = typename boost::mpl::reverse_fold<Sequence,
                                          boost::mpl::set0<>,
                                          boost::mpl::insert<boost::mpl::_1, 
                                                             Transform<boost::mpl::_2>>>::type;
    };
    

    We can finally proceed with the "accumulator" that will perform this for a variadic sequence of transformation lambdas:

    template <typename Sequence, template <typename> class... Transforms>
    struct MakeFullSequence;
    
    template <typename Sequence, template <typename> class Transform, template <typename> class... Tail>
    struct MakeFullSequence<Sequence, Transform, Tail...> {
    
        using type = typename boost::mpl::reverse_fold<typename MakeFullSequence<Sequence, Tail...>::type,
                                          typename TransformedSequenceBuilder<Sequence, Transform>::type,
                                          boost::mpl::insert<boost::mpl::_1, boost::mpl::_2>>::type;
    };
    
    template <typename Sequence>
    struct MakeFullSequence<Sequence> {
        typedef Sequence type;
    };
    

    The final step is to define an alias for the containers you are interested in:

    template <typename Sequence>
    using wrapped_set = typename MakeFullSequence<Sequence,
                                                  ContainerOfT<std::vector>::template impl,
                                                  ContainerOfT<std::set>::template impl//,
                                                  /* any transformation you fancy here */>::type;
    

    To test this, we can perform an equality test:

    using le_set = boost::mpl::set<int, double, char>;
    using le_vector_set = boost::mpl::set<std::vector<int>, std::vector<double>, std::vector<char>>;
    using le_set_set = boost::mpl::set<std::set<int>, std::set<double>, std::set<char>>;
    
    using le_transformed_set = wrapped_set<le_set>;
    using le_manually_transformed_set = boost::mpl::joint_view<boost::mpl::joint_view<le_set, le_set_set>::type, le_vector_set>::type;
    
    std::cout << boost::mpl::equal<le_transformed_set,
                                   le_manually_transformed_set>::value;
    

    The usage is then really simple: the user only provides the "raw" set of types Set and you branch your logic onto wrapped_set<Set> each time you need it:

    class Foo
    {
        typedef boost::mpl::set<bool, int, double> allowedTypes;
        template<class T>
        void some_templated_method()
        {
           BOOST_MPL_ASSERT((boost::mpl::has_key<wrapped_set<allowedTypes>, T>));
        }
    };
    

    You can find the demo illustrating we do end up with the union of the original set, the vector-wrapped set and the set-wrapped set here: Live Demo

    With that design, you can also add any other "repeated flavour" you'ld like. Referenced (resp. const) counterpart of the input types? Just pass in std::add_lvalue_reference (resp. std::add_const)!