-1

私は、opengl コンテキストにレンダリングされたスクラッチビルド コントロールを特徴とするゲーム プロジェクトに取り組んでいます。ボタン、スクロールバー、リストボックスなどです。これらのコントロールの多くはネストされています。たとえば、私のリストボックスにはスクロールバーがあり、スクロールバーには 3 つのボタンがあります。

スクロールバーの値が変更されたら、変更に応答する「何らかの」関数 (通常は親オブジェクト内) を呼び出したいと思います。たとえば、リストボックスにスライダーがある場合、スライダーをインスタンス化してから、新しいスライダーにリストボックスの「onScroll(float)」関数を呼び出すように指示する必要があります。すべてのコントロールは共通の基本クラスを共有しているため、「base* 親」の親ポインターを使用して、「parent->onScroll(val)」を実行できます。ただし問題は、親がベースから継承しない場合にどうなるかです。実行する仮想 onScroll() がないため、トップレベルの親は、子コントロールの値が変更されたかどうかを定期的に確認する必要があります。これは、子さえ持たない可能性があるため、他のコントロールも混乱させます。

より良い解決策は、親によって設定され、必要に応じて子によって呼び出される汎用関数ポインタ (コールバックなど) を子オブジェクトに保持させることです。このようなもの:

typedef (*ptFuncF)(float);

class glBase {
public:
  //position,isVisible,virtual mouseDown(x,y),etc
};

class glDerivedChild : public glBase {
public:
  glDerivedChild();
  ~glDerivedChild();

  void changeValue(float fIn) {
    Value = fIn; //ignore these forward declaration errors
    (*callBack)(fIn);
  }

  void setCallBack(ptFuncF pIn) {callBack = pIn;}

  ptFuncF callBack;
  float Value;
};

class glDerivedParent : public glBase {
public:
  glDerivedParent() {
    child = new glDerivedChild();
    child->setCallBack(&onScroll);
  }
  ~glDerivedParent() {delete child;}

  void onScroll(float fIn) {
    //do something
  }

  glDerivedChild* child;
};

class someFoo {
public:
  someFoo() {
    child->setCallBack(&setValue);
  }

  void setValue(float fIn) {
    //do something else
  }

  glDerivedChild child;
};

私は関数ポインタに慣れていないので、(明らかに)多くのことを間違っていることを知っています。「typedef (glBase::*ptFuncF)(float);」のようなものが含まれているのではないかと思います。「onScroll(f)」はオーバーライドされた仮想関数であり、おそらく「virtual void childCallBack(float)」のような一般名が付いています。ソリューションをできるだけバニラに近づけたいので、boost のような外部ライブラリは避けたいと思います。私は 8 時間のほとんどの間、これについて頭を悩ませてきました。誰かが助けてくれることを願っています。ありがとう!

4

4 に答える 4

1

クラスのメンバー関数への関数ポインターには、次のような型があります。

<return type> (<class name>::*)(<arguments>)

例えば:

typedef void (glBase::*ptFuncF)(float);
        ^^^^
      by the way, you have forgot the `void` in your `typedef`

ptFuncF func = &glDerivedChild::onScroll;

そして、あなたはそれを次のように使います:

glDerivedChild c;
(c.*func)(1.2);

特定の例では、関数は派生クラス自体のメンバーであるため、次のように呼び出す必要があります。

(c.*c.callback)(1.2);

内側c.callbackは関数ポインタです。残りは上記とまったく同じです。つまり、次のとおりです。

(class_instance.*function_pointer)(arguments);

この質問もご覧になることをお勧めします。

于 2012-07-08T16:32:30.517 に答える
1

あなたが望むのは、ある種のイベントまたはシグナルメカニズムだと思います。

たとえば、Windowsでイベント処理がどのように構成されているかを調べることができます。つまり、スクロールバーはシステムで新しいイベントを生成し、システムはそれをシステムに登録されているすべての要素に伝播します。

より便利なメカニズムは、シグナル/スロットメカニズムです。BoostまたはQtはそのようなツールを提供します。このソリューションをお勧めします

ただし、コールバックのみを使用したい場合は、生の関数ポインターの代わりにstd::function ( boost::function ) (必要に応じてstd::bind ( boost::bind )と組み合わせて)を使用することをお勧めします。

于 2012-07-08T11:14:14.430 に答える
1

使用しますboost::function(またはstd::function利用可能な場合)。このように(あなたの表記法を使用して):

typedef std::function<void (float)> ptFuncF; 
//...
void setCallBack(const ptFuncF &pIn);
//...
child->setCallBack(std::bind(&glDerivedParent::onScroll, this, _1)); 
//...
child->setCallBack(std::bind(&someFoo::setValue, this, _1)); 
于 2012-07-08T11:14:32.497 に答える
-1

わかりました。私が思いついた回避策には、余分なオーバーヘッドと分岐がありますが、それ以外の点では合理的です。

void*基本的に、各コールバック関数は、呼び出しを行ったオブジェクトへのポインターを含む必要なパラメーターを受け取る仮想メンバー関数として実装されます。各派生オブジェクトには、発行するイベントを受信する必要があるオブジェクトを参照する基本クラスポインタもあります(通常はその親ですが、基本クラスから継承する任意のオブジェクトである可能性があります)。コントロールに複数の子がある場合、コールバック関数はvoid*ポインターを使用してそれらを区別します。次に例を示します。

class glBase {
public:
  virtual onChildCallback(float fIn, void* caller);

  glBase* parent;
};

class glSlider : public glBase {
public:
  glSlider(glBase* parentIn);

  void changeValue(float fIn) {
    Value = fIn;
    parent->onChildCallback(fIn, this);
  }

  float Value;
};

class glButton : public glBase {
public:
  glButton(glBase* parentIn);

  void onClick() {
    parent->onChildCallback(0, this);
  }
};

class glParent : public glBase {
public:
  glParent(glBase* parentIn) : parent(parentIn) {
    childA = new glSlider(this);
    childB = new glButton(this);
  }

  void onChildCallback(float fIn, void* caller) {
    if (caller == childA) {
      //slider specific actions
    } else if (caller == childB) {
      //button specific actions
    } else {
      //generic actions
    }
  }

  glSlider* childA;
  glButton* childB;
};

かなり少量のオーバーヘッドに加えて、スキームは十分に柔軟性があるため、派生クラスは特定のコンポーネントを無視したり、それらを完全に省略したりできます。後で関数ポインターのアイデアに戻るかもしれませんが(shahbazに感謝)、インフラストラクチャの半分はどちらのスキームでも同じであり、特にコントロールの数と種類がかなり少ないため、余分なオーバーヘッドは最小限に抑えられます。コールバック関数にネストされた応答を使用させることは、子オブジェクトごとに個別の関数(onUpButton、onDownButtonなど)を必要としないため、実際には少し優れています。

于 2012-07-08T19:02:20.677 に答える