What I'm trying to do: I have a template object, which, as part of the interface, must have a "process" function defined with a few arguments (I donβt know how many) of which are template arguments.
those.
struct A { static void process(int a); }; struct B { template <typename B0> static void process(int a, B0 b0); };
are valid handlers for receiving. So now I need to find a signature for the handler: parameters with typical typing and several template parameters.
To do this, I use several magic patterns that can be narrowed down to the problem part - detecting a few pattern arguments (or just getting a pattern signature).
The way I try to find the information I need is to verify an explicitly specialized signature using the method described in Is it possible to write a template to verify the existence of a function?
struct _D; template <typename T> struct get_template_args_count { private: template <int count> struct R { enum { value = count }; }; template <typename C> static R<0> retrieve(decltype(&C::process)); template <typename C> static R<1> retrieve(decltype(&C::template process<_D>)); template <typename C> static R<-1> retrieve(...); public: typedef decltype(retrieve<T>(nullptr)) Result; enum { value = Result::value }; }; int main(int argc, char* argv[]) { std::cout << "A::process " << get_template_args_count<A>::value << "\n" << "B::process " << get_template_args_count<B>::value << "\n"; std::cin.get(); return 0; }
With clang (built using msvc2013 or the Linux version built using gcc-4.9.2), it compiles and outputs:
A::process 0 B::process 1
With msvc2012 it also compiles, but produces:
A::process 0 B::process -1
When silenced, commenting on the backup case (the one who has (...)) msvc2012 freaks out:
main.cpp(28): error C2893: Failed to specialize function template 'get_template_args_count<T>::R<count> get_template_args_count<T>::retrieve(unknown)' with [ T=B, count=1 ] With the following template arguments: 'B' v:\test\test\test\main.cpp(63) : see reference to class template instantiation 'get_template_args_count<T>' being compiled with [ T=B ] main.cpp(28): error C2893: Failed to specialize function template 'get_template_args_count<T>::R<count> get_template_args_count<T>::retrieve(unknown)' with [ T=B, count=0 ] With the following template arguments: 'B' main.cpp(29): error C2825: 'get_template_args_count<T>::Result': must be a class or namespace when followed by '::' with [ T=B ] main.cpp(29): error C2039: 'value' : is not a member of '`global namespace'' main.cpp(29): error C2275: 'get_template_args_count<T>::Result' : illegal use of this type as an expression with [ T=B ] main.cpp(29): error C2146: syntax error : missing '}' before identifier 'value' main.cpp(29): error C2143: syntax error : missing ';' before '}' main.cpp(29): error C2365: 'value' : redefinition; previous definition was 'enumerator' main.cpp(29) : see declaration of 'value'
(the log is slightly reformatted to accept fewer lines)
I also tried to use the various methods described in the comments on the above question (using char [sizeof], using typeof and moving the check to return type), but to no avail - either they give the same results, or (including "unexpected end of file" for no obvious reason )
I also checked a similar question Derive Variadic args and return type from functor template parameter (MSVC-specific) using another method (prototype comparison via SFINAE), but I cannot figure out how to use it when I don't know the exact signature (t .e. I do not know the number and types of static parameters). Of course, I can copy them for a specific task, but ...
So, I have two questions:
- Why should it always be so difficult with MSVC? .. Well, it's rhetorical, there is no answer.
- Am I abusing some kindness of clang / gcc and is MSVC actually doing the right thing by throwing meaningless errors on my face? Are there any workarounds or the right ways to do this, other than crudely forcing all possible combinations of static / template parameters and comparing them using a full signature prototype?