Reputation: 1145
I am trying to take the ideas from Is it possible to write a template to check for a function's existence? and apply them, but I am running into some trouble. I have the following defined:
#define HAS_MEM_FUNC(func, name) \
template<typename T, typename Sign> \
struct name {\
typedef char yes[1]; \
typedef char no[2]; \
template <typename U, U> struct type_check; \
template <typename _1> static yes &chk(type_check<Sign, &_1::func> *); \
template <typename > static no &chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
// Creates a member function detector and two overloads of a message handler
// registration function, one that registers a member function of a specific name
// if it exists and another that does nothing if that member function doesn't exist.
#define DEFINE_REGISTER_FUNC(msgid, msgtype, handlername) \
HAS_MEM_FUNC(handlername, has_##handlername);\
template <typename T>\
static void register_##handlername(\
T* pWnd, \
typename std::enable_if<has_##handlername<T, void(T::*)(msgtype&)>::value, T>::type* t = nullptr) \
{\
(void) t;\
pWnd->setMessageHandler(\
msgid,\
Handler<msgtype>(std::bind(&T::handlername, pWnd, std::placeholders::_1)));\
}\
\
template <typename T> \
static void register_##handlername(\
T* pWnd, \
typename std::enable_if<!has_##handlername<T, void(T::*)(msgtype&)>::value, T>::type* t = nullptr) \
{\
(void)pWnd;\
(void)t;\
}
I also have a class defined like this:
template <typename T>
class RegisterHandlers
{
public:
template <typename T>
static void registerHandlers(T* pWnd)
{
register_onCreate(pWnd);
register_onPaint(pWnd);
}
private:
DEFINE_REGISTER_FUNC(WM_CREATE, CreateMessage, onCreate);
DEFINE_REGISTER_FUNC(WM_PAINT, PaintMessage, onPaint);
};
The idea is that if the class T
has a member function onCreate(CreateMessage&)
, it will automatically be registered as the handler for WM_CREATE
. If it has no such method, then the do-nothing overload of register_onCreate
will be called and the compiler will be happy. Same for the onPaint handler (I included that mostly to illustrate that the SFINAE does work).
This works fine if I have a class like this:
class MainWnd
{
public:
friend class RegisterHandlers<MainWnd>;
MainWnd()
{
RegisterHandlers<MainWnd>::registerHandlers(this);
}
protected:
void onCreate(CreateMessage&) { /* do some stuff */ }
};
However, the moment I add this:
class SubWnd : public MainWnd
{
public:
friend class RegisterHandlers<SubWnd>;
SubWnd()
{
RegisterHandlers<SubWnd>::registerHandlers(this);
}
protected:
void onPaint(PaintMessage&) { /* do some stuff */ }
};
I get an error that MainWnd::onCreate
is inaccessible (cannot access protected member). I broke down the macros to help find where the problem is actually occurring, and it seems to be in the HAS_MEM_FUNC
macro, probably at this line:
template <typename _1> static yes &chk(type_check<Sign, &_1::func> *);
So, I have two questions really:
In case it's relevant, I am trying to do this on the Visual Studio 2013 Preview compiler. The motivation for this question is that I am experimenting with C++11 features and Windows API wrappers, and I am trying to see if there are better solutions than large vtables and message map macros for routing messages to an appropriate handler.
Upvotes: 2
Views: 420
Reputation: 61575
The obstacle here is this:
Class SubWnd
, derived from MinWnd
, declares friendship:
friend class RegisterHandlers<SubWnd>;
so that RegisterHandlers<SubWnd>::registerHandlers(this)
can do the registering of its
own protected handler onPaint
and also - redundantly - of the
protected handler onCreate
that it inherits from MainWnd
. The
latter registration is redundant because the base
class MainWnd
's constructor will already have registered onCreate
.
RegisterHandlers<T>::registerHandlers(WinType *)
is always going to "register"
all the handlers that any window might have, doing a no-op if WinType
doesn't actually have a given handler and repeating the registration if it
has already been done by a base class constructor.
But to register onCreate
, redundantly or not, which is a protected member
of MainWnd
, RegisterHandlers<SubWnd>
needs to be a friend of MainWnd
.
And it isn't: it is only a friend of SubWnd
.
You cannnot get rid of the template parameterization of RegisterHandlers<T>
by window-type T
because you depend on that template parameter for your
SFINAE method-introspecting
But this SFINAE method-introspecting, I have to say,
is a futile palaver in the present case. Every one of your window-class constructors
still has to do something to get its own handlers registered. The way that you want it
to go about this is by delegating the task to RegisterHandlers<T>::registerHandlers(WinType *)
, which will:
WinType
doesn't have the handler,WinType
's base class constructor has already registered it, andWinType
.The only part of all this that each window type actually, but unavoidably, needs to do is the last. So to your two questions:
- Is it possible to do what I am trying to do without making the event handlers public?
Yes. Just have each window-type register its own handlers.
- Is it also possible to avoid re-registering event handlers if the base class already did it?
Yes. Same answer.
I appreciate that you are merely experimenting, but this experiment is not promising.
You attached a third question to question 2:
Is there a way to tell if the function was declared in the base class or if it originated in the derived class
Yes. If you consult again this answer,
and then also consult my answer to the same question
just below, you will see that the first solution detects a member function T::mf
when mf
is defined in T
but not when mf
is inherited by T
, whereas my
solution detects mf
in either case. Thus you can SFINAE-probe for T::mf
by
both methods and will know that mf
is inherited if the first method does not
detect it and the second method does.
Upvotes: 1