Calling upwards from within an aggregation

S

Simon Elliott

If I have two classes:

class bar
{
public:
void RunTheBar(void);
};

class foo
{
private:
bar bar_;
public:
void Callback(void);
void RunTheBar(void){bar_.RunTheBar();}
...
};

What's the best way to design "bar" such that it can call
foo::Callback()
from within its RunTheBar() method?

This is pretty straightforward using the Borland C++Builder extension
"__closure":

typedef void (__closure *MyCallback)(void);

class bar
{
private:
MyCallback callback_;
public:
bar(void):callback_(0){}
void RunTheBar(void);
void SetCallback(MyCallback Acallback){callback_ = Acallback;}
};

void bar::RunTheBar(void)
{
// ...
if (callback_)
{
callback_();
}
// ...
}

class foo
{
private:
bar bar_;
public:
foo(void)
{
bar_.SetCallback(Callback);
}
void Callback(void);
void RunTheBar(void){bar_.RunTheBar();}
};

Is there a way I can do this without using the non standard __closure,
but still without "bar" needing a definition of "foo"? It would be nice
if "bar" could be contained by objects other than "foo" without needing
to change it each time.
 
A

Attila Feher

Simon said:
Is there a way I can do this without using the non standard __closure,
but still without "bar" needing a definition of "foo"? It would be
nice if "bar" could be contained by objects other than "foo" without
needing to change it each time.

Did you look at boost::function?
 
T

tom_usenet

If I have two classes:

class bar
{
public:
void RunTheBar(void);
};

class foo
{
private:
bar bar_;
public:
void Callback(void);
void RunTheBar(void){bar_.RunTheBar();}
...
};

What's the best way to design "bar" such that it can call
foo::Callback()
from within its RunTheBar() method?

This is pretty straightforward using the Borland C++Builder extension
"__closure":

typedef void (__closure *MyCallback)(void);

Hmm, I think GCC has something like this too.
class bar
{
private:
MyCallback callback_;
public:
bar(void):callback_(0){}
void RunTheBar(void);
void SetCallback(MyCallback Acallback){callback_ = Acallback;}
};

void bar::RunTheBar(void)
{
// ...
if (callback_)
{
callback_();
}
// ...
}

class foo
{
private:
bar bar_;
public:
foo(void)
{
bar_.SetCallback(Callback);
}
void Callback(void);
void RunTheBar(void){bar_.RunTheBar();}
};

Is there a way I can do this without using the non standard __closure,
but still without "bar" needing a definition of "foo"? It would be nice
if "bar" could be contained by objects other than "foo" without needing
to change it each time.

If you can use 3rd party libraries, then boost.function is what you
want:

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

class bar
{
public:
typedef boost::function<void()> MyCallback;
bar(void){}
void RunTheBar(void);
void SetCallback(MyCallback Acallback){callback_ = Acallback;}
private:
MyCallback callback_;
};

void bar::RunTheBar(void)
{
// ...
if (!callback_.empty())
{
callback_();
}
// ...
}

class foo
{
private:
bar bar_;
public:
foo(void)
{
bar_.SetCallback(boost::bind(&foo::Callback, this));
}
void Callback(void);
void RunTheBar(void){bar_.RunTheBar();}
};

See www.boost.org.

Tom
 
K

Karl Heinz Buchegger

Simon said:
[snip]

Is there a way I can do this without using the non standard __closure,
but still without "bar" needing a definition of "foo"? It would be nice
if "bar" could be contained by objects other than "foo" without needing
to change it each time.

Yep.
Define a class, eg. 'CallbackReceiver'
Every class wishing to use bar has to be derived
from that class. With multiple inheritance you
can add the 'feature' of being a callback receiver
very easily to each and every class.
bar::RunThebar expects a pointer to a CallbeckReceiver
object, which it uses to make the callback:

class CallbackReceiver
{
public:
void Callback() = 0;
};

class bar
{
public:
void RunTheBar( CallbackReceiver* pCalledFrom )
{
...
pCalledFrom->Callback();
...
}
};

class foo : public CallbackReceiver
{
private:
bar bar_;

public:
void Callback();
void RunTheBar()
{
bar_.RunTheBar( this );
}
...
};
 
S

Simon Elliott

Attila Feher writes
Did you look at boost::function?

Thanks for the quick response. I was put off boost::function by this
snippet from the documentation:

<quote>
Member functions

In many systems, callbacks often call to member functions of a
particular object. This is often referred to as "argument binding", and
is beyond the scope of Boost.Function.
</quote>

And the example which follows:

<quote>
struct X {
int foo(int);
};

boost::function2<int, X*, int> f;

f = &X::foo;

X x;
f(&x, 5);
</quote>

When the function f is called, it seems to need an instance of X to be
visible. Even if this could be a forward declaration, it still limits
the lower level class to only being able to call methods of objects of
type X, rather than methods of objects of an arbitrary type.

I've only ever designed one solution to this problem, and it involves
having an ABC which the lower level class calls a virtual method in. The
higher level class then defines a descendent of the ABC which does the
necessary. It's standard, it's flexible, and it works, but it's always
seemed to me that there must be a better and simpler way.
 
A

Attila Feher

Simon said:
Attila Feher writes


Thanks for the quick response. I was put off boost::function by this
snippet from the documentation:

See the post by tom_usenet in this thread.
 
S

Simon Elliott

tom_usenet said:
Hmm, I think GCC has something like this too.

There was some talk about something along these lines being introduced
into the standard.
If you can use 3rd party libraries, then boost.function is what you
want:

I can use those bits of boost that are implemented solely in terms of
headers.

[snip]

This seems a nice clean solution. And I like the way that I don't need
to initialise callback_ in bar's constructor.

I use Borland C++ Builder quite a lot. I'd be interested in using this
technique with the C++ form objects which are generated by this tool. I
wonder if boost::bind and boost::function work with these. One day when
Borland get their news server back up and running I'll ask them!
 
S

Simon Elliott

Karl Heinz Buchegger said:
Yep.
Define a class, eg. 'CallbackReceiver'
Every class wishing to use bar has to be derived
from that class. With multiple inheritance you
can add the 'feature' of being a callback receiver
very easily to each and every class.
bar::RunThebar expects a pointer to a CallbeckReceiver
object, which it uses to make the callback:

[snip]

Interesting idea, simpler and neater than my own inheritance based
solution.

Sadly it won't work in all the cases I'd like to use. I use Borland C++
Builder quite a lot, and any C++ object which inherits from an Object
Pascal/Delphi object can't use MI.
 
S

Simon Elliott

Attila Feher said:
See the post by tom_usenet in this thread.

Yep. This has been my first venture into comp.lang.c++ and I'm well
impressed at the quality and speed of the responses!
 
D

David Rubin

Simon said:
Karl Heinz Buchegger said:
Yep.
Define a class, eg. 'CallbackReceiver'
Every class wishing to use bar has to be derived
from that class. With multiple inheritance you
can add the 'feature' of being a callback receiver
very easily to each and every class.
bar::RunThebar expects a pointer to a CallbeckReceiver
object, which it uses to make the callback:

[snip]

Interesting idea, simpler and neater than my own inheritance based
solution.

Sadly it won't work in all the cases I'd like to use. I use Borland C++
Builder quite a lot, and any C++ object which inherits from an Object
Pascal/Delphi object can't use MI.

How about just doing

class Foo;
class Bar
{
public:
void doit(Foo *f);
};

class Foo
{
private:
Bar bar;
public:
Foo(Bar b) : bar(b) {}
void callback();
void doit() { bar.doit(this); }
};

void Bar::doit(Foo *foo) { foo->callback(); }

/david
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top