3

だから私は自分の便宜のために単純な win32 ラッパーに取り組んでおり、少し複雑な問題に遭遇しました。

これには他にもたくさんのメンバーがいますが、少し省略して問題のあるメンバーだけを残しています。

class Windows::AbstractWindow
{
public:
     void InstallHandler(UINT msgName, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM));

private:
     std::map<UINT, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

};

(記録として、この場合の Windows は、私が作成したさまざまなクラスとオブジェクトの名前空間です)

少し厄介ですが、私のプロセスと推論を説明してください。ウィンドウのすべての機能のほとんどを非常にオブジェクト指向の方法で含む AbstractWindow というクラスを作成しました。

私は現在、プライベート メンバー関数を取得し、それらへのポインターを介してマップに格納する方法に取り組んでいます。これは、処理するはずの Windows メッセージによって識別されます。このようにして、メッセージが Windows プロシージャによって受信されると、このマップを掘り下げて、そのハンドラがインストールされているかどうかを確認します。ある場合は、その関数を呼び出して終了します。そうでない場合は、DefWindowProc を呼び出して終了します。簡単です。

ただし、このオブジェクトはインスタンス化されることは想定されておらず、単に継承および拡張されることが想定されています。問題は、マップの関数ポインター宣言が AbstractWindow 型であるため、AbstractWindow から継承された型のメンバー関数ポインターを格納できないことです。例えば、

class BasicWindow : public Windows::AbstractWindow
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
}

...エラーが発生します:

error C2664: 'Windows::AbstractWindow::InstallHandler' : cannot convert parameter 2 from 'void (__thiscall BasicWindow::* )(HWND,MSG,WPARAM,LPARAM)' to 'void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)'

基本クラスから継承されているにもかかわらず、ポインターの型が同じではないためです。では、この方法を維持しながら解決策を提案したい人はいますか? そうでない場合は、この方法よりもメッセージ処理が便利になると思われる提案も受け付けています。

4

3 に答える 3

3

あなたが直面している問題は、逆の方法で変換しようとしていることです。関数ポインターは、その引数において反変ですthis(つまり、基本クラスの関数への関数ポインターは、派生クラスのメソッドへの関数ポインターとして機能しますが、その逆は成り立ちません)。あなたはできる:

  • キャストするだけです(static_cast暗黙の変換の逆であるため、 を使用)。不適切なクラス (例: ) でメソッドを決して呼び出さないことを保証できる限り、問題なく動作しますNotABasicWindow().*method()
  • スキームを取り除き、一般的な関数 (つまり、メンバー関数ポインターの代わりに呼び出すことができるもの) を登録します。たとえば、次のように使用します。std::function<void(HWND, UINT, WPARAM, LPARAM)>ハンドラーの型として、ウィンドウを認識するハンドラー (ラムダ関数など) を登録します。

    • ラムダ関数は C++11 の機能です。これらは、バインダーを持つ関数オブジェクトにほぼ対応しています。それらは、それらが存在するスコープから変数を参照できる無名関数を作成します。例は

      [=](HWND wnd, UINT i, WPARAM wp, LPARAM lp) { this->printHandler(wnd, i, wp, lp); }
      

      これは を覚えているthisので、呼び出さprintHandlerれたときに現在のオブジェクト (コードを呼び出すのではなく、ラムダを作成するコードの現在) を呼び出します。もちろん、メソッドが呼び出されるオブジェクトは、単なる別のパラメーターである可能性があります。

      ラムダやその他の関数オブジェクト (つまり、operator()定義済みのオブジェクト) は、オブジェクトに変換して格納することができstd::functionます。

于 2012-04-05T00:43:05.603 に答える
3

Curiously Recurring Template Pattern ( http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern ) を読んでください。

基本的に、サブクラスを指定するテンプレート パラメーターを使用して、基本クラスをテンプレートに変換します。

おそらく、次のように表示されると理解しやすいでしょう。

namespace Windows
{
     template <typename T>
     class AbstractWindow
     {
     public:
          void InstallHandler(UINT msgName, void (T::*)(HWND, UINT, WPARAM, LPARAM));

     private:
          std::map<UINT, void (T::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

     };
}

class BasicWindow : public Windows::AbstractWindow< BasicWindow >
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &BasicWindow::Create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
};
于 2012-04-05T15:29:33.117 に答える
0

この性質の何かが機能しますか...?Ps。関数シグネチャに typedef を使用していました。

BasicWindow() 
    {
         InstallHandler(WM_CREATE, reinterpret_cast<void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)>(&create));
    }
于 2012-04-05T00:41:47.790 に答える