2

関数の存在を確認するためのテンプレートを作成することは可能ですか? それらを適用しますが、問題が発生しています。私は以下を定義しています:

#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つの質問があります:

  1. イベントハンドラーを公開せずに、私がやろうとしていることを行うことは可能ですか?
  2. 基本クラスが既に登録している場合、イベント ハンドラの再登録を回避することも可能ですか? 別の言い方をすれば、関数が基本クラスで宣言されたのか、それとも派生クラスで発生したのかを知る方法はありますか?

関連する場合は、Visual Studio 2013 Preview コンパイラでこれを実行しようとしています。この質問の動機は、私が C++11 の機能と Windows API ラッパーを試しており、メッセージを適切なハンドラーにルーティングするための大きな vtable とメッセージ マップ マクロよりも優れたソリューションがあるかどうかを確認しようとしていることです。

4

1 に答える 1

1

ここでの障害は次のとおりです。

SubWndから派生したクラスは、MinWnd友情を宣言します。

friend class RegisterHandlers<SubWnd>;

そのためRegisterHandlers<SubWnd>::registerHandlers(this)、独自の保護されたハンドラーの登録と、それが継承するonPaint保護されたハンドラーの重複した登録を行うことができます。基本クラスのコンストラクターは既に登録されているため、後者の登録は冗長です。 任意のウィンドウが持つ可能性のあるすべてのハンドラーを常に「登録」し、 実際に指定されたハンドラーがない場合はノーオペレーションを実行し、基本クラスのコンストラクターによって既に行われている場合は登録を繰り返します。onCreateMainWndMainWndonCreateRegisterHandlers<T>::registerHandlers(WinType *)WinType

しかしonCreate、 のプロテクト メンバーである を重複して登録するかどうかに関係なく、のフレンドでMainWndあるRegisterHandlers<SubWnd>必要がありMainWndます。そうではありません: それは の単なる友達ですSubWnd

SFINAE メソッドの内省のためにそのテンプレート パラメータに依存しているためRegisterHandlers<T> 、ウィンドウ タイプによるテンプレート パラメータ化を取り除くことはできません。T

しかし、この SFINAE メソッドのイントロスペクティングは、現在のケースでは役に立たないと言わざるを得ません。すべてのウィンドウ クラス コンストラクターは、独自のハンドラーを登録するために何かを行う必要があります。これを行う方法は、タスクを に委任することです。これにより、次のことが行われRegisterHandlers<T>::registerHandlers(WinType *)ます。

  • ウィンドウが持つ可能性のあるすべてのハンドラーを登録しようとすると、
  • WinTypeハンドラーがない場合は常に何もしません。
  • WinTypeの基本クラス コンストラクターが既にハンドラーを登録している場合に、ハンドラーを冗長に再登録します。
  • の特定のハンドラを冗長ではなく正常に登録しますWinType

このすべての中で、各ウィンドウ タイプが実際に、しかしやむを得ず行う必要があるのは、最後の部分だけです。だからあなたの2つの質問に:

  1. イベントハンドラーを公開せずに、私がやろうとしていることを行うことは可能ですか?

はい。各ウィンドウタイプに独自のハンドラーを登録するだけです。

  1. 基本クラスが既に登録している場合、イベント ハンドラの再登録を回避することも可能ですか?

はい。同じ答え。

あなたが単に実験しているだけだとは思いますが、この実験は有望ではありません.

質問 2 に 3 番目の質問を追加しました。

関数が基本クラスで宣言されたのか、それとも派生クラスで発生したのかを知る方法はありますか

はい。この回答を再度参照してから、すぐ下の同じ質問に対する私の回答も参照すると、最初のソリューションは で定義されているT::mf ときに メンバー関数を検出しますが、によって継承されたときは検出しませんが、私のソリューションはどちらの場合でも検出します。したがって、両方の方法でSFINAE プローブを行うことができ、最初の方法で検出されず、2 番目の方法で検出された場合に継承されていることがわかります。mfTmfTmfT::mfmf

于 2013-07-20T10:48:11.620 に答える