2014-07-03 47 views
0

我試圖用不同的參數和返回類型創建函數的映射。所以,爲了做到這一點,我已經搜索了很多通過計算器,我覺得我得到了我所需要的,但不完全...從指針調用可變參數模板以提升:: any

背景:

this後,從答案pmr是我需要的最準確的解決方案。所以,我已經擴大了函數調用到一個可變模板(我希望)。所以,這裏是我的只是調用的版本(AnyCaller類的其餘部分完全一樣):

template<typename Ret, typename ...Args> 
    Ret call(const std::string& s, Args&&... arg) { 
    // we have to assume that our users know what we are actually returning here 
    const boost::any& a = calls[s]; 
    return boost::any_cast< std::function<Ret(Args...)> >(a)(std::forward<Args>(arg)...); 
    } 

而且它編譯OK。現在,這些都是我曾經用來測試類的三個功能:

int foo() { MessageBoxW(nullptr, L"Helolo VOID", L"Info", NULL); return 1; } 
double foo2(std::wstring str) { MessageBoxW(nullptr, str.data(), L"Info", NULL); return double(4.5); } 

UINT foo3(std::wstring str1, std::wstring str2) 
{ 
    std::wstring strBuffer; 

    strBuffer = str1; 
    strBuffer += L"==>"; 
    strBuffer += str2; 
    MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL); 

    return 4; 
} 

所以,第一個具有INT(無效),第二雙(STD :: wstring的)的簽名和第三個double(std :: wstring,std :: wstring)。現在

,這是測試代碼:

AnyCaller c; 
    c.add("foo1", std::function<int(void)>(foo)); 
    c.add("foo2", std::function<double(std::wstring)>(foo2)); 
    c.add("foo3", std::function<UINT(std::wstring, std::wstring)>(foo3)); 

    c.call<int>("foo1"); 
    c.call<double, std::wstring>("foo2", std::wstring(L"foo2!!!").data()); 
    c.call<UINT, std::wstring, std::wstring>("foo3", std::wstring(L"foo3!!!").data(), std::wstring(L"---foo3!!!").data()); 

,一切都運行平穩:)

所以,但這種好的工作,我真正需要的是增加了函數成員的支持。基本上,我所做的就是創建一個類一個與完全相同的三種功能和幾個測試目的:

class A 
{ 
public: 
    int foo() { MessageBoxW(nullptr, L"Helolo VOID", L"Info", NULL); return 1; } 
    double foo2(std::wstring str) { MessageBoxW(nullptr, str.data(), L"Info", NULL); return double(4.5); } 

    UINT foo3(std::wstring str1, std::wstring str2) 
    { 
    std::wstring strBuffer; 

    strBuffer = str1; 
    strBuffer += L"==>"; 
    strBuffer += str2; 
    MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL); 

    return 4; 
    } 

    std::wstring foo4(VOID) { return std::wstring(L"foo4"); } 
    std::wstring foo5(std::wstring strData) { return (strData + L"--foo5"); } 
    VOID foo6(VOID) { ; } 
}; 

但我無法得到這個工作。我的第一個問題是指針添加到成員函數:

A a; 
c.add("foo1", std::function<int(void)>(&A::foo)); // => Not valid 
c.add("foo1", std::function<int(void)>(&a.foo)); // => Not valid 
c.add("foo1", &a.foo);       // => Not valid 
c.add("foo1", a.foo);        // => Not valid 
c.add("foo1", ?????); //What in heaven goes here? 

顯然必須是某種來自一個轉換,但我無法想象...

當然,在這之後,我需要做的實際調用:

int nRet = c.call<int>("foo1"); 

由於任何人,可以幫助:__)

PS:我不能做靜態成員,如果這是一個解決方案...

PS2:我使用VS2013 ...

SOLUTION:

感謝@Kiroxas,@Praetorian和this文章的評論,我已經出來了一個不涉及可變模板的解決方案。

這是我的測試類AB

class A 
{ 
public: 
    int foo1() { MessageBoxW(nullptr, L"Helolo VOID", L"Info", NULL); return 1; } 
    int foo2(std::wstring str) { MessageBoxW(nullptr, str.data(), L"Info", NULL); return 5; } 

    int foo3(std::wstring str1, std::wstring str2) 
    { 
    std::wstring strBuffer; 

    strBuffer = str1; 
    strBuffer += L"==>"; 
    strBuffer += str2; 
    MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL); 

    return 4; 
    } 
}; 

class B 
{ 
public: 
    std::wstring foo4(VOID) { return std::wstring(L"foo4"); } 
    std::wstring foo5(std::wstring strData) { return (strData + L"--foo5"); } 
    VOID foo6(VOID) { ; } 
    double foo7(std::wstring str1, int nNum) 
    { 
    std::wstring strBuffer; 

    strBuffer = str1; 
    strBuffer += L"==>"; 
    strBuffer += std::to_wstring(nNum); 
    MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL); 

    return double(3.1415); 
    } 
}; 

這是將它們插入到一個數組,並呼籲他們:)我的理想的解決方案。將它們插入到地圖中的代碼,所以,只要我所有的工作,我會再次更新!該職位。

 typedef struct 
     { 
     UINT ID; 
     std::wstring NAME; 
     boost::any Func; 
     } funcs; 
     funcs CallBackItems[] = 
     { 
     //From class A 
     { 0, L"foo1", std::function<int(void)>(std::bind(&A::foo1, a)) }, 
     { 1, L"foo2", std::function<int(std::wstring)>(std::bind(&A::foo2, a, std::placeholders::_1)) }, 
     { 2, L"foo3", std::function<int(std::wstring, std::wstring)>(std::bind(&A::foo3, a, std::placeholders::_1, std::placeholders::_2)) }, 
     //From class B 
     { 3, L"foo4", std::function<std::wstring(void)>(std::bind(&B::foo4, b)) }, 
     { 4, L"foo5", std::function<std::wstring(std::wstring)>(std::bind(&B::foo5, b, std::placeholders::_1)) }, 
     { 5, L"foo6", std::function<void(void)>(std::bind(&B::foo6, b)) }, 
     { 6, L"foo7", std::function<double(std::wstring, int)>(std::bind(&B::foo7, b, std::placeholders::_1, std::placeholders::_2)) } 
     }; 

     int nResult = -1; 
     std::wstring wstrResult = L""; 
     double dResult = 0.0; 

    //class A 
    nResult = boost::any_cast<std::function<int(void)>>(CallBackItems[0].Func)(); 
    nResult = boost::any_cast<std::function<int(std::wstring)>>(CallBackItems[1].Func)(L"foo2"); 
    nResult = boost::any_cast<std::function<int(std::wstring, std::wstring)>>(CallBackItems[2].Func)(L"foo", L"3"); 
    //class B 
    wstrResult = boost::any_cast<std::function<std::wstring(void)>>(CallBackItems[3].Func)(); 
    wstrResult = boost::any_cast<std::function<std::wstring(std::wstring)>>(CallBackItems[4].Func)(L"foo5"); 
    boost::any_cast<std::function<void(void)>>(CallBackItems[5].Func)(); 
    dResult = boost::any_cast<std::function<double(std::wstring, int)>>(CallBackItems[6].Func)(L"foo", 7); 
+1

函數成員需要一個對象作爲參數,所以使用std :: bind將你的對象'a'綁定到你的函數。比如'c.add(「foo1」,std :: function (std :: bind(&A :: foo,std :: ref(a)));' – Kiroxas

+0

謝謝@Kiroxas!可悲的是它不適用於VS2013: ( –

回答

1

A::foo(1|2|3)爲非靜態成員函數,這意味着它們採取一個隱式的第一個參數,一個指向在其上他們正在被調用的對象實例(this指針)。您有兩個選項可用,可以使用std::bind來綁定要在其上調用成員函數的對象,或者在其後將指針傳遞給對象。

我有一個可變參數模板版本替換你的兩個call重載

template<typename Ret, typename... T> 
Ret call(const std::string& s, T&&... arg) { 
    // we have to assume that our users know what we are actually returning here 
    const boost::any& a = calls[s]; 
    return boost::any_cast< std::function<Ret(T...)> >(a)(std::forward<T>(arg)...); 
} 

方法1:使用std::bind

A a; 
AnyCaller c; 
c.add("foo1", std::function<int()>(std::bind(&A::foo1, &a))); 
c.add("foo2", std::function<double(std::wstring)>(
       std::bind(&A::foo2, &a, std::placeholders::_1))); 

c.call<int>("foo1"); 
c.call<double>("foo2", std::wstring(L"Calling foo2")); 

選項2:傳遞對象指針當調用該函數時。請注意,在這種情況下,std::function的類型是不同的。

A a; 
AnyCaller c; 
c.add("foo1", std::function<int(A*)>(&A::foo1)); 
c.add("foo2", std::function<double(A*, std::wstring)>(&A::foo2)); 

c.call<int>("foo1", &a); 
c.call<double>("foo2", &a, std::wstring(L"Calling foo2")); 

VS2013上的第二個選項does not work

Live demo這兩個選項。

+0

謝謝@Praetorian!:D它可以在coliru的環境中工作,但是,可悲的是,在VS2013中沒有:(可以http://stackoverflow.com/a/8304873/3802137幫助這裏? –

+0

@JesúsDelRío有一個錯誤在我發佈的例子中,我沒有注意到'foo2'的返回類型是'double'而不是'int'。無論如何,第二個版本仍然不能在VS2013上工作,因爲[這個bug](http:///connect.microsoft.com/VisualStudio/feedback/details/694400)。所以你唯一的選擇就是使用'std :: bind'。 – Praetorian