2015-09-28 73 views
5

在我的回調系統的std ::函數變量我想要存儲std::function具有可變參數(或別的東西)。C++具有可變參數的

例子:

  1. 我想打電話給void()
  2. 我想打電話給void(int, int)

我想1)和2)存儲在同一個變量,並選擇叫什麼在執行呼叫

FunctionPointer f0; 
FunctionPointer f2; 

f0(); 
f2(4, 5); 

是否有可能做某事這樣的興奮?或者我必須根據輸入參數計數創建幾個「FuntionPointer」模板。

編輯

是否有可能以某種方式利用的std ::綁定完成這個任務?性病::綁定,我可以有std::function<void()> f = std::bind(test, 2, 5);

EDIT 2

實際使用情況:我有一個觸發系統,我想分配funtion指針的動作,所以當動作發生,函數被調用。 僞代碼示例:

structure Trigger 
{ 
    Function f; 
} 

Init: 
    Trigger0.f = pointer to some function() 
    Trigger1.f = pointer to some function (a, b) 

Input: 
    Find Trigger by input 
    if (in == A) Trigger.f(); 
    else Trigger.f(10, 20) 

,或者如果可能的

Input: 
    Find Trigger by input 
    if (in == A) f = bind(Trigger.f); 
    else f = bind(Trigger.f, 10, 20); 
    f() 
+2

[C++ 11可變參數的std ::函數參數](http://stackoverflow.com/q/9242234/1708801)似乎接近比賽。 –

+1

@ShafikYaghmour只將問題轉移到模板,變量類型不能相同,並且同時保持兩個函數。我不能使std ::向量的不同功能,例如 –

+1

你能提供一個實際的實際用例嗎?沒有一個實際的用例,答案只是把語法放在牆上。 「類似這樣的事情」是非常含糊的,而且就像你得到的那樣具體。然後你繼續關於'bind',這與「這樣的事情」的例子不同。再次,一個實際的用例/問題來解決所有的噪音。這意味着編寫軟件時遇到的實際具體問題。我可以想出10個可能回答你的問題的答案,但哪一個可以幫助你取決於*你實際想要做什麼*。 – Yakk

回答

2

好吧,如果你可以使用RTTI,你可以定義一個MultiFuncObject這樣的,你可以很容易綁定等功能。此外,你可以輕鬆地打電話給他們。但不幸的是,這種方法僅適用於有限的參數。但實際上boost::bind也支持有限數量的參數(默認爲9)。所以你可以擴展這個類來滿足你的需求。

在給你MultiFuncObject的來源之前,我想告訴你如何使用它。它將一個模板參數用作返回類型。您可以將新功能綁定到+=運算符。通過一些模板魔法,類可以用至少一個不同的參數類型區分具有相同參數計數的綁定函數之間的差異。

您需要C++ 11,因爲MultiFuncObject使用std::unordered_mapstd::type_index

下面是用法:

#include <iostream> 
using namespace std; 

void _1() { 
    cout << "_1" << endl; 
} 

void _2(char x) { 
    cout << "_2" << " " << x << endl; 
} 

void _3(int x) { 
    cout << "_3" << " " << x << endl; 
} 

void _4(double x) { 
    cout << "_4" << " " << x << endl; 
} 

void _5(int a, int b) { 
    cout << "_5" << " " << a << " " << b << endl; 
} 

void _6(char a, int b) { 
    cout << "_6" << " " << a << " " << b << endl; 
} 

void _7(int a, int b, int c) { 
    cout << "_7" << " " << a << " " << b << " " << c << endl; 
} 

int main() { 
    MultiFuncObject<void> funcs; 
    funcs += &_1; 
    funcs += &_2; 
    funcs += &_3; 
    funcs += &_4; 
    funcs += &_5; 
    funcs += &_6; 
    funcs += &_7; 
    funcs(); 
    funcs('a'); 
    funcs(56); 
    funcs(5.5); 
    funcs(2, 5); 
    funcs('q', 6); 
    funcs(1, 2, 3); 
    return 0; 
} 

我希望這是接近你想要什麼。下面是MultiFuncObject源:

#include <typeinfo> 
#include <typeindex> 
#include <unordered_map> 

using namespace std; 

template <typename R> 
class MultiFuncObject { 
    unordered_map<type_index, void (*)()> m_funcs; 
public: 

    MultiFuncObject<R> operator +=(R (* f)()) { 
    m_funcs[typeid(R())] = (void (*)()) f; 
    return *this; 
    } 

    template <typename A1> 
    MultiFuncObject<R> operator +=(R (* f)(A1)) { 
    m_funcs[typeid(R(A1))] = (void (*)()) f; 
    return *this; 
    } 

    template <typename A1, typename A2> 
    MultiFuncObject<R> operator +=(R (* f)(A1, A2)) { 
    m_funcs[typeid(R(A1, A2))] = (void (*)()) f; 
    return *this; 
    } 

    template <typename A1, typename A2, typename A3> 
    MultiFuncObject<R> operator +=(R (* f)(A1, A2, A3)) { 
    m_funcs[typeid(R(A1, A2, A3))] = (void (*)()) f; 
    return *this; 
    } 

    R operator()() const 
    { 
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid(R())); 
    if (it != m_funcs.end()) { 
     R (*f)() = (R (*)())(it->second); 
     (*f)(); 
    } 
    } 

    template <typename A1> 
    R operator()(A1 a1) const 
    { 
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid(R(A1))); 
    if (it != m_funcs.end()) { 
     R (*f)(A1) = (R (*)(A1))(it->second); 
     (*f)(a1); 
    } 
    } 

    template <typename A1, typename A2> 
    R operator()(A1 a1, A2 a2) const 
    { 
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid(R(A1, A2))); 
    if (it != m_funcs.end()) { 
     R (*f)(A1, A2) = (R (*)(A1, A2))(it->second); 
     (*f)(a1, a2); 
    } 
    } 

    template <typename A1, typename A2, typename A3> 
    R operator()(A1 a1, A2 a2, A3 a3) const 
    { 
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid(R(A1, A2, A3))); 
    if (it != m_funcs.end()) { 
     R (*f)(A1, A2, A3) = (R (*)(A1, A2, A3))(it->second); 
     (*f)(a1, a2, a3); 
    } 
    } 

}; 

它使用std::unordered_mapstd::type_index鍵和的void (*)()值存儲不同函數原型。需要時,使用該地圖檢索正確的功能。

Here is the working example

+1

我已經通過可變參數模板改進了您的解決方案,現在它幾乎是我想要的(現在只需關注RTTI,但嘿,我可以忍受這一點,因爲我沒有實時應用程序) –

+0

@MartinPerry太棒了!我對可變參數模板並不熟悉,所以如果您可以發送鏈接到改進的代碼;那太好了! :) –

+1

檢查此鏈接:http://pastebin.com/R7HiUAS0 –

1

如果std::function是沒有必要的,你可以創建一個代理類。

class fn_t { 
public: 
    typedef void (*fn_1_t)(); 
    typedef void (*fn_2_t)(int, int); 
    fn_1_t fn_1; 
    fn_2_t fn_2; 
    fn_t operator=(fn_1_t func_1) { fn_1 = func_1; return *this; } 
    fn_t operator=(fn_2_t func_2) { fn_2 = func_2; return *this; } 
    void operator()() { (*fn_1)(); } 
    void operator()(int a, int b) { (*fn_2)(a, b); } 
}; 

#include <iostream> 
using namespace std; 

void first() { 
    cout << "first" << endl; 
} 

void second(int a, int b) { 
    cout << "second " << a << " : " << b << endl; 
} 

int main() { 
    fn_t f; 
    f = &first; 
    f = &second; 
    f(); 
    f(5, 4); 
    return 0; 
} 

fn_t你想要的,自動分配一個需要兩個原型自動工作,並且可以通過overlading ()操作使用適當的參數調用與兩個原型的功能。

你可能想要檢查函數指針fn_1fn_2的有效性,但我沒有包含這種檢查最小化。

這樣做的好處是,你只需要C++甚至沒有STL和提升。

+1

雖然只適用於這兩個例子。如果他突然需要3個參數會怎麼樣?或者兩種不同的類型等等?那麼他需要寫一大堆的課。 –

+0

@pfannkuchen_gesicht我把這個問題解釋爲:「我有兩個**定義明確的**函數原型,我只想將這些原型的函數指針存儲在一個對象中,並使用一個對象輕鬆調用它們。」 –

10

std::function<void()>std::function<void(int, int)>是兩種完全不同的類型。您需要某種聯合功能(或多態性)來存儲未知類型的對象。

如果可以使用Boost,你可以很容易地boost::variant做到這一點:

// Declaration: 
boost::variant<std::function<void()>, std::function<void(int, int)> > f; 

// Calling, explicit: 
if (fContainsNullary()) { 
    boost::get<std::function<void()>>(f)(); 
} else { 
    boost::get<std::function<void(int, int)>>(f)(4, 5); 
} 

它是由您提供的fContainsNullary()邏輯。另外,您也可以通過訪問者使用值類型的變量,自己的存儲知識:

struct MyVisitor : boost::static_visitor<void> 
{ 
    result_type operator() (const std::function<void()> &a) { 
    a(); 
    } 

    result_type operator() (const std::function<void(int, int)> &a) { 
    a(4, 5); 
    } 
}; 

// Calling, via visitor: 
boost::apply_visitor(MyVisitor(), f); 

如果加速是不是一種選擇,你可以出於同樣的目的,手工工藝合適union

4

以下解決方案可能會爲你的工作(我不知道該代碼是完全正確的位置):

std::function與虛擬析構函數創建一個包裝使用動態轉換

class function_wrapper_base 
{ 
    virtual ~function_wrapper_base(); 
} 

template <class... Args> 
class function_wrapper 
    : public function_wrapper_base 
{ 
public: 
    std::function<void, Args...> f; 
    ... 
}; 

然後創建一個類variant_function_holder

class variant_function_holder 
{ 
    std::unique_ptr<function_wrapper_base> f; 
    ... 
    template <class... Args> 
    void operator()(Args&&... args) 
    { 
     function_wrapper<std::decay<Args>::type...> * g = dynamic_cast<function_wrapper<std::decay<Args>::type...>>(f.get()); 

     if (g == nullptr) 
     { 
      // ToDo 
     } 

     g->f(std::forward<Args>(args)...); 
    } 
}; 
+0

注意:對於'function_wrapper '... – Jarod42

4

C++ 11來拯救!

如果您可以將函數概括爲不帶任何參數的函子對象,那麼您可以使用任何lambda來調用它。

#include <iostream> 
using namespace std; 

template <class F> 
void call_it(F&& f) 
{ 
    f(); 
} 

int main() 
{ 
    int x = 50, y = 75; 

    call_it([]() { cout << "Hello!\n"; }); 
    call_it([x,y]() { cout << x << " + " << y << " = " << x + y << '\n';}); 


    return 0; 
} 
+0

,使用c字符串文字('const char *')的調用將失敗(對於'dynamic_cast')這與使用'std :: function ' ? – Angew

+0

'std :: function'有一點存儲開銷,可以通過直接綁定到閉包類型來刪除。實際區別可以忽略不計,但根據用例可能很重要。 – Chad

0

其他的答案都很好,但我想告訴我的解決方案,以及。

這是一個small header與您可以「拉長」功能簽名。 這可以讓你做到這一點(從github example提取物):

int foo_1p(int a); 
int foo_2p(int a, int b); 
int foo_3p(int a, int b, int c); 
int foo_4p(int a, int b, int c, int d); 
int foo_5p(int a, int b, int c, int d, int e); 
int foo_6p(int a, int b, int c, int d, int e, int f); 
int foo_7p(int a, int b, int c, int d, int e, int f, std::string g); 
... 

int main() 
{ 
    std::unordered_map<std::string, std::function<int(int, int, int, int, int, int, std::string)>> map; 
    map["foo_1p"] = ex::bind(foo_1p, ph, ph, ph, ph, ph, ph); 
    map["foo_2p"] = ex::bind(foo_2p, ph, ph, ph, ph, ph); 
    map["foo_3p"] = ex::bind(foo_3p, ph, ph, ph, ph); 
    map["foo_4p"] = ex::bind(foo_4p, ph, ph, ph); 
    map["foo_5p"] = ex::bind(foo_5p, ph, ph); 
    map["foo_6p"] = ex::bind(foo_6p, ph); 
    map["foo_7p"] = foo_7p; 

    for (const auto& f : map) 
    { 
     std::cout << f.first << " = " << f.second(1, 1, 1, 1, 1, 1, "101") << std::endl; 
    } 
}