Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to check for existence of member templates just by an identifier?

Can we detect member function template, variable template, class/struct/union template or alias template without knowing amount, or nature of template/non-template parameters?

When I try to think about this, nothing really comes to my mind. But let's have structure with member function template:

struct foo
{
    // Really random. Let's assume we don't know this declaration, just the name "bar"
    template <class T, std::size_t N, class... Args>
    void bar(T a, T b, T(&c)[N], Args const& ...);
};

How do I check if the foo::bar template exists?

Instantiation-based type traits don't apply here, because (theoretically) we don't have knowledge of which parameters we should use, in what order and how many of them. Maybe some magic lookup method would be appropriate? Or maybe it's just impossible?


When searching, I found this question, but solutions in the answers require knowledge about nature of template.


Here is my first failed attempt for detecting struct template:

struct foo
{
    template<class T>
    struct bar { };
};

template <class T, class = void>
struct has_template_bar : std::false_type
{ };

template <class T>
struct has_template_bar <T, void> : std::true_type
{
    template<template<class...> class tplt_tplt = T::bar> // Invalid default argument
    struct placebo
    { };
};
like image 726
xinaiz Avatar asked Oct 01 '16 22:10

xinaiz


3 Answers

I can show you how to detect a struct template:

template < class > struct check_template : std::false_type {};

// Specialize for template classes
template <template<class...> class X, class... Args>
struct check_template< X<Args...> > : std::true_type {};

You can then probably play around with declval, void_t, etc. to detect member templates.

In case you want to detect types vs. meta-types, i.e. templates like std::vector and not std::vector<int>, you can do the following:

#include <iostream>

template <template<class...> class>
constexpr bool is_template()
{
    return true;
}

template <class>
constexpr bool is_template()
{
    return false;
}

struct Foo{};

template<class>
struct TemplateFoo{};

int main()
{
     std::cout << std::boolalpha;
     std::cout << is_template<Foo>() << std::endl;
     std::cout << is_template<TemplateFoo>() << std::endl;
}

Live on Coliru

Note that the solutions won't work if the meta-type has any non-type parameters, like

template<class, int> struct X{};
like image 101
vsoftco Avatar answered Nov 19 '22 11:11

vsoftco


This approach works in the presence of multiple overloads, returning false_type if and only if there are no methods, or members, called bar. It doesn't tell us anything useful about what the bar(s) is/are though (more on that later).

(Note: This answer (and question?) are dupes. I learned about this technique on SO only a few days ago. But I can't find the original!)

This uses void_t, which you may need to define yourself (not in c++11, for example):

template<typename ...T>
struct voider { using type = void; };
template<typename ...T>
using void_t = typename voider<T...> :: type;

bar is the member we are interested in, so we make a really boring struct with a member called bar:

struct just_a_bar { int bar; };

Then a template, given T, which declares a struct that inherits from both T and just_a_bar.

template<typename T>
struct MultipleBars : public T , public just_a_bar { };

Now, decltype(MultipleBars<T>::bar) will give an ambiguity error if, and only if, there is a member bar in T. We can use this:

template<typename T, typename =void>
struct has_at_least_one_bar : public true_type {};

template<typename T>
struct has_at_least_one_bar<T, void_t< decltype(MultipleBars<T>::bar) >>
    : public false_type { 
};

Then, to use the above for real:

struct zero { };
struct one { 
    void bar(int,int);
};
struct two { 
    //template<typename P, typename Q> // works fine with templates too
    void bar(int);
    void bar(int,int);
};


int main() { 
    cout << boolalpha;
    cout << has_at_least_one_bar<zero>{} << endl; // false
    cout << has_at_least_one_bar<one>{} << endl;  // true
    cout << has_at_least_one_bar<two>{} << endl;  // true
}

Once you know bar exists, you probably want more detail. If you have a few specific patterns in mind (non-template member, template method with type parameters only, template method with two int non-type parameters, template method with three template-template parameters, ...) then I think you can test for each of those patterns individually. But ultimately there are limits to what you can detect with a finite number of such patterns. (And the fact that you mean templated methods, not templated structs, might make this more difficult

like image 4
Aaron McDaid Avatar answered Nov 19 '22 12:11

Aaron McDaid


In C++14, you can use template variables to detect if a type is a specialization:

#include <type_traits>

template<typename>
constexpr bool is_spec = false;

template<template<typename...> class T, typename... U>
constexpr bool is_spec<T<U...>> = true;

struct S {};
template<typename> struct R {};

int main() {
    static_assert(not is_spec<S>, "!");
    static_assert(is_spec<R<void>>, "!");
}

Note that it won't work if non-type parameters are involved (as an example template<int> struct R {};).

like image 3
skypjack Avatar answered Nov 19 '22 11:11

skypjack