12

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
    { };
};
Community
  • 1
  • 1
xinaiz
  • 7,744
  • 6
  • 34
  • 78
  • If there are two such methods, I guess you also want it to return true? – Aaron McDaid Oct 01 '16 at 22:29
  • @AaronMcDaid That would be great! But I'm afraid that adding possibility of overloading might complicate things even more. – xinaiz Oct 01 '16 at 22:33
  • I'm sure I saw an answer to this just a few days ago, which also covered the overload case. Can't find it now though. I'll keep looking! – Aaron McDaid Oct 01 '16 at 22:35
  • It (let me say) _doesn't exist_ until you specialize it. Do you want to detect if it exists `bar`, as an example? – skypjack Oct 01 '16 at 22:39
  • @skypjack I partially know how to implement it when I know what I can use as arguments, but the problem arises when I don't know if I can use template template argument, type argument or non-type argument, and if the count of arguments is correct. – xinaiz Oct 01 '16 at 22:43
  • @skypjack I thought about your comment. I don't think you can simply detect `TemplateFoo`, as it's not a type but a meta-type (used to generate types). So I don't see any way of specializing on it. – vsoftco Oct 01 '16 at 22:48
  • @vsoftco That's what I'm saying. He cannot detect something that _doesn't exist_ yet, unless by giving it the births. I guess the OP wants to detect if at least a specialization exists, no matter what. Am I wrong? The question is a bit unclear. – skypjack Oct 01 '16 at 22:51
  • @skypjack Although you can have stuff like `template – vsoftco Oct 01 '16 at 22:53
  • @vsoftco This question is somehow intriguing indeed, if we are going to discuss about member methods. – skypjack Oct 01 '16 at 22:55
  • The answers here are very different from each other, and make me worry that I don't even understand the question. What is the actual question such a trait would answer? "Is there at least one method called `bar`, which may or may not be a template?" ***or*** "Ignoring non-template methods, is there at least one template method called bar?" ***or*** something else entirely? – Aaron McDaid Oct 01 '16 at 23:14
  • 1
    @AaronMcDaid Well, I try to think simply about this :) The original idea is: check if structure has declared (possibly overloaded, that came later) member template (of listed kinds). Your answer is probably closest to that - I mean you can detect templates by their names alone, and thats great. The problem is that this method doesn't distinguish between templates and non-templates (not a big problem in my point of view, basic idea is completed). Bigger drawback is that checked structure cannot be `final` (because you derieve from it). – xinaiz Oct 01 '16 at 23:19

4 Answers4

5

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{};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vsoftco
  • 55,410
  • 12
  • 139
  • 252
4

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

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • What should be the template argument when calling `has_at_least_one_bar`? When I try `just_a_bar`, then `MultipleBars` derieves twice from it. – xinaiz Oct 01 '16 at 23:00
  • I forgot to add an example of usage. I've just added some examples now. `just_a_bar` is an implementation detail of this trait. We need a 'dummy' type that has a (useless) `bar` method/member. – Aaron McDaid Oct 01 '16 at 23:10
  • 1
    Why not simply `template using void_t = void;`? I think the need for an intermediate `struct` was due to a defect in standard, but now the simple definition should work. – vsoftco Oct 01 '16 at 23:32
  • Oh, so to detect underlying template, it would require to provide `3^n` (template template, type and non-type parameters) overloads (I'm still not sure about these), for example for max. 10 arguments, 59049 overloads would be required - and that just for one small `bar` ;) – xinaiz Oct 01 '16 at 23:32
  • @vsoftco, correct about `void_t`. Actually, did any versions of g++,clang,MSVC have a faulty implementation of that? If not, then I'd happily trust it. I don't want to trip up one SO user who happens to be on the wrong version of their compiler – Aaron McDaid Oct 01 '16 at 23:37
3

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 {};).

skypjack
  • 49,335
  • 19
  • 95
  • 187
1

I think I got it. Thanks to Aaron McDaid and vsoftco answers, I succeeded in detecting member type templates (alias template, struct template, class template and union template), member function templates with one additional drawback, and member variable templates.

This implementation has some drawbacks:

  • Class foo that we check for existence of name bar musn't be final type.
  • Templates with mixed type/non-type/template-template parameters won't be detected.
  • Code is kinda long.

Additional drawback is:

  • [Note: Will be fixed soon!] Checking for member function tempalates will return true, if class foo has any overloaded function bar. I just didn't have any means to detect overloaded function alone. This will also affect final has_member_template type trait.

Here is the implementation:

#include <iostream>
#include <type_traits>
#include <iomanip>

/***Check if type is template***/
template <template<class...> class>
constexpr bool is_template_type()
{
    return true;
}

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

/***Check if T has static member function "bar" ***/
template <class, class = void>
struct has_static_member_function_bar : std::false_type
{ };

template <class T>
struct has_static_member_function_bar<T,
    std::enable_if_t<std::is_function<typename std::remove_pointer<decltype(&T::bar)>::type>::value
        >
    > : std::true_type
{ };

/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_member_function_bar : std::false_type
{ };


template <class T>
struct has_member_function_bar<T,
    std::enable_if_t<std::is_member_function_pointer<decltype(&T::bar)>::value
        >
    > : std::true_type
{ };

/***Check if T has member reference "bar" ***/
template <class, class = void>
struct has_member_reference_bar : std::false_type
{ };

template <class T>
struct has_member_reference_bar<T,
    std::enable_if_t<std::is_reference<decltype(T::bar)>::value
        >
    > : std::true_type
{ };

/***Check if T has static member object "bar" ***/
template <class, class = void>
struct has_static_member_object_bar : std::false_type
{ };

template <class T>
struct has_static_member_object_bar<T,
    std::enable_if_t<std::is_object<typename std::remove_pointer<decltype(&T::bar)>::type>::value &&
                    (!std::is_member_object_pointer<decltype(&T::bar)>::value)

        >
    > : std::true_type
{ };

/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_member_object_bar : std::false_type
{ };

template <class T>
struct has_member_object_bar<T,
    std::enable_if_t<std::is_member_object_pointer<decltype(&T::bar)>::value
        >
    > : std::true_type
{ };

/***Check if T has member alias, struct, class, union template "bar" ***/
template <class, class = void>
struct has_member_type_template_bar : std::false_type
{ };

template <class T>
struct has_member_type_template_bar<T,
    std::enable_if_t<is_template_type<T::template bar>()
        >
    > : std::true_type
{ };

/***Check if T has at least one name "bar" ***/
struct has_at_least_one_bar_impl { int bar; };

template<typename T>
struct bar_overloads : T , has_at_least_one_bar_impl { };

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

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

/***Check if T has member object, reference, not-overloaded function "bar" ***/
template <class, class = void>
struct has_non_type_non_overloaded_member_bar : std::false_type
{ };

template <class T>
struct has_non_type_non_overloaded_member_bar<T,
    std::void_t<decltype((void)(T::bar))>> : std::true_type
{ };


/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_type_member_bar : std::false_type
{ };

template <class T>
struct has_type_member_bar<T,
    std::void_t<typename T::bar>> : std::true_type
{ };

/***Check if T has no more than one member "bar" ***/
template<class, class = void, class = void>
struct has_at_most_one_bar : std::false_type
{ };

template<class T>
struct has_at_most_one_bar<T,
    std::enable_if_t<
        has_type_member_bar<T>::value ||
        has_non_type_non_overloaded_member_bar<T>::value
        >
    > : std::true_type
{ };

/***Check if T has member function template "bar" ***/
template <class, class = void>
struct has_member_function_template_bar : std::false_type
{ };

template <class T>
struct has_member_function_template_bar<T,
    std::enable_if_t<has_at_least_one_bar<T>::value &&
        (!has_member_type_template_bar<T>::value) &&
        (!has_non_type_non_overloaded_member_bar<T>::value) &&
        (!has_member_function_bar<T>::value) &&
        (!has_type_member_bar<T>::value)
        >
    > : std::true_type
{ };

/***Check if T has member variable template "bar" ***/
template <class, class = void>
struct has_member_variable_template_bar : std::false_type
{ };

template <class T>
struct has_member_variable_template_bar<T,
    std::enable_if_t<has_at_least_one_bar<T>::value &&
        (!has_member_type_template_bar<T>::value) &&
        (!has_member_function_template_bar<T>::value) &&
        (!has_type_member_bar<T>::value) &&
        (!has_static_member_function_bar<T>::value) &&
        (!has_member_function_bar<T>::value) &&
        (!has_member_object_bar<T>::value) &&
        (!has_member_reference_bar<T>::value) &&
        (!has_static_member_object_bar<T>::value)>
    > : std::true_type
{ };

/***Check if T has any member template "bar" ***/
template <class, class = void>
struct has_member_template_bar : std::false_type
{ };

template <class T>
struct has_member_template_bar<T,
    std::enable_if_t<has_member_type_template_bar<T>::value ||
        has_member_function_template_bar<T>::value ||
        has_member_variable_template_bar<T>::value>
    > : std::true_type
{ };

Live example

Example output:

---Has type template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         false
consists_reference:               false
consists_t_alias:                 true
consists_t_struct:                true
consists_t_class:                 true
consists_t_union:                 true
consists_t_variable:              false
consists_t_function:              false
consists_t_overloaded_function:   false
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       false
consists_s_t_function:            false
consists_s_t_overloaded_function: false

--Has member function template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         true // implmementation bug
consists_reference:               false
consists_t_alias:                 false
consists_t_struct:                false
consists_t_class:                 false
consists_t_union:                 false
consists_t_variable:              false
consists_t_function:              true
consists_t_overloaded_function:   true
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       true // implmementation bug
consists_s_t_function:            true
consists_s_t_overloaded_function: true

--Has member variable template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         false
consists_reference:               false
consists_t_alias:                 false
consists_t_struct:                false
consists_t_class:                 false
consists_t_union:                 false
consists_t_variable:              true
consists_t_function:              false
consists_t_overloaded_function:   false
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       false
consists_s_t_function:            false
consists_s_t_overloaded_function: false

--Has any member template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         true // implmementation bug
consists_reference:               false
consists_t_alias:                 true
consists_t_struct:                true
consists_t_class:                 true
consists_t_union:                 true
consists_t_variable:              true
consists_t_function:              true
consists_t_overloaded_function:   true
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       true // implmementation bug
consists_s_t_function:            true
consists_s_t_overloaded_function: true

I'm still sad that I couldn't detect overloaded functions... But it was fun :)

Community
  • 1
  • 1
xinaiz
  • 7,744
  • 6
  • 34
  • 78