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