views:

68

answers:

2

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. :(

+3  A: 

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

Jesse Beder
A: 

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.

Noah Roberts