1

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;
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
codeJack
  • 2,423
  • 5
  • 24
  • 31
  • I'm just letting you know I edited my answer a bit to allow more genericity on the type transformations. The core ideas remains identical but it will allow you more flexibility should you need it :) – Rerito Jun 20 '17 at 07:40

2 Answers2

1

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)!

Rerito
  • 5,886
  • 21
  • 47
0

I think the right approach would be to add custom type trait that should be specialized to have true value for types supported by some_templated_method.

#include <string>
#include <type_traits>
#include <vector>

template< typename T, typename TDummy = void > class
t_GoodForSomeTemplateMethod final
:   public ::std::integral_constant< bool, false >
{
    //  Nothing.
};

template<> class
t_GoodForSomeTemplateMethod< bool > final
:   public ::std::integral_constant< bool, true >
{
    //  Nothing.
};

template< typename TInt > class
t_GoodForSomeTemplateMethod< TInt, ::std::enable_if_t< ::std::is_integral< TInt >::value > > final
:   public ::std::integral_constant< bool, true >
{
    //  Nothing.
};

template< typename TFlt > class
t_GoodForSomeTemplateMethod< T, ::std::enable_if_t< ::std::is_floating_point< TFlt >::value > > final
:   public ::std::integral_constant< bool, true >
{
    //  Nothing.
};

template< typename TItem, typename TAllocator > class
t_GoodForSomeTemplateMethod< ::std::vector< TItem, TAllocator > > final
:   public ::std::integral_constant< bool, true >
{
    //  Nothing.
};

template< class T > void
SomeTemplateMethod(void)
{
    static_assert(t_GoodForSomeTemplateMethod< T >::value, "type is not suitable for SomeTemplateMethod");
}

int
main(void)
{
    SomeTemplateMethod< int >(); // ok
    SomeTemplateMethod< ::std::vector< char > >(); // ok
    SomeTemplateMethod< ::std::string >(); // error
}
user7860670
  • 35,849
  • 4
  • 58
  • 84