2013-06-24 49 views
0

我寫的東西就像SDL一個按鈕,如果我與它進行交互,可以做一些事情。我想將每個按鈕都存儲在一個容器中(類似於列表或Deque),因此我可以調用它們自己的繪圖函數,並且還可以檢查我點擊的是哪一個。所以,我認爲最簡單的方法是從一個泛型類(「Button」)實例化它們,以便它們都具有位置,顏色等,還有一個函數指針,當我實例化它們時可以設置爲自定義函數。類具有函數指針,可以調用多種類型的功能在不同的實例

我的問題是,當我在類中定義的函數指針,我要聲明的參數列表,但可以有,當我需要一個不同的參數列表(如例:

MyButton button() 
button.fooptr=quit; //quit is a void function with no arguments 
button.fooptr(); 
//another case 
button.fooptr=print; //print is a void function which takes an SDL_Surface* as an argument. 
button.fooptr(screen); 

我已經試過來定義函數指針作爲void (*fooptr)(...),這奇妙的作品,如果我的函數(...)作爲參數,但如果我嘗試調用函數fooptr(screen),功能無法看到屏幕變我傳遞給它有沒有什麼解決方案,能夠傳遞的參數對特定功能的定製量,並通過零函數的參數不帶參數?

+0

這聽起來不太安全。如果你做了'fooptr = print,會發生什麼? fooptr();'? –

+0

'MyButton按鈕()'不會做你認爲它的功能。 –

+0

@LightnessRacesinOrbit什麼,你不認爲Brabulla需要一個名爲'button'的函數來返回'MyButton'的實例?也許Brabulla在每行後都忘了'()';) – Yakk

回答

0

通常的C方法是讓void *參數可以爲NULL或可以指向內容所在的結構。被調用函數將其轉換爲適當的結構類型並訪問元素。

對於C++的std ::綁定和std ::功能可以幫助你。 (我認爲)你至少需要一個參數才能開始訪問其餘的部分,並且這個函數可以用它的方式來處理它們。如果你真的想要使用簡單的函數,你可以檢查出正確的va_arg用法。請注意......限於POD參數類型。

+0

最後我使用了這個解決方案。我想我不會使用任何複雜的函數,所以va_list應該足夠了,這也是一個相當簡單的解決方案,它並不需要大量的擴展頭,儘管這可能不是最好的解決方案,感謝您的幫助 – Brabulla

0

按鈕的按下是應包含的基本信息(看看在現有框架/語言的回調/事件)的基礎按鈕的界面應該只支持特定用例的事件。如果您需要某種特定類型的按鈕來調用不同類型的功能,那麼需要對其進行不同的處理。一種選擇是使用std::bind來調整觸發事件的接口(以最簡單的形式沒有參數)和要調用的函數(注入表面)。

struct Button { 
    std::function<void()> callback; 

    void pressed() { callback(); } 
    void register(std::function<void()> const &cb) { 
     callback = cb; 
    } 
}; 
struct ButtonWithSurface : Button { 
... 
    SDLSurface *surface; 
    void register(std::function<void(SDLSurface*)> const &cb) { 
     Button::register(std::bind(cb,surface); 
    } 

從基部Button可以執行的操作的所有用戶按壓按鈕,雖然在ButtonWithSurface情況下將觸發給帶有SDLSurface*參數的函數的調用。儘管如此,用戶不需要知道詳細信息,因爲只有在使用具體類型完成的回調註冊過程中才能處理該細節。

1

你所描述的定義和調用功能也不是很安全的方式。這是因爲奧利在他們的評論中指出,很容易錯誤地調用該函數。如果目標函數需要3個參數,並且最終只傳遞1個參數,則會發生未定義的行爲,並且會發生任何事情。在這種情況下,「任何事情」都會變得糟糕。

如果您事先知道參數,可以使用std::function,std::bind和參數的早期綁定以更安全的方式調用該函數。這將允許您在[應] 知道的位置創建函數對象,需要傳遞哪些參數,綁定參數,然後設置fooptr

下面的示例使用從你的問題的代碼,並將其擴展使用std::bindstd::function調用函數在一個安全統一的方式上。它還包括使用std::reference_wrapper以允許通過引用傳遞參數的示例。

#include <iostream> 
#include <string> 
#include <vector> 
#include <memory> 
#include <functional> 

struct SDLSurface 
{ 
    std::string str; 
}; 

struct Button 
{ 
    std::function<void(void)> fooptr; 
}; 

void quit() 
{ 
    std::cout << "called quit()" << std::endl; 
} 

void print(SDLSurface *surface) 
{ 
    std::cout << "called print(\"" << surface->str << "\")" << std::endl; 
} 

void print_string(const std::string& str) 
{ 
    std::cout << "called print_string(\"" << str << "\")" << std::endl; 
} 


int main() 
{ 
    std::vector<std::unique_ptr<Button>> buttons; 
    std::unique_ptr<Button> b; 

    // Create a button and assign a function that takes no parameters 
    b.reset(new Button()); 
    b->fooptr = quit; 
    buttons.push_back(std::move(b)); 

    // Create a button and assign a function 
    b.reset(new Button()); 
    SDLSurface surface; 
    b->fooptr = std::bind(print, &surface); 
    buttons.push_back(std::move(b)); 

    // Create a button and assign a function taking a parameter by reference 
    b.reset(new Button()); 
    std::string string_param; 
    // Since we are passing by reference and want to bind to an existing variable 
    // we need to use a reference wrapper. 
    b->fooptr = std::bind(
     print_string, 
     std::reference_wrapper<const std::string>(string_param)); 
    buttons.push_back(std::move(b)); 

    // Call all functions setting the value of string_param before we do. 
    surface.str = "hello"; 
    string_param = "world"; 
    for(auto it = buttons.begin(); it != buttons.end(); ++it) 
    { 
     (*it)->fooptr(); 
    } 
} 
0

如果你不熟悉閉包(而不想了解他們),定義函數對象:

class BtnFunctional { 
public: 
    virtual void Call() = 0; 
    virtual ~BtnFunctional() { 
    } 
}; 

class QuitBtnFunctional : public BtnFunctional { 
public: 
    virtual void Call() { 
     // call quit function 
    } 
}; 

class PrintBtnFunctional : public BtnFunctional { 
private: 
    ScreenType * theScreen; 
public: 
    PrintBtnFunctional (ScreenType * screen) : theScreen (screen) { 
    } 

    virtual void Call() { 
     // call print function, e.g. print (theScreen); 
    } 
}; 

// ... 
btn.fooptr = new QuitBtnFunctional(); 
btn.fooptr.Call(); 
btn.fooptr = new PrintBtnFunctional (screen); 
btn.fooptr.Call(); 

注意:該物體應銷燬的地方,即你應該使用智能指針來存儲它們!

相關問題