Noah said:
....
I'm kind of new to using boost but this appears to be a good answer.
You can use the type "boost::any*" as the only pointer type you allow
to be passed through generic pointers. Then later if you change the
inheritance tree on some object you will get a predictable error
instead of undefined behavior should you miss such casts.
Now the trick will be to get this to be used by the team and begin
replacing current C-Style casts and misc. pointer passing with this
more predictable setup.
Thats exactly what I proposed. Now you have an issue with how to manage
the lifetime of the any object.
If the callback is a synchronous this, you can use the exact same thing
you're using - all is well.
If pointer is stored in "C" land and comes back at some later event,
then you need to match the lifetime of the any object with the lifetime
of the object it's pointing to.
This can also be done without using the boost::any class - just have a
blase class that stores it's typeid - that's all that all that
boost::any_cast does, it checks that the typeid is equal.
One way to do this is to inherit this monster ugly thing but the use
case is quite nice.
// caution - brain dump alert - all the code below is directly from
// brain to you with no compile checks - useful as a demo
class C_CallbackBase
{
protected:
C_CallbackBase( const typeinfo & i_callback_type )
: m_sentinel( 0xca11bac8 )
m_callback_type( i_callback_type )
{
}
const unsigned m_sentinel;
const typeinfo & m_callback_type;
// make this assignable ...
C_CallbackBase & operator( const C_CallbackBase & )
{
// my derived type does not change when I am assigned ...!
}
private:
// default copy constructor is not ok
C_CallbackBase( const C_CallbackBase & ); // never called
};
template <typename DerivedType>
class CallBackBase
: public C_CallbackBase
{
CallBackBase()
: C_CallbackBase( typeid( DerivedType ) )
{
}
CallBackBase( const CallBackBase & )
: C_CallbackBase( typeid( DerivedType ) )
{
}
void * GetCallbackPtr()
{
return static_cast< void * >(
static_cast<C_CallbackBase *>( this )
);
}
};
class CallBackCast
{
void * m_ptr;
CallBackCast( void * ptr )
: m_ptr( ptr )
{
}
template <typename DerivedType>
DerivedType * operator()
{
C_CallbackBase * ptr =
static_cast<C_CallbackBase *>( m_ptr );
// this is UB if the cast is wrong but it
// probably do the right thing
if ( ptr->m_sentinel != 0xca11bac8 )
{
throw "CALLBACK CLASS CORRUPT";
}
if ( m_callback_type == typeid( DerivedType ) )
{
return static_cast<DerivedType *>( ptr );
}
throw "TYPE MISMATCH FROM C CALLBACK";
}
};
class APPCLASS
: public CallBackBase<APPCLASS>
{
};
void c_callback_func( void * cb )
{
APPCLASS * appptr = CallBackCast( cb );
... do your thing
}
int main()
{
APPCLASS app;
c_callback_func( app.GetCallbackPtr() );
}
Note that you can have two versions of this thing if performance is an
issue, a debug version that checks (like this one) and one that has an
empty base class and no checks are done.
Note that you can't use boost::any as a member (or base class) because
the copy and assignment make no sense. Note that C_CallBackBase has an
empty assignment and copy construction is not allowed.