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

发布于 2024-11-30 07:37:37 字数 2408 浏览 0 评论 0原文

需要 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));

但我不确定如何。在我的代码中,我开始获得一些非常长的绑定后模板专业化,这些专业化开始 吃掉我的水平空白 :)

#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();
}

谢谢!

need a function wrapper for std::bind that will be called before the function it's wrapper, passing the arguments along to the wrapped functions.

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

That's as far as I've got too. I'd like to make the postbind object auto-deduce the type. I've tried creating an object generator make_postbind(service, handle) but it was unable to deduce the types automatically.

Below I've written a test case. Compiles using: g++ -o postbind postbind.cpp -std=c++0x -lboost_system

I'd like to get the line:

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

Down to:

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

But am unsure how to. In my code, I'm starting to get some really lengthy postbind template specialisations that are beginning to 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();
}

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

彼岸花似海 2024-12-07 07:37:37

您可以将模板专业化移到另一个类中,这样您就不必将它们放在对 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 */);

将所有复杂的 typedef 打包到头文件中,您将获得更清晰的代码- 在主代码模块文件中设置。

You could move your template specializations into another class so that you do not have to place them on your calls to postbind. For instance, create an empty class who's purpose is to simply hold all the long drawn-out template arguments:

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

Now somewhere else in your code (i.e., another file), you could setup all the versions of arguments you would need. For instance, in a header file you could do the following:

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

Then you can create a partial template specialization of your postbind class that looks like the following:

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_;
};

Now you can call postbind, provided you have access to the typedef definitions in the header files, like the following:

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

Pack all the complicated typedefs in your header, and you'll have a much cleaner code-set in your main code-module files.

风吹短裙飘 2024-12-07 07:37:37

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

  • std::function 的函数签名必须在声明时指定。
  • std::bind 返回的仿函数的签名实际上有一个可变参数模板参数,直到调用它的operator() 时才会决定。这意味着签名在声明时不是唯一的,这肯定是在评估时间之前。

查看预期的调用,std::functionfunc = 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
}

I think the answer is no way. This is because of the difference between std::function and the return value of std::bind.

  • The function signature of std::function must be specified while being declared.
  • The signature of functor returned by std::bind has actually a variadic template arguments, which won't be decided until its operator() is invoked. It means that the signature is not unique at declaring time, which is definitely before evaluating time.

Look at the expected invocation, std::function<void(...)> func = postbind(strand, std::bind(foo, myfoo(), 'a', _1);. Actually, the compiler only knows the bound arguments and some placeholders. After a while, its operator() is invoked, then the unbound arguments are going to replace the placeholders, and now compiler can check whether all arguments matches the function signature or not.

If above sentences are too recondite to understand, please let me show some code:

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!

As a result, you have to specify the signature explicitly while binding.

But anyway, here's a code snippet which might be a substitute solution I guess:

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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文