Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   type safety and reinterpret_cast<> (http://www.velocityreviews.com/forums/t458034-type-safety-and-reinterpret_cast.html)

Noah Roberts 10-30-2006 08:13 PM

type safety and reinterpret_cast<>
 
What steps do people take to make sure that when dealing with C API
callback functions that you do the appropriate reinterpret_cast<>? For
instance, today I ran into a situation in which the wrong type was the
target of a cast. Of course with a reinterpret_cast nothing complains
until the UB bites you in the ass. It seems to me that there ought to
be a way to deal with these kinds of functions yet still retain some
semblance of type safety. Perhaps either any or variant from boost
would help?

What do you guys do to keep from stabbing your own foot in these
situations?


Jim Langston 10-30-2006 08:41 PM

Re: type safety and reinterpret_cast<>
 
"Noah Roberts" <roberts.noah@gmail.com> wrote in message
news:1162239227.542251.79120@m73g2000cwd.googlegro ups.com...
> What steps do people take to make sure that when dealing with C API
> callback functions that you do the appropriate reinterpret_cast<>? For
> instance, today I ran into a situation in which the wrong type was the
> target of a cast. Of course with a reinterpret_cast nothing complains
> until the UB bites you in the ass. It seems to me that there ought to
> be a way to deal with these kinds of functions yet still retain some
> semblance of type safety. Perhaps either any or variant from boost
> would help?
>
> What do you guys do to keep from stabbing your own foot in these
> situations?


If you use reinterpret_cast you better know what the heck you're doing, or
don't do it. reinterpret_cast is the most dangerous cast and should be used
only when absolutly necessary. Use static_cast if possible which is a bit
mroe safer.

Other that that, if you use it wrong you shoot yoru self in the foot.



Gianni Mariani 10-30-2006 09:41 PM

Re: type safety and reinterpret_cast<>
 
Noah Roberts wrote:
> What steps do people take to make sure that when dealing with C API
> callback functions that you do the appropriate reinterpret_cast<>? For
> instance, today I ran into a situation in which the wrong type was the
> target of a cast. Of course with a reinterpret_cast nothing complains
> until the UB bites you in the ass. It seems to me that there ought to
> be a way to deal with these kinds of functions yet still retain some
> semblance of type safety. Perhaps either any or variant from boost
> would help?
>
> What do you guys do to keep from stabbing your own foot in these
> situations?


The whole meaning of reinterpret_cast is "please compiler, get out of
the way and do what I tell you to do, those bits you have, they're of
this type and just do it".

I usually relegate the reinterpret casts to very minimal usage where I
can check visually very easily that I know is happening.

One thing you could do is use a single type that embodies some type
safety whenever talking to the C api - perhaps use an "Any" class that
contains the desired pointer. That way you only expect one type to come
back and forth from the C call backs and you can verify that they
contain the right type. How you manage their lifetime however is
another story.


Noah Roberts 10-30-2006 09:45 PM

Re: type safety and reinterpret_cast<>
 

Jim Langston wrote:
> "Noah Roberts" <roberts.noah@gmail.com> wrote in message
> news:1162239227.542251.79120@m73g2000cwd.googlegro ups.com...
> > What steps do people take to make sure that when dealing with C API
> > callback functions that you do the appropriate reinterpret_cast<>? For
> > instance, today I ran into a situation in which the wrong type was the
> > target of a cast. Of course with a reinterpret_cast nothing complains
> > until the UB bites you in the ass. It seems to me that there ought to
> > be a way to deal with these kinds of functions yet still retain some
> > semblance of type safety. Perhaps either any or variant from boost
> > would help?
> >
> > What do you guys do to keep from stabbing your own foot in these
> > situations?

>
> If you use reinterpret_cast you better know what the heck you're doing, or
> don't do it. reinterpret_cast is the most dangerous cast and should be used
> only when absolutly necessary. Use static_cast if possible which is a bit
> mroe safer.


Heh, really?
>
> Other that that, if you use it wrong you shoot yoru self in the foot.


very insightfull.


Noah Roberts 10-30-2006 10:12 PM

Re: type safety and reinterpret_cast<>
 

Gianni Mariani wrote:
> Noah Roberts wrote:
> > What steps do people take to make sure that when dealing with C API
> > callback functions that you do the appropriate reinterpret_cast<>? For
> > instance, today I ran into a situation in which the wrong type was the
> > target of a cast. Of course with a reinterpret_cast nothing complains
> > until the UB bites you in the ass. It seems to me that there ought to
> > be a way to deal with these kinds of functions yet still retain some
> > semblance of type safety. Perhaps either any or variant from boost
> > would help?
> >
> > What do you guys do to keep from stabbing your own foot in these
> > situations?

>
> The whole meaning of reinterpret_cast is "please compiler, get out of
> the way and do what I tell you to do, those bits you have, they're of
> this type and just do it".


But sometimes it is a necissary evil and you would like to retain some
safety net.
>
> I usually relegate the reinterpret casts to very minimal usage where I
> can check visually very easily that I know is happening.


Well, I _prefer_ to avoid it all together.
>
> One thing you could do is use a single type that embodies some type
> safety whenever talking to the C api - perhaps use an "Any" class that
> contains the desired pointer. That way you only expect one type to come
> back and forth from the C call backs and you can verify that they
> contain the right type. How you manage their lifetime however is
> another story.


This appears like it will do the trick. You have to implement it as a
coding standard but once so required and practiced you can't undo
yourself.

#include <iostream>
#include <boost/any.hpp>

class Test
{
int x;
public:
Test(int y) : x(y) {}
int GetSerial() const { return x; }
};

class NotTest
{
double y;
public:
NotTest(double x) : y(x) {}
double f() const { return y; }
};

void f_test(void * t)
{
boost::any * at = reinterpret_cast<boost::any*>(t);

// attempt cast to Test
Test * test = boost::any_cast<Test>(at);

if (test)
std::cout << test->GetSerial() << std::endl;
}

int main()
{

boost::any x;
x = Test(50);
f_test(&x); // output 50

x = NotTest(5.09);
f_test(&x); // no output

int y; std::cin >> y;
return 0;
}

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.


Gianni Mariani 10-31-2006 12:13 AM

Re: type safety and reinterpret_cast<>
 
Noah Roberts wrote:
> Gianni Mariani wrote:
>> Noah Roberts wrote:

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

Frederick Gotham 10-31-2006 03:30 PM

Re: type safety and reinterpret_cast<>
 
Gianni Mariani:

> The whole meaning of reinterpret_cast is "please compiler, get out of
> the way and do what I tell you to do, those bits you have, they're of
> this type and just do it".



That only happens when you cast to a reference type. Elsewhere, it performs a
proper conversion:

MyClass obj;

char unsigned *p = reinterpret_cast<char unsigned*>(&obj);

--

Frederick Gotham

werasm 10-31-2006 03:44 PM

Re: type safety and reinterpret_cast<>
 

Gianni Mariani wrote:

> class C_CallbackBase
> {
> protected:
> C_CallbackBase( const typeinfo & i_callback_type )
> : m_sentinel( 0xca11bac8 )
> m_callback_type( i_callback_type )
> {
> }


Do you have a specific way in which you select your sentinal, or did
you use an arbitrary value? I have in the past used the this pointer
for this. Is that viable?

Werner


red floyd 10-31-2006 06:36 PM

Re: type safety and reinterpret_cast<>
 
werasm wrote:
> Gianni Mariani wrote:
>
>> class C_CallbackBase
>> {
>> protected:
>> C_CallbackBase( const typeinfo & i_callback_type )
>> : m_sentinel( 0xca11bac8 )
>> m_callback_type( i_callback_type )
>> {
>> }

>
> Do you have a specific way in which you select your sentinal, or did
> you use an arbitrary value? I have in the past used the this pointer
> for this. Is that viable?


his sentinel is the word "callback" written as best as possible in hex
digits.

Bart 10-31-2006 08:54 PM

Re: type safety and reinterpret_cast<>
 
Frederick Gotham wrote:
> Gianni Mariani:
>
> > The whole meaning of reinterpret_cast is "please compiler, get out of
> > the way and do what I tell you to do, those bits you have, they're of
> > this type and just do it".

>
> That only happens when you cast to a reference type. Elsewhere, it performs a
> proper conversion:
>
> MyClass obj;
>
> char unsigned *p = reinterpret_cast<char unsigned*>(&obj);


I don't know what you mean by "proper conversion". The mapping
performed by reinterpret_cast is always implementation-defined. It
could just "take those bits and reinterpret them to mean something
else" or it could "properly convert those bits" (whatever that means).
It's all up to the implementation.

Also, the standard explicitly states that reinterpret_cast<T&>(x) is
equivalent to *reinterpret_cast<T*>(&x).

Regards,
Bart.



All times are GMT. The time now is 03:07 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.