2016-03-20 67 views
0

我有兩個結構,struct B從struct A繼承。我想將一個函數作爲參數從struct B傳遞給struct A以供struct 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); 
    }; 
}; 

的問題是這一行:Tick(0.1f, true, &B::RunThis);從功能void DoSomething()除非我做錯了從一開始,但我想我傳遞錯誤的,因爲它仍然是在頭部內我目前定義的結構是什麼?

錯誤(我已經修改,以適應我的例子錯誤,我不認爲我搞砸了..):

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()成爲基類的一個函數,然後調用一個虛函數,它可以在派生類中重寫。與使用'std:function''相比,這仍然是一個更好的方法,只要您具有「派生類」的1:1關係:「您想要執行的函數」。 – BitTickler

+0

@BitTickler - 對我的目的來說,這是一個更好的解決方案,謝謝!至於具體問題的答案,我仍然會等待它發佈,以便那些谷歌類似問題的人有一個準確的答案 – Vii

回答

1

第一個選項:改爲使用虛擬功能。

如果每個派生類有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(/*...*/); 
    } 

}; 

第二個選項:標準::功能+在結構乙拉姆達或成員函數

#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

或者一個lambda和一個方法,而不是'std :: bind'與'[this](float delta,bool doit,int32_t which){return this-> RunThis(delta,doit,which); }' – 6502

+0

@ 6502是的,唯一不起作用的是嘗試將lambda傳遞給A :: tick()而不使用std :: function,因爲那樣,類型將是未知的...我仍然不會t考慮一件好事:) – 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}; 

,然後你需要「分配」封閉i,把你的上下文數據在相應的closure_data[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)()'')。但是爲了在這種情況下做到這一點,struct A必須知道struct B,這顯然是不可取的。 – BitTickler

+0

@BitTickler:指向C++成員的指針不帶任何上下文,並且需要應用一個對象。如果一個API調用不幸地接受一個函數指針,那麼你不能傳遞一個指向成員的指針......它們是不同的抽象(當然這個例外是'static'成員函數......這些實際上只是一個帶有時髦名字的常規函數​​)。 – 6502