Home > Net >  type checking constexpr function to check non-template type or template type on C 17
type checking constexpr function to check non-template type or template type on C 17

Time:10-27

I wrote type checking constexpr function. It the type is type1 or type2 then returns true, otherwise returns false.

Here is the code. It works as I expected.

#include <type_traits>

struct type1{};
struct type2{};
struct type3{};

template <typename T>
constexpr bool is_type1or2() {
    return std::is_same_v<T, type1> || std::is_same_v<T, type2>;
}

static_assert(is_type1or2<type1>());
static_assert(is_type1or2<type2>());
static_assert(!is_type1or2<type3>());

int main(){}

https://godbolt.org/z/dncKo1Pbb

Now, type1 is changed to template that has non-type parameter. How to do the same type checking?

#include <type_traits>

template <std::size_t N>
struct type1{};
struct type2{};
struct type3{};

template <typename T>
constexpr bool is_type1or2() {
    return std::is_same_v<T, type2>;
}

template <typename T, std::size_t N>
constexpr bool is_type1or2() {
    return std::is_same_v<T, type1<N>>;
}

// I want to write as follows but I couldn't find a way, so far.
// static_assert(is_type1or2<type1<42>>());
// so I pass explicit second template argument 42.
static_assert(is_type1or2<type1<42>, 42>());

static_assert(is_type1or2<type2>());
static_assert(!is_type1or2<type3>());

int main(){}

https://godbolt.org/z/G1o5447z8

I tried but I can't eliminate the second template argument. It avoids generic code. Is there any good way to check the type is type1<anyN> or type2 ?

In my actual case, I have 20 of non template types like type2 and 20 of template types like type1. And half of them need to match. I want to avoid code repeatation as long as I can.

Clarify requirement

For template type type1<N>, N is not important. Both template is type1 is important. So the result of is_type1or2<type1<10>>() and is_type1or2<type1<20>>() are always same. I don't need to define individual template argument specialization based matching.

CodePudding user response:

You can make your own type trait:

template <typename>
struct is_type1 : public std::false_type {};

template <std::size_t N>
struct is_type1<type1<N>> : public std::true_type {};

This still requires the usage to instantiate an actual type:

static_assert(is_type1or2<type1<42>>());

Demo

You can also do it this way:

template <template<std::size_t> class>
struct is_type1 : public std::false_type {};

template <>
struct is_type1<type1> : public std::true_type {};

But then is_type1 can only be instantiated with templates that have a std::size_t parameter. So you can no longer do is_type1<T>::value || std::is_same_v<T, type2> because is_type1 only accepts templates while std::is_same_v only accepts types, and T cannot be both. It can work if type2 is also defined like type1, that is:

template <std::size_t N>
struct type1{};
template <std::size_t N>
struct type2{};

template <template<std::size_t> class>
struct is_type1or2_t : public std::false_type {};
template <>
struct is_type1or2_t<type1> : public std::true_type {};
template <>
struct is_type1or2_t<type2> : public std::true_type {};

template <template<std::size_t> class T>
constexpr bool is_type1or2() {
    return is_type1or2_t<T>::value;
}

But you still can't use is_type1or2 with type3, even in a static_assert. So you also need to add an overload for simple types:

template <typename>
constexpr bool is_type1or2() {
    return false;
}

Demo

If you also need an instantiation of type1 or type2 to match, you can add yet another type trait:

template <typename>
struct is_type1or2_instantiation_t : public std::false_type {};
template <std::size_t N>
struct is_type1or2_instantiation_t<type1<N>> : public std::true_type {};
template <std::size_t N>
struct is_type1or2_instantiation_t<type2<N>> : public std::true_type {};
// [...]
template <typename T>
constexpr bool is_type1or2() {
    return is_type1or2_instantiation_t<T>::value;
}

Demo

CodePudding user response:

This can be solved easily if you change the syntax of the static_assert to accept the type as a function argument, as this will allow function template argument deduction (see Takatoshi Kondo's answer).

However, this can also be solved by writing a template that checks whether a type is an instantiation of a template:

template<template <std::size_t> typename, typename>
struct is_instance_of : std::false_type {};

template<template <std::size_t> typename T, std::size_t N>
struct is_instance_of<T, T<N>> : std::true_type {}; 

Now is_instance_of can be used in the function (without argument deduction) as:

template <typename T>
constexpr bool is_type1or2() {
    return 
        is_instance_of<type1, T>::value  // repeat for other templates that take a size_t parameter 
        or std::is_same_v<T, type2>;     // repeat for non-template types
}

If you have other template types that you want to allow (i.e. templates that take parameters other than a size_t), you can edit is_instance_of as needed.

Here's a demo.

CodePudding user response:

After some of try and error, I finally found the solution.

In my actual case, I have 20 of non template types like type2 and 20 of template types like type1. And half of them need to match. I want to avoid code repeatation as long as I can.

In order to demonstrate my situation above, I added type4 non-type template similar to type1. And type1, type2 and type4 need to match.

#include <type_traits>

template <std::size_t N>
struct type1{};
struct type2{};
struct type3{};
template <std::size_t N> // type4 added
struct type4{};

// overload for non-templates
template <typename T>
constexpr bool is_type1or2or4(T) { // or 4 added
    return std::is_same_v<T, type2>;
}

// overload for templates
template <template <std::size_t> typename T, std::size_t N>
constexpr bool is_type1or2or4(T<N>) { // or 4 added
    return 
        std::is_same_v<T<N>, type1<N>> ||
        std::is_same_v<T<N>, type4<N>>;
}

static_assert(is_type1or2or4(type1<42>()));
static_assert(is_type1or2or4(type2()));
static_assert(!is_type1or2or4(type3()));
static_assert(is_type1or2or4(type4<12>()));

int main(){}

https://godbolt.org/z/17GbGK738

I use template argument deduction. Then I can extract N and pass it as template argument. There are two overload funtion templates. One of for non-template types, the other is template types. It works fine.

  • Related