Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Member pointer puzzle..

3 views
Skip to first unread message

Rob Kramer

unread,
Apr 23, 2002, 11:46:33 AM4/23/02
to
Hi,

I'm working on a simple timer class that will call back a member in an
object on timeout. The object and the member are specified in the timer
constructor as a C++ function pointer. All classes that want to use the
timer (or rather the ones who's callback will be used) must derive from a
Triggerable interface.

All works fine, until multiple inheritance is used. The compiler says:

g++ test.cc -o test
test.cc: In method `c::c()':
test.cc:31: type `Triggerable' is ambiguous base class for type `c'
test.cc:31: type `Triggerable' is ambiguous base class for type `c'
make: *** [test] Error 1

The test code is appended below, line 31 is marked.
The compiler complains about both arguments passed to the Timer constructor.

Does anyone have a clue what I could do to resolve this? Any tips on how to
make the use of C++ callbacks less frustrating are welcome too! :)

Cheers,

Rob

---------------------------------------
/* Classes that want to use timers must inherit from this. */
struct Triggerable
{
virtual ~Triggerable() {}
};


class Timer;


/* A timer callback typedef. */
typedef void (Triggerable::*TimerCallback) (Timer *t);


/* The timer class implementation. */
struct Timer
{
Timer (Triggerable *o, TimerCallback tcb);

// ...
};


/* Class 'a' - uses a timer which will call aCallback() on timeout. */
struct a : public Triggerable
{
a() : aTimer (this, (TimerCallback) &a::aCallback) {}

void aCallback (Timer *t) {}

Timer aTimer;
};

struct b : public Triggerable
{
};


/* Class 'c' is derived from a and b, and here the problems start. */
struct c : public a, public b
{
c() : cTimer (this, (TimerCallback) &c::cCallback) {} // --- LINE 31

void cCallback (Timer *t) {}

Timer cTimer;
};

int main()
{
c test;

return 0;
}


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Arnold the Aardvark

unread,
Apr 23, 2002, 4:48:43 PM4/23/02
to
struct c has two subobjects of type Triggerable due to the multiple
inheritence. The compiler can't decide which one you want it to
initialise. I would suggest you merge the two subobjects by making
Triggerable a *virtual* base class of both struct a and struct b. This
compiles:

struct a : public virtual Triggerable
{
...
};

struct b : public virtual Triggerable
{
...
};

struct c : public a, public b
{

...
};


Hope this helps.


Arnold the Aardvark
=======================================
7th Birmingham Circus Convention - May 4th 2002
http://www.bhamcircus.co.uk "I'll do anything for an organiser's
T-shirt"
- A certain aardvark, in a moment of madness

Giovanni Bajo

unread,
Apr 23, 2002, 5:23:53 PM4/23/02
to

"Rob Kramer" <ro...@starhub.net.sg> ha scritto nel messaggio
news:aa369e$efo$1...@coco.singnet.com.sg...

> All works fine, until multiple inheritance is used. The compiler says:
>
> g++ test.cc -o test
> test.cc: In method `c::c()':
> test.cc:31: type `Triggerable' is ambiguous base class for type `c'
> test.cc:31: type `Triggerable' is ambiguous base class for type `c'
> make: *** [test] Error 1

Use virtual inheritance, it's used to solve diamond-shape inheritance
graphs. Just do "public virtual Triggerable" instead of "public
Triggerable".

> a() : aTimer (this, (TimerCallback) &a::aCallback) {}

> c() : cTimer (this, (TimerCallback) &c::cCallback) {} // --- LINE

> 31

Both are unsafe casts, but the second will surely result in a run-time
crash. Use static_cast<> instead of an old style C cast which should be
always avoided.

Also, you may want to google a bit for "generalized callback" or
something like that. There are various libraries that helps registering
a generic callback (either a function or a member function with its
binded object, or even a class with operator() overloaded) and treat
them as they were an unique type. For example,
boost::bind/boost::function works very well for this.

void Timer::RegisterCallback(boost::function<void> cb)
{ .... }

void Timer::CallCallback(boost::function<void> cb)
{ cb(); }

void GlobalFunction(float val)
{ }

class A
{
void MemFun1(void)
{}

void MemFun2(int val)
{}

void Setup(Timer* timer)
{
// Register a->MemFun1 as callback
timer->RegisterCallback(boost::bind(&A::MemFun1, this));

// Register a->MemFun2 as callback, and it will be called with
40 as parameter
timer->RegisterCallback(boost::bind(&A::MemFun2, this, 40));

// Register GlobalFunction as callback, and it will be called
with 4.5 as parameter
timer->RegisterCallback(boost::bind(GlobalFunction, 4.5f));
}
};

Giovanni Bajo

Daniel T.

unread,
Apr 24, 2002, 9:05:02 AM4/24/02
to
Rob Kramer <ro...@starhub.net.sg> wrote:

>I'm working on a simple timer class that will call back a member in an
>object on timeout. The object and the member are specified in the timer

>constructor as a C++ function pointer. All classes that want to use the

>timer (or rather the ones who's callback will be used) must derive from
a
>Triggerable interface.
>
>All works fine, until multiple inheritance is used. The compiler says:

Don't use multiple inheritance. I would suggest you implement your
"Timer" class as an template. Something like this:

/* The timer class implementation. */

template < typename Triggerable >
struct Timer
{
typedef void( Triggerable::*TimerCallback)( Timer* t );


Timer (Triggerable *o, TimerCallback tcb);

// ...
};


/* Class 'a' - uses a timer which will call aCallback() on timeout. */

struct a {
a() : aTimer (this, &a::aCallback) {}

void aCallback (Timer<a> *t) {}

Timer<a> aTimer;
};

struct b
{
};


/* Class 'c' is derived from a and b, and here the problems start. */
struct c : public a, public b {

c() : cTimer (this, &c::cCallback) {} // --- LINE 31

void cCallback (Timer<c> *t) {}

Timer<c> cTimer;
};

int main()
{
c test;
}

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Rob Kramer

unread,
Apr 24, 2002, 9:10:30 AM4/24/02
to
Giovanni Bajo wrote:
>> test.cc:31: type `Triggerable' is ambiguous base class for type `c'
>
> Use virtual inheritance, it's used to solve diamond-shape inheritance
> graphs. Just do "public virtual Triggerable" instead of "public
> Triggerable".

Thanks! But now the compiler (gcc 2.95.3) warns:

test.cc: In method `a::a()':
test.cc:27: warning: pointer to member cast to virtual base
`Triggerable'
test.cc:27: warning: will only work if you are very careful


test.cc: In method `c::c()':

test.cc:42: warning: pointer to member cast to virtual base
`Triggerable'
test.cc:42: warning: will only work if you are very careful

Which to me is an indicator I'm trying to do something really dodgy
here.

> Also, you may want to google a bit for "generalized callback" or
> something like that. There are various libraries that helps
> registering a generic callback (either a function or a member function

> with its binded object, or even a class with operator() overloaded)
> and treat them as they were an unique type. For example,

Thanks again. I looked into the boost library, but I think it would be a
bit
of an overkill here. Very interesting though.

I got another reply by e-mail of a person who suggested I should use
templates (see code below). I think this is actually a much neater
solution
than using the Triggerable interface requirement. Somehow, I didn't
think
of that, but then again I almost never use templates other than STL's.
Not
a good habit I guess.

Cheers!

Rob

/* The timer class implementation. */

template < typename T >
struct Timer
{
typedef void (T::*callback) (Timer <T> *t);

Timer (T *o, callback tcb) {}

// ...
};


/* Class 'a' - uses a timer which will call aCallback() on timeout. */

struct a {
a() : aTimer (this, &a::aCallback) {}

void aCallback (Timer <a> *t) {}

Timer <a> aTimer;
};

struct b
{
};


/* Class 'c' is derived from a and b, and here the problems start. */
struct c : public a, public b {

c() : cTimer (this, &c::cCallback) {}

void cCallback (Timer <c> *t) {}

Timer <c> cTimer;
};

int main()
{
c test;

return 0;
}


Ron Natalie

unread,
Apr 24, 2002, 1:23:06 PM4/24/02
to

Rob Kramer wrote:
>
> Giovanni Bajo wrote:
> >> test.cc:31: type `Triggerable' is ambiguous base class for type `c'
> >
> > Use virtual inheritance, it's used to solve diamond-shape inheritance
> > graphs. Just do "public virtual Triggerable" instead of "public
> > Triggerable".
>
> Thanks! But now the compiler (gcc 2.95.3) warns:
>
> test.cc: In method `a::a()':
> test.cc:27: warning: pointer to member cast to virtual base
> `Triggerable'

Yes, with virtual bases you'll need dynamic_cast.

Werner Salomon

unread,
Apr 24, 2002, 2:30:03 PM4/24/02
to
Rob Kramer <ro...@starhub.net.sg> wrote in message
news:<aa369e$efo$1...@coco.singnet.com.sg>...

> Hi,
>
> I'm working on a simple timer class that will call back a member in an
> object on timeout. The object and the member are specified in the
timer
> constructor as a C++ function pointer. All classes that want to use
the
> timer (or rather the ones who's callback will be used) must derive
from a
> Triggerable interface.

Hi Rob,
it's not necessary to derive the user class of timer from a Triggerable
interface, if You use the adapter pattern (see Gamma's "Design
Pattern"). I changed Your Triggerable to TriggerableBase which is now
the target in the pattern. The adapter will be generated at compile time
(see template Triggerable). The adaptees are Your user classes a,b and
c.

>
> All works fine, until multiple inheritance is used. ...
.. is no more a problem, because Triggerable is no base class.

Another advantage of the solution is - no cast is necesary.

Greetings
Werner

Here is the code:
struct Timer;

// interface for calling on timeout (the target of the adpater
pattern)
class TriggerableBase
{
public:
virtual void OnTimeout( const Timer& tm ) =0;
virtual ~TriggerableBase() {};
};


// The timer class implementation.
struct Timer
{
Timer (TriggerableBase *o);

// ...
};

// the adapter
template< typename T >
class Triggerable : public TriggerableBase
{
public:
typedef void(T::*meth_type)( const Timer& tm );

Triggerable() : m_pAdaptee( 0 ), m_meth() {}

void OnTimeout( const Timer& tm )
{
if( m_pAdaptee ) (m_pAdaptee->*m_meth)( tm );
}

void Attach( T& adaptee, meth_type meth )
{
m_meth = meth;
m_pAdaptee = &adaptee;
}

private:
T* m_pAdaptee;
meth_type m_meth;
};


// the client-class - the adpatee
class A // no base class necessary
{
public:
A() : m_triggerable(), aTimer( &m_triggerable )
{
// connect the adapter and me (the adpatee)
// no cast necessary
m_triggerable.Attach( *this, &A::CallMe );
}

// method of type 'meth_type' - s. above
void CallMe( const Timer& tm );

private:
Triggerable< A > m_triggerable;
Timer aTimer;

Ivan Vecerina

unread,
Apr 25, 2002, 4:13:25 AM4/25/02
to
"Rob Kramer" <ro...@starhub.net.sg> wrote in message
news:aa369e$efo$1...@coco.singnet.com.sg...
: I'm working on a simple timer class that will call back a member in

an
: object on timeout. The object and the member are specified in the
timer
: constructor as a C++ function pointer. All classes that want to use
the
: timer (or rather the ones who's callback will be used) must derive
from a
: Triggerable interface.
:
: All works fine, until multiple inheritance is used.
....
: Does anyone have a clue what I could do to resolve this? Any tips on

how to
: make the use of C++ callbacks less frustrating are welcome too! :)

Here is an example of a general solution for callbacks:

// Let's say you have an existing callback interface:
class Trigger
{
public:
// NB: - constructor could handle registration to an event source.
// - destructor would typically disconnect the callback
automatically.
Trigger() {}
virtual void execute()=0; // the single method in this interface
// more events could exist: e.g. source disconnect, ...
protected: // just forbid deletion through interface pointer:
~Trigger() {}
//... private: disable implicit copy support ...
};

/*
The following class allows an object to support multiple callback
interfaces of the same type (e.g. registered with different events).
Instantiated as a data member, it forwards callbacks to a method of
the containing object. Base/derived classes may freely use additional
members of the same callback type
Typical usage:
class MyClass {
MyClass(.....)
: evt1_(*this, &handleEvent1)
: evt2_(*this, &handleEvent2)
{ ... }
private:
void handleEvent1();
TriggerPlug<MyClass> evt1_;
void handleEvent2();
TriggerPlug<MyClass> evt2_;
};
*/
template<class TargetT>
class TriggerPlug : public Trigger
{
public:
typedef void (TargetT::*MemFunT)();

TriggerPlug( TargetT& target, MemFunT memFun)
: Trigger(), target_(target), memFun_(memFun) { }
private:
TargetT& target_;
const MemFunT memFun_;
virtual void execute() { (target_.*memFun_)(); }
};


For a related post I made on this topic a couple years ago:
http://groups.google.com/groups?selm=966698440.46753%40unet.urbanet.ch


Several variants of the technique are possible (e.g. implement op-> in
the plug class to provide access to the event source, handle some
callbacks or provide filtering within it, etc).


I hope this helps,
Ivan


--
Ivan Vecerina, Dr. med. <> http://www.post1.com/~ivec
Soft Dev Manger, XiTact <> http://www.xitact.com
Brainbench MVP for C++ <> http://www.brainbench.com

0 new messages