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:
The problem here is that MSVC refuses to extract the type of &C::template process<_D>
to use in overload resolution (Note the absence of any meaningful error message). It appears that it considers the instanciated function to be an overload-set of only one function; probably an implementation goof.
You can force it to convert the overload-set to a function pointer type by feeding it into a function parameter:
template<typename T>
T* fn_id(T* func) {
return nullptr;
}
Once T
has been flattened into a function type, you can use it into decltype
.
template <typename C>
static R<1> retrieve(decltype(fn_id(&C::template process<_D>)));
With this hack I get the same output as you had with clang.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With