関数の存在を確認するためのテンプレートを作成することは可能ですか? それらを適用しますが、問題が発生しています。私は以下を定義しています:
#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;\
}
次のように定義されたクラスもあります。
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);
};
クラスT
にメンバー関数がある場合onCreate(CreateMessage&)
、それが のハンドラとして自動的に登録されるという考え方ですWM_CREATE
。そのようなメソッドがない場合、何もしないオーバーロードregister_onCreate
が呼び出され、コンパイラは満足します。onPaint ハンドラーについても同じです (主に、SFINAEが機能することを説明するために含めました)。
次のようなクラスがある場合、これは正常に機能します。
class MainWnd
{
public:
friend class RegisterHandlers<MainWnd>;
MainWnd()
{
RegisterHandlers<MainWnd>::registerHandlers(this);
}
protected:
void onCreate(CreateMessage&) { /* do some stuff */ }
};
ただし、これを追加した瞬間:
class SubWnd : public MainWnd
{
public:
friend class RegisterHandlers<SubWnd>;
SubWnd()
{
RegisterHandlers<SubWnd>::registerHandlers(this);
}
protected:
void onPaint(PaintMessage&) { /* do some stuff */ }
};
MainWnd::onCreate
アクセスできないというエラーが表示されます (保護されたメンバーにアクセスできません)。問題が実際に発生している場所を見つけるのに役立つように、マクロを分解しましたHAS_MEM_FUNC
。おそらく、次の行で、マクロにあるようです。
template <typename _1> static yes &chk(type_check<Sign, &_1::func> *);
だから、私は本当に2つの質問があります:
- イベントハンドラーを公開せずに、私がやろうとしていることを行うことは可能ですか?
- 基本クラスが既に登録している場合、イベント ハンドラの再登録を回避することも可能ですか? 別の言い方をすれば、関数が基本クラスで宣言されたのか、それとも派生クラスで発生したのかを知る方法はありますか?
関連する場合は、Visual Studio 2013 Preview コンパイラでこれを実行しようとしています。この質問の動機は、私が C++11 の機能と Windows API ラッパーを試しており、メッセージを適切なハンドラーにルーティングするための大きな vtable とメッセージ マップ マクロよりも優れたソリューションがあるかどうかを確認しようとしていることです。