C ++ 0x中std :: bind的函数装饰器

时间:2011-08-20 05:28:53

标签: c++11 bind decorator boost-asio

需要std :: bind的函数包装器,它将在函数包装器之前调用,将参数传递给包装函数。

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

就我而言也是如此。我想让postbind对象自动推断出类型。我已经尝试创建一个对象生成器make_postbind(service,handle)但它无法自动推断出类型。

下面我写了一个测试用例。编译使用: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();
}

谢谢!

2 个答案:

答案 0 :(得分: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 */);

在标题中打包所有复杂的typedefs,您的主代码模块文件中的代码集会更清晰。

答案 1 :(得分:0)

我认为答案是没办法的。这是因为std :: function和std :: bind的返回值之间存在差异。

  • 必须在声明时指定std :: function的函数签名。
  • std :: bind返回的仿函数签名实际上是一个可变参数模板参数,在调用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
}