Декораторы функций для std :: bind в C ++ 0x - PullRequest
2 голосов
/ 20 августа 2011

нужна функция-обертка для 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, которые начинают поглощать мой горизонтальный пробел :)

#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 ]

1 голос
/ 20 августа 2011

Вы можете переместить ваши шаблонные специализации в другой класс, чтобы вам не приходилось помещать их в ваши звонки на 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 в свой заголовок, и у вас будет намного более чистый кодовый набор в ваших основных файлах модуля кода.

0 голосов
/ 11 апреля 2013

Я думаю, что ответ - нет. Это из-за разницы между std :: function и возвращаемым значением std :: bind.

  • Сигнатура функции std :: function должна быть указана при объявлении.
  • Сигнатура функтора, возвращаемая std :: bind, на самом деле имеет переменные аргументы шаблона, которые не будут определены, пока не будет вызван его оператор (). Это означает, что подпись не уникальна при объявлении времени, что определенно предшествует оценке времени.

Посмотрите на ожидаемый вызов, std::function<void(...)> func = postbind(strand, std::bind(foo, myfoo(), 'a', _1);. На самом деле, компилятор знает только связанные аргументы и некоторые заполнители. Через некоторое время вызывается его оператор (), затем несвязанные аргументы заменят заполнители, и теперь компилятор может проверить, соответствуют ли все аргументы сигнатуре функции.

Если приведенные выше предложения слишком сложны для понимания, пожалуйста, позвольте мне показать код:

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
}
...