2011-08-20 23 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 
}