3

基本的な GLUT プログラムを考えてみてください。それらは単にメイン メソッドから実行され、`glutMouseFunc(MouseButton) (MouseButton はメソッドの名前) のようなコールバックを含みます。

私が行ったことは、メイン ファイルをクラスにカプセル化したため、MouseButton は静的関数ではなく、インスタンスを持つようになりました。しかし、これを行うと、コンパイルエラーが発生します:

エラー 2 エラー C3867: 'StartHand::MouseButton': 関数呼び出しに引数リストがありません。「&StartHand::MouseButton」を使用して、メンバー c:\users\angeleyes\documents\visual studio 2008\projects\capstone ver 4\starthand.cpp へのポインターを作成します 388 IK エンジン

クラスが非常に大きいため、コード サンプルを提供することはできません。

使用してみthis->MouseButtonましたが、同じエラーが発生します。コールバックにインスタンス関数へのポインタを与えることはできませんか?

4

6 に答える 6

5

エラー メッセージにあるように、&StartHand::MouseButton構文を使用してメンバ関数 (ptmf) へのポインタを取得する必要があります。これは単に言語の一部として義務付けられています。

ptmf を使用する場合、呼び出す関数 (この場合は glutMouseFunc) もコールバックとして ptmf を取得する必要があります。そうしないと、非静的 MouseButton を使用しても機能しません。代わりに、一般的な手法は、コールバックがユーザー指定のvoid*コンテキスト (インスタンス ポインターの場合もあります) で動作することですが、コールバックを実行するライブラリでは、このパラメーターを明示的に許可する必要があります。また、外部ライブラリ (以下のhandle_mouse関数) が期待する ABI と一致することを確認することも重要です。

glutはユーザー提供のコンテキストを許可しないため、別のメカニズムを使用する必要があります: オブジェクトを glut の現在のウィンドウに関連付けます。ただし、「現在のウィンドウ」を取得する方法は提供されており、これを使用して avoid*をウィンドウに関連付けました。次に、トランポリンを作成して型変換を行い、メソッドを呼び出すだけです。

機械:

#include <map>

int glutGetWindow() { return 0; } // make this example compile and run  ##E##

typedef std::pair<void*, void (*)(void*,int,int,int,int)> MouseCallback;
typedef std::map<int, MouseCallback> MouseCallbacks;
MouseCallbacks mouse_callbacks;
extern "C" void handle_mouse(int button, int state, int x, int y) {
  MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
  if (i != mouse_callbacks.end()) { // should always be true, but possibly not
                                    // if deregistering and events arrive
    i->second.second(i->second.first, button, state, x, y);
  }
}

void set_mousefunc(
  MouseCallback::first_type obj,
  MouseCallback::second_type f
) {
  assert(obj); // preconditions
  assert(f);
  mouse_callbacks[glutGetWindow()] = MouseCallback(obj, f);
  //glutMouseFunc(handle_mouse); // uncomment in non-example  ##E##
  handle_mouse(0, 0, 0, 0); // pretend it's triggered immediately  ##E##
}

void unset_mousefunc() {
  MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
  if (i != mouse_callbacks.end()) {
    mouse_callbacks.erase(i);
    //glutMouseFunc(0); // uncomment in non-example  ##E##
  }
}

例:

#include <iostream>

struct Example {
  void MouseButton(int button, int state, int x, int y) {
    std::cout << "callback\n";
  }
  static void MouseButtonCallback(
    void* self, int button, int state, int x, int y
  ) {
    static_cast<Example*>(self)->MouseButton(button, state, x, y);
  }
};

int main() {
  Example obj;
  set_mousefunc(&obj, &Example::MouseButtonCallback);

  return 0;
}

もうglutMouseFuncを直接呼び出さないことに注意してください。[un]set_mousefuncの一部として管理されます。


明確でない場合に備えて、この回答を書き直して、あなたのために機能し、C/C++ リンケージの問題が議論されないようにします。そのままコンパイルして (glut なしで) 実行し、少し変更するだけで glut で動作するはず##E##です。

于 2010-01-14T21:02:37.100 に答える
2

いいえ、特定のシグネチャの関数ポインタを期待するコールバック関数にインスタンス関数へのポインタを与えることはできません。彼らの署名は異なります。コンパイルされません。

通常、このような API では、「コンテキスト」パラメーターとして void* を渡すことができます。そこにオブジェクトを渡し、コンテキストをコールバックとして受け取るラッパー関数を記述します。ラッパーは、それを使用していたクラスにキャストし直し、適切なメンバー関数を呼び出します。

于 2010-01-14T21:20:24.977 に答える
1

静的コールバックをインスタンス コールバックに置き換えることはできません。発信者がコールバックを呼び出すとき、どのインスタンスで呼び出すのですか? つまり、呼び出し元は正式な 'this' 引数をどのように渡すのでしょうか?

解決策は、静的なコールバック スタブを用意し、インスタンスを引数として渡すことです。これは、呼び出し先が、コールバックを呼び出すときに返される任意の pvoid を受け入れる必要があることを意味します。スタブでは、非静的メソッドを呼び出すことができます。

class C {
    void f() {...}
    static void F(void* p) {
      C* pC = (C*)p;
      pC->f();
    }
  }

  C* pC = ...;
  someComponent.setCallback(&C::F, pC);
于 2010-01-14T21:04:52.463 に答える
1

誰もが言っているように思われることに反して、非静的メンバー関数をコールバック メソッドとして使用できることは間違いありません。非静的メンバーへのポインターを取得するために特別に設計された特別な構文と、クラスの特定のインスタンスでその関数を呼び出すための特別な構文が必要です。必要な構文の説明については、こちらを参照してください。

これがどのように機能するかを示すサンプル コードを次に示します。

#include <cstdlib>
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;


class Operational
{
public:
    Operational(int value) : value_(value) {};

    string FormatValue() const ;

private:
    int value_;

};

string Operational::FormatValue() const
{
    stringstream ss;
    ss << "My value is " << value_;
    return ss.str();
}

typedef string(Operational::*FormatFn)() const; // note the funky syntax

Operational make_oper(int val)
{
    return Operational(val);
}

int main()
{
    // build the list of objects with the instance callbacks we want to call
    Operational ops[] = {1, 2, 3, 5, 8, 13};
    size_t numOps = sizeof(ops)/sizeof(ops[0]);

    // now call the instance callbacks
    for( size_t i = 0; i < numOps; ++i )
    {
        // get the function pointer
        FormatFn fn = &Operational::FormatValue;    

        // get a pointer to the instance
        Operational* op = &ops[i];  

        // call the callback on the instance
        string retval = (op->*fn)();

        // display the output
        cout << "The object @ " << hex << (void*)op << " said: '" << retval << "'" << endl;
    }


    return 0;
}

このプログラムを自分のマシンで実行したときの出力は次のとおりです。

The object @ 0017F938 said: 'My value is 1' 
The object @ 0017F93C said: 'My value is 2' 
The object @ 0017F940 said: 'My value is 3' 
The object @ 0017F944 said: 'My value is 5' 
The object @ 0017F948 said: 'My value is 8' 
The object @ 0017F94C said: 'My value is 13'
于 2010-01-14T21:54:04.503 に答える
0

この場合、非静的メンバー関数は使用できません。基本的に、glutMouseFunc が期待する引数の型は

void (*)(int, int, int, int)

非静的メンバー関数の型は

void (StartHand::*)(int, int, int, int)

最初の問題は、タイプが実際には一致しないことです。第二に、そのメソッドを呼び出すことができるようにするために、コールバックは、メソッドが属するオブジェクト (つまり、「この」ポインター) を認識している必要があります (これが、そもそも型が異なる理由のほとんどです)。そして 3 番目に、メソッドのポインターを取得するために間違った構文を使用していると思います。正しい構文は、&StartHand::MouseButton です。

そのため、そのメソッドを静的にするか、MouseButton の呼び出しに使用する StartHand ポインターを認識できる他の静的メソッドを使用する必要があります。

于 2010-01-14T21:26:11.907 に答える
0

以下は c++ で ac コールバック関数を定義するために機能します。たとえば、このクラスのインスタンスが 1 つだけ必要なときに glut (glutDisplayFunc、glutKeyboardFunc、glutMouseFunc ...) を使用する場合に役立ちます。

MyClass * ptr_global_instance = NULL;

extern "C" void mouse_buttons_callback(int button, int state, int x, int y) {

    // c function call which calls your c++ class method

    ptr_global_instance->mouse_buttons_cb(button, state, x, y);
}

void MyClass::mouse_buttons_cb(int button, int state, int x, int y) {

    // this is actual body of callback - ie.  if (button == GLUT_LEFT_BUTTON) ...
    // implemented as a c++ method
}

void MyClass::setup_glut(int argc, char** argv) { // largely boilerplate glut setup

    glutInit(&argc, argv);

    // ... the usual suspects go here like glutInitWindowSize(900, 800); ...

    setupMouseButtonCallback(); // <-- custom linkage of c++ to cb

    // ... other glut setup calls here
}

void MyClass::setupMouseButtonCallback() {

    // c++ method which registers c function callback

    ::ptr_global_instance = this;
    ::glutMouseFunc(::mouse_buttons_callback);
}

MyClass ヘッダーに以下を追加します。

void mouse_buttons_cb(int button, int state, int x, int y);

void setupMouseButtonCallback();

これは、同じロジック フローを使用して glutDisplayFunc(display) への glut 呼び出しを設定することもできます。

于 2013-07-07T20:39:12.697 に答える