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 c 11:
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 c 17 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 c 20, 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.