2016-03-20 6 views
0

構造体Bは2つの構造体を持ちます。構造体Bは構造体Aから継承します。構造体Bから構造体Aのパラメータとして関数を渡します。C++継承された構造体の関数のパラメータとしてstruct関数を渡します。

これは達成したい問題の例で、私が実行している問題は、完全なコードはTL; DRです。

struct A 
{ 
    int32 mynum; 

    void Tick(float delta, bool doThis, void (funcparam)(float, bool, int32)) 
    { 
     funcparam(delta, doThis, mynum); 
    } 
}; 

struct B : public A 
{ 
    void RunThis(float deltaTime, bool doIt, int32 which) 
    { 
     // do something when called by Struct A 
    }; 

    void DoSomething() 
    { 
     Tick(0.1f, true, &B::RunThis); 
    }; 
}; 

問題は、この行である:私は最初から間違ったことをやったが、それは、ヘッダ内まだですので、私はそれが間違って渡しています想像していない限り機能void DoSomething()からTick(0.1f, true, &B::RunThis);私が現在定義している構造体の?

エラー(私は私の例に合わせて、エラーを修正している、私は私が台無しとは思わない。):

error C2664: 'void A::Tick(float,bool,void (__cdecl *)(float,bool))': cannot convert argument 3 from 'void (__cdecl B::*)(float,bool)' to 'void (__cdecl *)(float,bool)'

何も解決しませんもちろんの&B::RunThisからB::を省略します。

+0

'std :: function()'を見て、あなたの関数ポインタ 'void(funcparam)(float、bool、int32)'はメンバ関数ポインタを取ることができません。 –

+1

'' void(funcparam)(float、bool、int32) ''はあなたが望むものではありません。まず、(メンバーではない)関数ポインタのシグネチャは 'void(* funcparam)(float、bool、int32)' 'です(*が足りません)。次に、Tick()を基本クラスの関数にすることができます。この関数は派生クラスでオーバーライドする仮想関数を呼び出します。これは、 "派生クラス"の1:1の関係を持つ限り、 "std:function''を使用するよりも優れたアプローチです:"実行したい関数 "。 – BitTickler

+0

@BitTickler - それは私の目的のためのより良い解決策でした、ありがとう!具体的な質問に対する回答については、私はそれが似たような問題をGoogleの人々が正確な答えを持つように投稿されるのを待つつもりです – Vii

答えて

1

最初のオプション:代わりに仮想関数を使用します。

とにかく派生クラスごとに1つの関数しかない場合に最適です。

struct A { 
    virtual void RunThis(float deltaTime, bool doIt, int32 which) = 0; 
    void Tick(float delta, bool doThis) 
    { 
     //... 
     RunThis(delta, doThis, which); 
     // ... 
    } 
    virtual ~A() // virtual destructor... 
    {} 
}; 

struct B : public A 
{ 
    virtual void RunThis(float deltaTime, bool doIt, int32 which) 
    { 
    } 
    void DoSomething(/*...*/) 
    { 
     // ... 
     Tick(/*...*/); 
    } 

}; 

番目のオプション:STD ::機能+構造体Bにおけるラムダまたはメンバ関数

#include <functional> 
struct A 
{ 
    void Tick(float delta, bool doit, std::function<void(float,bool,int32)> action) 
    { 
    } 
}; 

struct B : public struct A 
{ 
    void DoSomething(/*...*/) 
    { 
     // you can use a lambda, if convenient... 
     Tick(/*...*/, 
      [this](float delta, bool doit, int32_t which) -> void { 
       // ... 
      }); 
    } 

    // or you can also use a pointer to member: 
    void RunThis(/*...*/) { /* ... */ } 
    void DoSomething(/*...*/) 
    { 
     std::function<void(float,bool,int32)> f = std::bind(&B::RunThis, this, _1,_2,_3); 
     Tick(/*...*/, f); 
    } 
}; 
+0

またはラムダと、 '[this](float delta、bool doit、int32_t){return this-> RunThis(delta、doit、which)}で' std :: bind'ではなくラムダとメソッド。 } ' – 6502

+0

@ 6502はい、唯一の問題は、ラムダをA :: tick()にstd ::を使わずに渡すことです。そのときの型は不明です...私はまだ'良いことを考えてみてください:) – BitTickler

0

残念ながら、C++の関数ポインタは、他の言葉で...整数を受け入れる関数へのポインタをコンテキストを持っていません

void (*f)(int x) 

は、ポインタに「バインドされた」データを持たせることはできず、パラメータとグローバル変数にしかアクセスできません。 一部のコンパイラで回避策を実装すると、カスタムデータを関数ポインタにバインドすることができる"trampoline" librariesが実行時に動的コード生成に依存し、標準C++の境界内では不可能なものがあります。

標準C++における溶液を

std::function<void(int)> 

代わりに

void (*)(int) 

を使用することであるstd::functionオブジェクトは、機能に関連するデータを保持し、クロージャの作成を可能にすることができます。また、関数ポインタと下位互換性があります(つまり、関数ポインタを暗黙的にstd::functionオブジェクトに変換できます)。

あなたのコードは、ラムダがメソッドを呼び出すために使用されている例

struct A { 
    int32 mynum; 
    void Tick(float delta, bool doThis, 
       std::function<void(float, bool, int32)> funcparam) { 
     funcparam(delta, doThis, mynum); 
    } 
}; 

struct B : public A { 
    void RunThis(float deltaTime, bool doIt, int32 which) { 
     // do something when called by Struct A 
    }; 
    void DoSomething() { 
     Tick(0.1f, true, [this](float dt, bool di, int32 w) { 
      this->RunThis(dt, di, w); 
     }); 
    } 
}; 

のためになることを使用。

関数ポインタを受け入れるコードを変更できない場合、移植可能な回避策は、与えられたシグネチャに対して関数ポインタの事前割り当てされたセットを持つことです...例

void *closure_data[10]; 
void (*closure_code[10])(void *, int); 

void closure0(int x){ closure_code[0](closure_data[0], x); } 
void closure1(int x){ closure_code[1](closure_data[1], x); } 
void closure2(int x){ closure_code[2](closure_data[2], x); } 
... 
void closure9(int x){ closure_code[9](closure_data[9], x); } 

void (*closures[10])(int) = {closure0, 
          closure1, 
          ... 
          closure9}; 

とするために、あなたは、対応するclosure_data[i]であなたのコンテキストデータを入れて、閉鎖i「を割り当てる」closure_code[i]のコードと関数ポインタclosures[i]として渡す必要があります。

通常、まともなC++ 03のコードまたはCコードのコールバック関数ポインタを受け入れることは、常にコンテキストパラメータを提供

...インターフェイスは、フォーム

void Tick(void *context, 
      void (*callback)(float, bool, int32, void *)) 

を持っており、不透明なvoid *contextを渡しても、コールバックを呼び出します。すなわち関数ポインタを提供した呼び出し側によって指定された

+0

C++では、メンバへのポインタ( '' void(Foo :: * funcptr)() '')があります。しかし、この場合には、構造体Aは構造体Bを知る必要がありますが、これは明らかに望ましくありません。 – BitTickler

+0

@BitTickler:C++のメンバーへのポインタはコンテキストを持ちませんし、オブジェクトを適用する必要があります。 API呼び出しが関数ポインタを受け入れると、残念なことにあなたはメンバへのポインタを渡すことができません。それらは異なる抽象です(例外はもちろん '静的'メンバ関数です...これらは実際にはファンキーな名前の正規関数です)。 – 6502

関連する問題