2011-08-20 21 views
2

需要std :: bind的函數包裝器,它將在包裝函數之前調用,並將參數傳遞給包裝函數。C++中std :: bind的函數裝飾器0x

std::function<void (int)> foo = postbind<int>(service, handle); 

這就像我得到的一樣。我想讓postbind對象自動推斷出類型。我試過創建一個對象生成器make_postbind(服務,句柄),但它無法自動推導出類型。

下面我寫了一個測試用例。編譯使用:G ++ -o postbind postbind.cpp -std =的C++ 0x -lboost_system

我想獲得該行:

std::function<void (int)> func = postbind<int>(strand, std::bind(foo, myfoo(), 'a', _1)); 

下到:

std::function<void (int)> func = postbind(strand, std::bind(foo, myfoo(), 'a', _1)); 

但我不確定如何。在我的代碼,我開始獲得正開始還真有些冗長postbind模板專門到eat up my horizontal whitespace :)

#include <boost/asio.hpp> 
#include <thread> 
#include <iostream> 
#include <functional> 
#include <memory> 
using namespace boost::asio; 
using std::shared_ptr; 

typedef shared_ptr<io_service> service_ptr; 
typedef shared_ptr<io_service::work> work_ptr; 
typedef shared_ptr<io_service::strand> strand_ptr; 
typedef std::shared_ptr<io_service::work> work_ptr; 

using std::placeholders::_1; 

template<typename... Args> 
class postbind 
{ 
public: 
    typedef std::function<void (Args...)> function; 

    postbind(strand_ptr strand, function memfunc) 
     : strand_(strand), memfunc_(memfunc) 
    { 
    } 

    void operator()(Args... params) 
    { 
     strand_->post(std::bind(memfunc_, std::forward<Args>(params)...)); 
    } 
private: 
    strand_ptr strand_; 
    function memfunc_; 
}; 

// -------------------------------------------- 

struct myfoo 
{ 
    char a; 
    int b; 
}; 

void run(service_ptr service) 
{ 
    service->run(); 
} 

void foo(myfoo foo, char a, int x) 
{ 
    std::cout << "this thread: " << std::this_thread::get_id() << "\n" 
      << x << "\n"; 
} 

int main() 
{ 
    service_ptr service(new io_service); 
    strand_ptr strand(new io_service::strand(*service)); 
    work_ptr work(new io_service::work(*service)); 
    std::thread t(std::bind(run, service)); 
    std::cout << "main thread: " << std::this_thread::get_id() << "\n"; 
    std::function<void (int)> func = postbind<int>(strand, std::bind(foo, myfoo(), 'a', _1)); 
    func(99); 
    t.join(); 
} 

謝謝!

回答

1

您可以將您的模板專業化轉移到另一個類中,這樣您就不必將它們放在您撥打postbind的電話上。例如,創建一個空的類誰的目的是簡單地容納所有的曠日持久的模板參數:

template<typename... Args> 
struct post_bind_traits {}; 

現在別的地方在你的代碼(即其他文件),你可以設置的參數所有版本你需要。例如,在一個頭文件,你可以做到以下幾點:

typedef post_bind_traits<int, int> pb_int_int; 
typedef post_bind_traits<double, int> pb_double_int; 
//... additional definitions 

然後你可以創建你postbind類的模板偏特看起來像以下:

template<typename... Args> 
class postbind<post_bind_traits<Args...>> //add this partial specialization 
{ 
public: 
    typedef std::function<void (Args...)> function; 

    postbind(strand_ptr strand, function memfunc) 
     : strand_(strand), memfunc_(memfunc) 
    { 
    } 

    void operator()(Args... params) 
    { 
     strand_->post(std::bind(memfunc_, std::forward<Args...>(params))); 
    } 
private: 
    strand_ptr strand_; 
    function memfunc_; 
}; 

現在,您可以撥打postbind ,只要你有機會獲得typedef定義在頭文件,如下所示:

postbind<pb_int_int>::function func = postbind<pb_int_int>(/* arguments */); 

P在你的頭文件中找到所有複雜的typedefs,並且你的主代碼模塊文件中會有一個更乾淨的代碼集。

0

我認爲答案是沒有辦法的。這是因爲std :: function和std :: bind的返回值有所不同。

  • std :: function的函數簽名必須在聲明時指定。
  • 由std :: bind返回的functor的簽名實際上是一個可變參數模板,直到調用它的operator()纔會被決定。這意味着簽名在聲明時並非唯一,這在評估時間之前是明確的。

看看預期的調用std::function<void(...)> func = postbind(strand, std::bind(foo, myfoo(), 'a', _1);。實際上,編譯器只知道綁定參數和一些佔位符。一段時間後,它的operator()被調用,然後未綁定的參數將替換佔位符,現在編譯器可以檢查所有參數是否與函數簽名匹配。

如果上面的句子太深奧理解,請讓我帶一些代碼:

void foo(int) {} 

foo(1);   // Correct. 
foo(1, 2);  // Illegal, signature mismatched. 

auto f = std::bind(foo, _1); // Here f has no idea about unbound args for foo. 

f(1);   // OK, 1 matches int. 
f(1, 2);  // OK too, although 2 is used. 
f(1, 1, 1); // Same as before ones. 

auto func = postbind(
    strand, std::bind(foo, _1)); // If this is acceptable, 

func(99);  // this is a correct invocation then. 
func(99, 98); // And this should also be happy for compiler. Ambiguity! 

其結果是,你必須明確地指定簽名,而結合。

但無論如何,這裏有一個代碼段,這可能是一個替代解決方案,我想:

template <typename... ArgTypes> 
void do_post(strand_ptr strand, ArgTypes&&... args) 
{ 
    strand->post(std::bind(std::forward<ArgTypes>(args)...)); 
} 

int main() 
{ 
    // some code 

    auto original_closure = std::bind(foo, myfoo(), 'a', _1); 
    auto final_closure = std::bind(
     do_post<decltype(std::ref(original_closure)), int>, // signature deduced here 
     strand, std::ref(original_closure), _1); // std::ref used for inner std::bind 
    final_closure(99); 

    // others 
}