What I'm trying to do: I've got a template object incoming which, as part of an interface, should have a "process" function defined with a number of arguments (I don't know how many) some of which are template arguments.
I.e.
struct A { static void process(int a); };
struct B { template <typename B0> static void process(int a, B0 b0); };
are both valid handlers to receive. So now I need to detect a signature for a handler: static-typed parameters and a number of template parameters.
To do so, I'm using a number of template magic hacks which may be narrowed down to the problematic part - detecting a number of template args (or just retrieving a templated signature).
The way I'm trying to find out the required info is by checking for a explicitly specialized signature using the method described in Is it possible to write a template to check for a function's existence?
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 with msvc2013 or linux version, built with gcc-4.9.2) it compiles and outputs:
A::process 0
B::process 1
With msvc2012 it compiles too, but outputs:
A::process 0
B::process -1
When cornered in by commenting out the fallback case (the one with (...)) 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 take less lines)
I've also tried to use different techniques described in comments of the question above (using char[sizeof], using typeof and moving the check into return type), to no avail - it either produce the same results or falls apart with even weirder errors (including "unexpected end-of-file" with no obvious reason).
I've also checked out a similar question Deduce variadic args and return type from functor template parameter (MSVC-specific) with another technique (comparing prototypes via SFINAE), but I can't see how to use it when I don't know exact signature (i.e. I don't know a number and types of static parameters). I may brute-force them for a specific task at hand, of course, but...
So I've got two questions:
- Why it should be always so hard with MSVC?.. Ok, that's a rhetoric one, no answer is needed.
- Am I abusing some kindness of clang/gcc and actually MSVC is doing the right thing by throwing meaningless errors at my face? Are there any workarounds or right ways to do this, other than brute-forcing all possible static/template parameters combinations and comparing them using a full signature prototype?