2012-12-26 75 views
7

注:正如應該已經從標籤清楚,這是嚴格C++ 03是的,我知道,拉姆達使得這一切痛苦消失(在新帶來的種類,我打賭),但這是一個嵌入式系統,具有90年代的操作系統版本,我被告知應該很高興我有一個C++ 03編譯器(GCC4.1.x,BTW)或C++因此,請放棄發佈C++ 11解決方案,不需要蹭它,真的。
另外,std::bind(),std::function()等當然實際上在std::tr1,但我編輯了tr1前綴,因爲我認爲它在代碼中只增加了噪聲。
如何用std :: bind做到這一點?

我有一些類似服務器的東西,我需要註冊的功能,我需要調整它們來調用一些對象的相似但略有不同的功能。這些函數有不同的參數列表。服務器「知道」,當我嘗試註冊一個函數時,它只接受一個帶有正確簽名的函數(正如std::function要求的那樣),這取決於作爲模板參數傳入的一些魔術標記。

這裏的代碼的草圖

// this I just use 
class server { 
public: 
    template<unsigned int MagicTag> 
    bool register_call(typename some_traits_type<MagicTag>::func_type); 
}; 

// this needs to be called from the server 
class X { 
public: 
    bool foo(); 
    bool bar(std::string&); 
    bool baz(int); 
}; 

// this is the glue 
class Y { 
public: 
    Y(X& x) : x_(x) { 
     register_call<MAGIC_FOO>(&Y::foo ); 
     register_call<MAGIC_BAZ>(&Y::bar, _1); 
     register_call<MAGIC_FBZ>(&Y::baz, _1); 
    } 

private: 
    X&     x_; 

    template<unsigned int MagicTag, typename Function> 
    bool register_call(Function function) { 
     somewhere->register_call<MagicTag>(std::bind(function 
                , this)); 
    } 
    template<unsigned int MagicTag, typename Function, typename PlaceHolder1> 
    bool register_call(Function function, PlaceHolder1 place_holder1) { 
     somewhere->register_call<MagicTag>(std::bind(function 
                , this 
                , place_holder1)); 
    } 

    int foo()    {return x_.foo() ? MAGIC_OK : MAGIC_FAILED;} 
    int bar(std::string& s) {return x_.bar(s) ? MAGIC_OK : MAGIC_FAILED;} 
    int baz(int i)   {return x_.baz(i) ? MAGIC_OK : MAGIC_FAILED;} 
}; 

這實際工作,但在現實中有這樣多的功能,做這個作爲一個乏味的copy'n'paste努力侮辱我的尊嚴感和產生臭味的代碼。由於所有這些函數的功能完全相同,唯一不同的是它們調用的函數以及它們具有或不具有的參數,所以我應該能夠將它們摺疊成一個參數化函數,從而將差異隱藏在std::bind()之後。如果做不到這一點,我首先對沒有任何參數的所有功能(如foo())這樣做,這是絕大多數。

int call_it(std::function<bool()> f) {return f() ? MAGIC_OK : MAGIC_FAILED;} 

,並綁定在X相應的函數作爲參數傳遞給它:

所以我通過一個單一功能Y::call_it,做繁瑣的部分想航線的foo()樣功能的所有調用X

register_call<MAGIC_FOO>(&X::foo); // note the X! 

// (this is wrong) 
somewhere->register_call<MagicCode>(std::bind(std::bind(&Y::call_it 
                 , this 
                 , function) 
               , std::ref(x_)); 

顯然,這是錯誤的,所以我所有的其他嘗試來解決這個問題。 (我現在只玩了std::bind() 10周,所以請耐心等待)。最後,我迷失在一個令人難以置信的迷宮中,這些迷人的錯誤信息來自於std::function的模板化膽量,它可以讓一個成年男子流下眼淚,至少應該讓他的家庭縮水一年。

所以在我之前kill myself out of sheer frustration和孤兒我的孩子 - 我怎麼能這樣做?

+0

我從來沒有使用過std :: bind。那是爲了認罪。但是,你不想'register_call (std :: bind(&Y :: call_it,this,std :: bind(ptmf,std :: ref(x_)));'? – rici

+0

@rici:當然,你是對的。[我已經看到了這個我自己](http://chat.stackoverflow.com/transcript/10?m=6878122#6878122。)它只是我一直在撞我的頭撞牆小時,而這只是1001號中間的混亂狀態,試圖讓它工作,這是當我決定要問這個問題的時候,然而,問題是我不能以另一種方式工作,_sigh._ – sbi

+1

我想這就是爲什麼我從來沒有使用過std :: bind模板metadebugging是瘋狂的,或者它讓你瘋狂,這可能是爲什麼所有標準庫實現都不合標準。祝你好運! – rici

回答

3

從我收集的內容中,您想要調用Y::call_it()並將std::function對象合適地綁定。考慮到你的內部函數需要不同數量的參數,有必要爲傳遞附加參數的情況創建一個std::function<bool()>生成器。假設X對象可以在登記時間的約束,而無需額外的參數的成員函數的登記是直截了當:

template <int Magic, typename RC> 
void register_call(RC (X::*member)()) { 
    somewhere->register_call<Magic>(
     std::bind(&Y::call_it, this, 
      std::function<bool()>(std::bind(member, 
              std::ref(this->x_))))); 
} 

當通過一個或多個參數,它是必須建立在呼叫時間std::function<bool()>對象,因爲額外的論據需要加以約束。我不認爲這可以在沒有輔助函數來完成,但它可以與每數量的參數一個輔助函數來完成:

template <typename RC, typename Arg0> 
static std::function<bool()> 
bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) { 
    return std::bind(member, std::ref(x), arg0); 
} 
template <int Magic, typename RC, 
      typename Arg0, typename PlaceHolder> 
void register_call(RC (X::*member)(Arg0), PlaceHolder pc) { 
    somewhere->register_call<Magic>(
     typename some_traits_type<Magic>::type(
      std::bind(&Y::call_it, this, 
         std::bind(&bind_argument<RC, Arg0>, member, 
           std::ref(this->x_), pc)))); 
} 

的輔助函數有額外的參數被綁定創建了一個函數。請注意,被綁定的函數被構造爲與寄存器函數期望的類型相同的類型:這是必要的,例如,創建一個帶有額外的,被忽略的參數的函數。

下面是一個測試程序,我用來看看是否編譯。我手邊沒有帶有TR1的C++ 2003編譯器,並用C++ 2011編譯器編譯了代碼。但是,我不認爲我已經使用了C++ 2011擴展,它不能從C++ 2003和TR1中獲得。

#include <functional> 

enum { 
    MAGIC_OK, 
    MAGIC_FAILED, 
    MAGIC_FOO, 
    MAGIC_BAR, 
    MAGIC_FBZ, 
    MAGIC_BAZ 
}; 

template <int> struct server_traits; 
template <> struct server_traits<MAGIC_FOO> { 
    typedef std::function<bool()> type; 
}; 
template <> struct server_traits<MAGIC_BAR> { 
    typedef std::function<bool(std::string&)> type; 
}; 
template <> struct server_traits<MAGIC_FBZ> { 
    typedef std::function<bool(long)> type; 
}; 
template <> struct server_traits<MAGIC_BAZ> { 
    typedef std::function<bool(std::string, long)> type; 
}; 


// this I just use 
class server { 
public: 
    template<unsigned int MagicTag> 
    bool register_call(typename server_traits<MagicTag>::type) { 
     return true; 
    } 
}; 

server s; 
server* somewhere = &s; 

// this needs to be called from the server 
class X { 
public: 
    bool foo() { return true; } 
    bool bar(std::string&) { return true; } 
    bool baz(int) { return true; } 
}; 

// this is the glue 
class Y { 
public: 
    Y(X& x) : x_(x) { 
     register_call<MAGIC_FOO>(&X::foo ); 
     register_call<MAGIC_BAR>(&X::bar, std::placeholders::_1); 
     register_call<MAGIC_FBZ>(&X::baz, std::placeholders::_1); 
     register_call<MAGIC_BAZ>(&X::baz, std::placeholders::_2); 
    } 

private: 
    X& x_; 

    int call_it(std::function<bool()> f) { 
     return f() ? MAGIC_OK : MAGIC_FAILED; 
    } 

    template <int Magic, typename RC> 
    void register_call(RC (X::*member)()) { 
     somewhere->register_call<Magic>(
      std::bind(&Y::call_it, this, 
       std::function<bool()>(std::bind(member, 
               std::ref(this->x_))))); 
    } 
    template <typename RC, typename Arg0> 
    static std::function<bool()> 
    bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) { 
     return std::bind(member, std::ref(x), arg0); 
    } 
    template <int Magic, typename RC, 
       typename Arg0, typename PlaceHolder> 
    void register_call(RC (X::*member)(Arg0), PlaceHolder pc) { 
     somewhere->register_call<Magic>(
      typename server_traits<Magic>::type(
       std::bind(&Y::call_it, this, 
          std::bind(&bind_argument<RC, Arg0>, member, 
            std::ref(this->x_), pc)))); 
    } 
}; 

int main() 
{ 
    X x; 
    Y y(x); 
} 
+0

謝謝,Dietmar!對於'std :: bind()'的調用,如果沒有我已經發現的[在聊天中](http://chat.stackoverflow.com/transcript/10?m=6878274#6878274) 。不過,我仍在與編譯器就爭奪參數的函數進行鬥爭,所以您的解決方案確實非常受歡迎。 – sbi

+0

不幸的是,我無法讓你的代碼編譯。對於使用'std :: string&'編譯器的函數吠叫'沒有匹配的函數來調用'bind(<未解析的重載函數類型>,bool(X :: *&)(std :: string&),std :: reference_wrapper ,std :: _佔位符<1>&)''。還有兩個錯誤。對'Arg0 const&arg0'來說,它似乎很沮喪,它既不適合'int也不適合'std :: string&'。 – sbi

+0

它確實編譯器爲我使用gcc。嘗試使用clang編譯代碼失敗,原因是由於我使用的libcxx版本似乎存在錯誤。然而,嘗試改變'bind_argument()'的聲明來移除'Arg0'參數上的'const&'(const''是我推斷參數並且'Arg0'是正確的鍵入無論如何, –

1

從聊天頻道的鏈接,它看起來就像你煮問題到一個事實,即嵌套的綁定無法編譯:

bind(&Y::call_it, this, bind(&X::foo, ref(x_))) 

,因爲編譯器不能推斷出內部綁定()的類型簽名(在本例中爲函數< bool()>)。這可能不是工作:

bind(&Y::call_it, this, function<bool()>(bind(&X::foo, ref(x_)))) 

,如果是的話,你就必須像

template<unsigned int MagicTag, typename Function > 
bool register_call(Function func) { 
    somewhere->register_call<MagicTag>(
     bind(&Y::call_it, this, function<bool()>(bind(func, ref(x_))))); 
} 

雖然我得到的感覺是第二個模板參數可能沒有必要以某種方式。關鍵的想法是在兩個std :: bind之間放置一個std :: function。