I'm creating a couple of interfaces intended to provide access to a callback functionality. That is, inheriting from interface A allows a class to use callbacks of type one; interface B allows type two. Inheriting from both A and B allows callbacks of both types. The ultimate purpose is that classes A and B will take care of all the dirty work by just inheriting from them.
First Problem
Here's a small example that should illustrate some of the trouble I'm having:
class A
{
public:
static void AFoo( void* inst )
{
((A*)inst)->ABar( );
}
virtual void ABar( void ) = 0;
};
class B
{
public:
static void BFoo( void* inst )
{
((B*)inst)->BBar( );
}
virtual void BBar( void ) = 0;
};
class C : public A, public B
{
public:
void ABar( void ){ cout << "A"; };
void BBar( void ){ cout << "B"; };
};
And by making the calls
C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo( (void*)c_inst );
BFoo( (void*)c_inst );
I expect I'll get "AB" as output. Instead I get "AA". Reversing the order of derived classes (B before A), produces "BB". Why is this?
Second Problem
The actual interfaces I'm using are templated, so the code appears more like
template <class T> class A
{
public:
static void AFoo( void* inst )
{
((T*)inst)->ABar( );
}
virtual void ABar( void ) = 0;
};
template <class T> class B
{
public:
static void BFoo( void* inst )
{
((T*)inst)->BBar( );
}
virtual void BBar( void ) = 0;
};
class C : public A<C>, public B<C>
{
public:
void ABar( void ){ cout << "A"; };
void BBar( void ){ cout << "B"; };
};
The reason for this is so A and B can do all the work, but their implementations don't need to have any knowledge of C.
Now, calling with
C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo( (void*)c_inst );
BFoo( (void*)c_inst );
produces the correct output: "AB".
This small example works fine here, but it doesn't always work correctly in practice. Very strange things start happening, similar to the weirdness in the first problem above. The main issue appears to be that both virtual functions (or the static functions, or something) don't always make it into C.
For instance, I can successfully call C::AFoo(), but not always C::BFoo(). And this is sometimes dependent on the order in which I derive from A and B: class C: public A<C>, public B<C>
may produce code where neither AFoo or BFoo work, while class C: public B<C>, public A<C>
may produce code where one of them works, or maybe both.
Since the classes are templated, I can remove the virtual functions in A and B. Doing so produces working code, so long as ABar and BBar exist in C of course. That's acceptable, but not desired; I'd rather know what the problem is.
What possible reasons are there that the above code could cause bizarre problems?
Why does the second example produce the correct output where though the first doesn't?
You're invoking undefined behavior. You can cast an
X*
to avoid*
, but once you've done that, the only thing it is safe to cast thatvoid*
is to anX*
(this is not entirely true, I'm oversimplifying but for the sake of argument pretend it is).Now why is the code behaving like it is? One way to implement MI is something akin to:
In this example A is first, but the order will be determined by the compiler. When you cast to void, you get a pointer to the beginning of C. When you cast that void* back, you've lost the information you need to adjust the pointer appropriately if necessary. Since both A and B have a single virtual function with the same signature, you end up calling the impl. of whichever class happens to be first in the object layout.