Reputation: 2043
Consider the following templated member function:
template<typename E, typename N, typename P>
void Node::connectEvent( const bool( N::*fn )( const P& ), N *inst )
{
// Obtain unique event ID based on type.
size_t eventId = typeid( E ).hash_code();
// Actual code wraps the function returned from std::bind,
// but for this example let's assume we can store it directly.
mCallbacks[eventId] = std::bind( fn, inst, std::placeholders::_1 );
}
I'd like to be able to call this function in the following way:
connectEvent<MouseDownEvent>( &MyNode::mouseDown, this );
, where the callback function is defined as:
bool MyNode::mouseDown( const MouseDownEvent &event );
, or even using a base class as parameter, which is why I have a separate event type E and parameter type P in the template:
bool MyNode::mouseDown( const Event &event );
I also need support for this:
connectEvent<DrawEvent>( &MyNode::draw, this );
, where the callback function is defined as:
bool MyNode::draw();
Question: to support the latter, I want to specialize the connectEvent
function for the case where parameter P is void, because it requires a different call to std::bind
. I've tried many different approaches, including using a combination of enable_if
and is_void
on the base template, but none compiled, so I must be doing something wrong and have resorted to trial-and-error at this point.
In most cases, the Visual Studio 2015 compiler complains about "illegal use of explicit template arguments".
Here's a version of the code that I thought would work, but didn't:
template<typename E, typename N>
void Node::connectEvent<E,N,void>( const bool( N::*fn )(void), N *inst )
{
size_t eventId = typeid( E ).hash_code();
mCallbacks[eventId] = std::bind( fn, inst );
}
What should I change in my code to make this possible?
Upvotes: 0
Views: 82
Reputation: 2043
Thanks to the answers given here, especially T.C.'s remarks, I realized the problem lies in the combination of the two functions: even if we could write two valid versions, the compiler would not be able to properly distinguish between them because it treats the void
as a parameter and always tries to construct the first version. It then reports an error trying to use the const void ¶m
.
The only solution that worked, was using a different name for the connectEvent
function, e.g.:
template<typename E, typename N>
void Node::connectVoidEvent( const bool( N::*fn )( ), N *inst );
, so that the compiler does not get confused about which version I want to use. I'll probably not going to use this, though, and will instead always require a single parameter for the callback method.
For what it's worth: I also tried to do this instead, shifting the problem to the call to std::bind
, but couldn't figure out where to go from there:
template<typename E, typename F, typename N>
void Node::connectEvent( const F &fn, N *inst )
{
// F has type: (__thiscall MyNode::*)(void)
// or something like: (__thiscall MyNode::*)(const Event&)
size_t eventId = typeid( E ).hash_code();
// How will the compiler know the number of parameters of F,
// and whether or not to use placeholders?
mCallbacks[eventId] = std::bind( fn, inst, std::placeholders::_1 );
}
But that's a different question. Anyway, thanks for everyone's input.
Upvotes: 0
Reputation: 9416
Yout cannot partially specialize function templates. In your case, you could just define a second overload template with only two template parameters.
template<typename E, typename N, typename P>
void Node::connectEvent( const bool( N::*fn )( const P& ), N *inst ) { ...}
template<typename E, typename N>
void Node::connectEvent( const bool( N::*fn )(), N *inst ) { ...}
Specialising function template is usually not a good idea anyway. The work-around would be to define a template function and dispatch internally to a template class which can be partially specialized:
template<typename E, typename N, typename P> struct C
{
static void connectEvent(const bool( N::*fn )( const P& ), N *inst)
{
size_t eventId = typeid( E ).hash_code();
mCallbacks[eventId] = std::bind( fn, inst );
}
};
template<typename E, typename N> struct C<E,N,void> {
... specialize ...
};
template<typename E, typename N, typename P>
void Node::connectEvent( const bool( N::*fn )( const P& ), N *inst )
{
C<E,N,P>::connectEvent(fn, inst);
}
template<typename E, typename N>
void Node::connectEvent( const bool( N::*fn )(), N *inst )
{
C<E,N,void>::connectEvent(fn, inst);
}
Upvotes: 2