Home > Net >  is it possible to generate a set of template classes by using another template and recursion?
is it possible to generate a set of template classes by using another template and recursion?

Time:01-09

I want to create a few template classes in order to assert certain type of data:

assertType { static constexpr bool v{false}; };
template<> struct assertType<int> { static constexpr bool v{true}; };
template<> struct assertType<bool> { static constexpr bool v{true}; };
template<>struct assertType<float> { static constexpr bool v{true}; };
template<>struct assertType<long> { static constexpr bool v{true}; };

However, I would like to do it programmatically, So I thought about defining a "list" of the supported types:

template<typename ...Types>
struct TypesList { };
static inline TypesList<int, bool, float, long> supportedTypes;

And have that "list" passed to another template that, by recursion, generates the "assertType" template for every type in the list. Something like:

template<typename ...Ts>
struct BuildTemplates { };

template<typename ...Ts>
struct BuildTemplates<TypesList<Ts...>> { };

BuildTemplates<supportedTypes> /* Build the templates for int, bool, float and long */

So I can use:

assertType<int>::v // v should be true
assertType<bool>::v // v should be true
assertType<float>::v // v should be true
assertType<long>::v // v should be true

Any other template whose type is not one of those, should have its v value set to false.

Is this possible? Thanks in advance.

CodePudding user response:

The answer mostly depends on the syntax you want to get. For example, you could do this:

#include <type_traits>

template<typename... Types> struct TypeList {};

using SupportedTypes = TypeList<int, bool, float, long>;

template<typename T>
struct assertType {
    template<class... Types>
    static constexpr bool contains(TypeList<Types...>) {
        return (std::is_same_v<T, Types> || ...);
    }

    static constexpr bool v = contains(SupportedTypes{});
};

static_assert( assertType<int>::v);
static_assert( assertType<bool>::v);
static_assert(!assertType<short>::v);

If you can use boost, boost::mp11::mp_contains could be employed to make the implementation trivial:

#include <boost/mp11.hpp>

using SupportedTypes = boost::mp11::mp_list<int, bool, float, long>;

template<typename T>
struct assertType : boost::mp11::mp_contains<SupportedTypes, T> {};

static_assert( assertType<int>::value);
static_assert( assertType<bool>::value);
static_assert(!assertType<short>::value);

Or if you want to use v instead of value (which is de facto a standard name):

template<typename T>
struct assertType {
    static constexpr bool v =
        boost::mp11::mp_contains<SupportedTypes, T>::value;
};

CodePudding user response:

This is only possible if your programmatic solution is allowed to define a template specialization of assertType, or to be used within its template definition.

Here's an approach by defining a single partial template specialization of assertType in :

template <typename>
struct assertType { static constexpr bool v = false; };

template <typename...>
struct TypeList {};

using supportedTypes = TypesList<int, bool, float, long>;

#include <type_traits>

template <typename...>
struct any_of : std::false_type {};

template <typename T, typename U, typename... Us>
struct any_of<T, TypeList<U, Us...>> : any_of<T, TypeList<Us...>> {};

template <typename T, typename... Us>
struct any_of<T, TypeList<T, Us...>> : std::true_type {};

template <typename T>
struct assertType<typename std::enable_if<any_of<T, supportedTypes>, T>::type> {
  static constexpr bool v = true;
};

You could also just use it within the class template definition itself rather than defining a partial specialization:

// assuming supportedTypes and any_of have already been defined

template <typename T>
struct assertType { static constexpr bool v = any_of<T, supportedTypes>::value; };

In the definition of any_of becomes much simpler with a fold expression and std::bool_constant:

#include <type_traits>

template <typename...>
struct any_of : std::false_type {};

template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::bool_constant<(... || std::is_same_v<T, Us>)> {};

Or by using std::disjunction:

#include <type_traits>

template <typename...>
struct any_of : std::false_type {};

template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::disjunction<std::is_same_v<T, Us>...> {};

Finally in , you can simplify the definition of the assertType partial template specialization using concepts:

// assuming supportedTypes and any_of have already been defined

template <typename T, typename L>
concept any_of_v = any_of<T, L>::value;

template <typename>
struct assertType { static constexpr bool v = false; };

template <any_of_v<supportedTypes> T>
struct assertType<T> { static constexpr bool v = true; };

CodePudding user response:

Except for instantiating a template or preprocessor trickery, there is no way to generate declarations in C automatically. Each declaration must be written out explicitly.

But there is no need to have an explicit specialization for each type in the list. You can simply use a single primary template for assertType and set the v member according to membership in the list or you can use a fixed number of partial specializations. The other answers give approaches to do this.

  • Related