Как это сделать с помощью std :: bind?

(Замечания: Как уже должно быть ясно из тегов, это строго C ++ 03. Да, я знаю, что лямбда избавляет от всей этой боли (и я уверен, что она приносит новые виды), но это встроенная система с версией ОС 90-х годов, и мне сказали, что я должна быть рада, что у меня есть C Компилятор ++ 03 (GCC4.1.x, BTW) или компилятор C ++ вообще. Поэтому, пожалуйста, воздержитесь от публикации решений C ++ 11. Не надо втирать, правда.
Также, std::bind(), std::function() и т.д., конечно, на самом деле в std::tr1, но я отредактировал tr1 префикс, потому что я думал, что это добавляет в код только шумы.
)

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

Вот эскиз кода:

// this I just use
class server {
public:
template<unsigned int MagicTag>
bool register_call(typename some_traits_type<MagicTag>::func_type);
};

// this needs to be called from the server
class X {
public:
bool foo();
bool bar(std::string&);
bool baz(int);
};

// this is the glue
class Y {
public:
Y(X& x) : x_(x) {
register_call<MAGIC_FOO>(&Y::foo    );
register_call<MAGIC_BAZ>(&Y::bar, _1);
register_call<MAGIC_FBZ>(&Y::baz, _1);
}

private:
X&                    x_;

template<unsigned int MagicTag, typename Function>
bool register_call(Function function) {
somewhere->register_call<MagicTag>(std::bind( function
, this ));
}
template<unsigned int MagicTag, typename Function, typename PlaceHolder1>
bool register_call(Function function, PlaceHolder1 place_holder1) {
somewhere->register_call<MagicTag>(std::bind( function
, this
, place_holder1 ));
}

int foo()               {return x_.foo()  ? MAGIC_OK : MAGIC_FAILED;}
int bar(std::string& s) {return x_.bar(s) ? MAGIC_OK : MAGIC_FAILED;}
int baz(int i)          {return x_.baz(i) ? MAGIC_OK : MAGIC_FAILED;}
};

Это на самом деле работает, но на самом деле есть намного больше функций, и выполнение этого в виде утомительного усилия копирования и вставки оскорбляет мое чувство собственного достоинства и производит вонючий код. Поскольку все эти функции работают точно так же, с той лишь разницей, что они вызывают функцию и аргументы, которые они передают или не имеют, передать, я должен быть в состоянии сложить их в одну параметризованную функцию, скрывая различия за std::bind(), В противном случае я решил сначала сделать это для всех функций без каких-либо параметров (как в foo()), что составляет подавляющее большинство.

Поэтому я хотел направить все звонки foo()-подобная функция в X через одну функцию Y::call_it что делает утомительную часть:

int call_it(std::function<bool()> f) {return f() ? MAGIC_OK : MAGIC_FAILED;}

и связать соответствующую функцию в X в качестве аргумента этому:

register_call<MAGIC_FOO>(&X::foo); // note the X!

// (this is wrong)
somewhere->register_call<MagicCode>( std::bind( std::bind( &Y::call_it
, this
, function )
, std::ref(x_) );

Очевидно, что это неправильно, как и все мои другие попытки решить эту проблему. (Я только играл с std::bind() уже 10 недель, поэтому, пожалуйста, потерпите меня). В конце концов я заблудился в невероятном лабиринте веселых сообщений об ошибках из std::functionЭто смелые мужественные существа, которые могут оплакивать взрослого человека и должны кормить психиатра и его большую семью как минимум год.

Так что прежде чем я убить себя из чистого разочарования и осиротеть моих детей — как я могу это сделать?

7

Решение

Из того, что я собираю, вы хотите позвонить Y::call_it() с std::function объект соответствующим образом связан. Учитывая, что ваша внутренняя функция принимает разное количество аргументов, необходимо создать std::function<bool()> генератор для случаев, когда передаются дополнительные аргументы. Предполагая, что X объект может быть привязан во время регистрации, регистрация функции-члена без дополнительных аргументов проста:

template <int Magic, typename RC>
void register_call(RC (X::*member)()) {
somewhere->register_call<Magic>(
std::bind(&Y::call_it, this,
std::function<bool()>(std::bind(member,
std::ref(this->x_)))));
}

При передаче одного или нескольких аргументов необходимо создать std::function<bool()> объект во время вызова, потому что дополнительный аргумент должен быть связан. Я не думаю, что это может быть сделано без вспомогательной функции, но это может быть сделано с одной вспомогательной функцией на количество аргументов:

template <typename RC, typename Arg0>
static std::function<bool()>
bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) {
return std::bind(member, std::ref(x), arg0);
}
template <int Magic, typename RC,
typename Arg0, typename PlaceHolder>
void register_call(RC (X::*member)(Arg0), PlaceHolder pc) {
somewhere->register_call<Magic>(
typename some_traits_type<Magic>::type(
std::bind(&Y::call_it, this,
std::bind(&bind_argument<RC, Arg0>, member,
std::ref(this->x_), pc))));
}

Вспомогательная функция создает функцию с привязкой дополнительного аргумента. Обратите внимание, что привязываемая функция сконструирована так, чтобы она была того же типа, что и ожидаемая функцией регистра: это необходимо, например, для создания функции, принимающей дополнительные игнорируемые аргументы.

Ниже приведена тестовая программа, которую я использовал, чтобы убедиться, что все компилируется. У меня нет компилятора C ++ 2003 с TR1 под рукой, и я скомпилировал код с помощью компилятора C ++ 2011. Однако я не думаю, что использовал какое-либо расширение C ++ 2011, которое недоступно в C ++ 2003 с TR1.

#include <functional>

enum {
MAGIC_OK,
MAGIC_FAILED,
MAGIC_FOO,
MAGIC_BAR,
MAGIC_FBZ,
MAGIC_BAZ
};

template <int> struct server_traits;
template <> struct server_traits<MAGIC_FOO> {
typedef std::function<bool()> type;
};
template <> struct server_traits<MAGIC_BAR> {
typedef std::function<bool(std::string&)> type;
};
template <> struct server_traits<MAGIC_FBZ> {
typedef std::function<bool(long)> type;
};
template <> struct server_traits<MAGIC_BAZ> {
typedef std::function<bool(std::string, long)> type;
};// this I just use
class server {
public:
template<unsigned int MagicTag>
bool register_call(typename server_traits<MagicTag>::type) {
return true;
}
};

server  s;
server* somewhere = &s;

// this needs to be called from the server
class X {
public:
bool foo() { return true; }
bool bar(std::string&) { return true; }
bool baz(int) { return true; }
};

// this is the glue
class Y {
public:
Y(X& x) : x_(x) {
register_call<MAGIC_FOO>(&X::foo    );
register_call<MAGIC_BAR>(&X::bar, std::placeholders::_1);
register_call<MAGIC_FBZ>(&X::baz, std::placeholders::_1);
register_call<MAGIC_BAZ>(&X::baz, std::placeholders::_2);
}

private:
X& x_;

int call_it(std::function<bool()> f) {
return f() ? MAGIC_OK : MAGIC_FAILED;
}

template <int Magic, typename RC>
void register_call(RC (X::*member)()) {
somewhere->register_call<Magic>(
std::bind(&Y::call_it, this,
std::function<bool()>(std::bind(member,
std::ref(this->x_)))));
}
template <typename RC, typename Arg0>
static std::function<bool()>
bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) {
return std::bind(member, std::ref(x), arg0);
}
template <int Magic, typename RC,
typename Arg0, typename PlaceHolder>
void register_call(RC (X::*member)(Arg0), PlaceHolder pc) {
somewhere->register_call<Magic>(
typename server_traits<Magic>::type(
std::bind(&Y::call_it, this,
std::bind(&bind_argument<RC, Arg0>, member,
std::ref(this->x_), pc))));
}
};

int main()
{
X x;
Y y(x);
}
3

Другие решения

Из ссылки на канал чата, похоже, вы свели проблему к тому факту, что вложенная привязка не компилируется:

bind( &Y::call_it, this, bind( &X::foo, ref(x_) ) )

потому что компилятор не может вывести сигнатуру типа внутреннего bind () (как функция< bool ()> в этом случае). Это может работать вместо:

bind( &Y::call_it, this, function<bool()>( bind( &X::foo, ref(x_) ) ) )

и если это произойдет, у вас будет что-то вроде

template<unsigned int MagicTag, typename Function >
bool register_call(Function func) {
somewhere->register_call<MagicTag>(
bind( &Y::call_it, this, function<bool()>( bind( func, ref(x_)))));
}

хотя у меня такое ощущение, что второй параметр шаблона может и не понадобиться. Основная идея заключается в том, чтобы поместить std :: function между двумя std :: bind.

1

По вопросам рекламы [email protected]