2012-12-26 76 views
7

注:正如应该已经从标签清楚,这是严格C++ 03是的,我知道,拉姆达使得这一切痛苦消失(在新带来的种类,我打赌),但这是一个嵌入式系统,具有90年代的操作系统版本,我被告知应该很高兴我有一个C++ 03编译器(GCC4.1.x,BTW)或C++因此,请放弃发布C++ 11解决方案,不需要蹭它,真的。
另外,std::bind(),std::function()等当然实际上在std::tr1,但我编辑了tr1前缀,因为我认为它在代码中只增加了噪声。
如何用std :: bind做到这一点?

我有一些类似服务器的东西,我需要注册的功能,我需要调整它们来调用一些对象的相似但略有不同的功能。这些函数有不同的参数列表。服务器“知道”,当我尝试注册一个函数时,它只接受一个带有正确签名的函数(正如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;} 
}; 

这实际工作,但在现实中有这样多的功能,做这个作为一个乏味的copy'n'paste努力侮辱我的尊严感和产生臭味的代码。由于所有这些函数的功能完全相同,唯一不同的是它们调用的函数以及它们具有或不具有的参数,所以我应该能够将它们折叠成一个参数化函数,从而将差异隐藏在std::bind()之后。如果做不到这一点,我首先对没有任何参数的所有功能(如foo())这样做,这是绝大多数。

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

,并绑定在X相应的函数作为参数传递给它:

所以我通过一个单一功能Y::call_it,做繁琐的部分想航线的foo()样功能的所有调用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的模板化胆量,它可以让一个成年男子流下眼泪,至少应该让他的家庭缩水一年。

所以在我之前kill myself out of sheer frustration和孤儿我的孩子 - 我怎么能这样做?

+0

我从来没有使用过std :: bind。那是为了认罪。但是,你不想'register_call (std :: bind(&Y :: call_it,this,std :: bind(ptmf,std :: ref(x_)));'? – rici

+0

@rici:当然,你是对的。[我已经看到了这个我自己](http://chat.stackoverflow.com/transcript/10?m=6878122#6878122。)它只是我一直在撞我的头撞墙小时,而这只是1001号中间的混乱状态,试图让它工作,这是当我决定要问这个问题的时候,然而,问题是我不能以另一种方式工作,_sigh._ – sbi

+1

我想这就是为什么我从来没有使用过std :: bind模板metadebugging是疯狂的,或者它让你疯狂,这可能是为什么所有标准库实现都不合标准。祝你好运! – rici

回答

3

从我收集的内容中,您想要调用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)))); 
} 

的辅助函数有额外的参数被绑定创建了一个函数。请注意,被绑定的函数被构造为与寄存器函数期望的类型相同的类型:这是必要的,例如,创建一个带有额外的,被忽略的参数的函数。

下面是一个测试程序,我用来看看是否编译。我手边没有带有TR1的C++ 2003编译器,并用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); 
} 
+0

谢谢,Dietmar!对于'std :: bind()'的调用,如果没有我已经发现的[在聊天中](http://chat.stackoverflow.com/transcript/10?m=6878274#6878274) 。不过,我仍在与编译器就争夺参数的函数进行斗争,所以您的解决方案确实非常受欢迎。 – sbi

+0

不幸的是,我无法让你的代码编译。对于使用'std :: string&'编译器的函数吠叫'没有匹配的函数来调用'bind(<未解析的重载函数类型>,bool(X :: *&)(std :: string&),std :: reference_wrapper ,std :: _占位符<1>&)''。还有两个错误。对'Arg0 const&arg0'来说,它似乎很沮丧,它既不适合'int也不适合'std :: string&'。 – sbi

+0

它确实编译器为我使用gcc。尝试使用clang编译代码失败,原因是由于我使用的libcxx版本似乎存在错误。然而,尝试改变'bind_argument()'的声明来移除'Arg0'参数上的'const&'(const''是我推断参数并且'Arg0'是正确的键入无论如何, –

1

从聊天频道的链接,它看起来就像你煮问题到一个事实,即嵌套的绑定无法编译:

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

,因为编译器不能推断出内部绑定()的类型签名(在本例中为函数< 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 :: bind之间放置一个std :: function。