How to do this with std :: bind?

( Note: As it should already be clear from the tags, this is strictly C ++ 03. Yes, I know, lambda makes all this pain go away (and adds new kinds, I'm sure), but it is an embedded system with an OS version from the 90s years, and I was told that I should be glad that I have a C ++ 03 compiler (GCC4.1.x, BTW), or a C ++ compiler. Therefore, please refrain from posting solutions in C ++ 11 No need to rub it in. Also, std::bind() , std::function() , etc., of course, are actually in std::tr1 , but I edited the tr1 prefix because I thought that he adds mostly just noise to a.)

I have a server thing in which I need to register functions, and I need to adapt them to call some kind of object similar, but slightly different from it. These functions have different argument lists. The server "knows" that, and when I try to register a function, it only accepts the one that has the correct signature (as required, for example, std::function ), depending on some magic tag passed as an argument template.

Here's a sketch of the code:

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

It actually works, but in fact there are even more functions and does it, because the tedious effort of copying reduces my dignity and produces smelly code. Since all these functions perform exactly the same, with the only difference being that the function they call and the arguments that they have or don't have to pass, I would have to collapse them into one parameterized function, hiding the differences behind std::bind() . Otherwise, I decided to do it first for all functions without any parameters (as in foo() ), which is the vast majority.

So, I wanted to direct all calls to the foo() -like function in X through one function Y::call_it , which does the tedious part:

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

and bind the corresponding function in X as an argument to it:

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

This is obviously wrong, and all my other attempts to solve it. (I only play with std::bind() for 10 weeks, so please bear with me). In the end, I got lost in an incredible maze of funny error messages from std::function templatized guts, which can lead an adult to tears and have to feed the reduction and its extended family for a year at least.

So, before I kill me out of sheer disappointment and orphaned my children - how can I do this?

+7
source share
2 answers

From what I'm compiling, you want to call Y::call_it() std::function object, appropriately related. Given that your inner function accepts a different number of arguments, you need to create a std::function<bool()> generator for cases when additional arguments are passed. Assuming that an object X can be bound during registration, registering a member function without additional arguments is straightforward:

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

When passing one or more arguments, you must create the std::function<bool()> object during the call, because the additional argument must be bound. I do not think that this can be done without a helper function, but this can be done with a single helper function for the number of arguments:

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

A helper function creates a function with an optional argument bound. Note that the associated function is constructed in the same way as the register function: this is necessary, for example, to create a function with additional ignored arguments.

Below is a test program that I used to see if things compiled. I donโ€™t have a C ++ 2003 compiler with TR1 at hand and compiled code with a C ++ 2011 compiler. However, I donโ€™t think I used any C ++ 2011 extension that is not available in C ++ 2003 with 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
source

From the chat channel channel, it looks like you have boiled the problem until the nested binding does not compile:

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

because the compiler cannot output a signature such as internal binding () (like the function <bool ()> in this case). This may work instead:

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

and if that happens, you will have something like

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

although I feel that the second template parameter might not be needed in some way. The basic idea is to set std :: function between two std :: bind.

+1
source

All Articles