“Interface” like semantics with boost::bind

浪尽此生 提交于 2019-12-07 18:57:21

问题


I wanted to be able to have something like Java's interface semantics with C++. At first, I had used boost::signal to callback explicitly registered member functions for a given event. This worked really well.

But then I decided that some pools of function callbacks were related and it made sense to abstract them and register for all of an instance's related callbacks at once. But what I learned was that the specific nature of boost::bind and/or taking the value of this seemed to make that break. Or perhaps it was just the fact that the add_listener(X &x) method declaration changed the code that boost::bind generated.

I have a very rough understanding why the problem occurred and I think it's probably functioning correctly as per its design. I am curious: what should I have done instead? Surely there's a Right Way to do it.

Here's some example code:

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <iostream>

using namespace std;

struct X;
struct Callback
{
    virtual void add_listener(X &x) = 0;
};

struct X
{
    X() {}
    X(Callback &c) {  c.add_listener(*this); }
    virtual void go() { cout << "\t'" << __PRETTY_FUNCTION__ << "'" << endl; }
};

struct CallbackReal : public Callback
{
    virtual void add_listener(X &x)
    {
        f = boost::bind<void>(boost::mem_fn(&X::go), x);
    }

    void go() { f(); }

    boost::function<void (void)> f;
};


struct Y : public X
{
    Y() {}

    Y(Callback &c) {  c.add_listener(*this); }
    virtual void go() { cout << "\t'" << __PRETTY_FUNCTION__ << "'" << endl; }
};


int main(void)
{
    CallbackReal c_x;
    CallbackReal c_y;

    X x(c_x);
    Y y(c_y);

    cout << "Should be 'X'" << endl;
    boost::bind<void>(boost::mem_fn(&X::go), x)();

    cout << "Should be 'Y'" << endl;
    boost::bind<void>(boost::mem_fn(&X::go), y)();

    cout << "------------------" << endl;

    cout << "Should be 'X'" << endl;
    c_x.go();
    cout << "I wish it were 'Y'" << endl;
    c_y.go();

    return 0;
}

Okay, I did not describe the problem completely. The title is misleading.

Oh, man. Downvote this one. I obviously haven't described the problem well and I think this ultimately boils down to mostly a syntactical error. :(


回答1:


boost::bind takes its parameters by value and copies them. That means

f = boost::bind<void>(boost::mem_fn(&X::go), x);

will pass a copy of x, which will slice off the Y piece of it (if it was really a Y to begin with). To get virtual dispatch to work, you need to pass a pointer to boost::bind:

f = boost::bind(&X::go, &x);

(Note that you don't actually need mem_fn, or to explicitly write <void>, since boost::bind and argument deduction take care of those for you.)




回答2:


Java interfaces don't specifically exist within C++. Closest you can get is pure abstract base classes. This is generally quite close enough.

The rest of your question is unrelated to interfaces. Java uses the Observer pattern for event connection and dispatch. The interface part is only mildly related because observers are required to obey specific interfaces (of course, since otherwise you wouldn't have any idea what to call).

Using boost::bind to create functors is actually an abstraction beyond interfaces and is thus a more generic solution. The observer pattern and functors are put together into signal/slot idiom/patterns implemented in various libraries like boost::signals, boost::signals2, and gtk++. The Qt version is quite different in mechanics but similar in concept.

So, what's this mean to help you understand what, why and where? I'd suggest starting with a search on what the Observer pattern is and try to write a few implementations.



来源:https://stackoverflow.com/questions/4038256/interface-like-semantics-with-boostbind

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!