views:

608

answers:

9

For a system I need to convert a pointer to a long then the long back to the pointer type. As you can guess this is very unsafe. What I wanted to do is use dynamic_cast to do the conversion so if I mixed them I'll get a null pointer. This page says http://publib.boulder.ibm.com/infocenter/lnxpcomp/v7v91/index.jsp?topic=/com.ibm.vacpp7l.doc/language/ref/clrc05keyword_dynamic_cast.htm

The dynamic_cast operator performs type conversions at run time. The dynamic_cast operator guarantees the conversion of a pointer to a base class to a pointer to a derived class, or the conversion of an lvalue referring to a base class to a reference to a derived class. A program can thereby use a class hierarchy safely. This operator and the typeid operator provide run-time type information (RTTI) support in C++.

and I'd like to get an error if it's null so I wrote my own dynamic cast

template<class T, class T2> T mydynamic_cast(T2 p)
{
 assert(dynamic_cast<T>(p));
 return reinterpret_cast<T>(p);
}

With MSVC I get the error "error C2681: 'long' : invalid expression type for dynamic_cast". It turns out this will only work with classes which have virtual functions... WTF! I know the point of a dynamic cast was for the up/down casting inheritance problem but I also thought it was to solve the type cast problem dynamically. I know I could use reinterpret_cast but that doesn't guarantee the same type of safety.

What should I use to check if my typecast are the same type? I could compare the two typeid but I would have a problem when I want to typecast a derived to its base. So how can I solve this?

+1  A: 

dynamic_cast can be used only between classes related through inheritance. For converting a pointer to long or vice-versa, you can use reinterpret_cast. To check whether the pointer is null, you can assert(ptr != 0). However, it is usually not advisable to use reinterpret_cast. Why do you need to convert a pointer to long?

Another option is to use a union:

union  U { 
int* i_ptr_;
long l;
}

Again, union too is needed only seldom.

Amit Kumar
A: 

dynamic_cast<> is a cast intended to be used only on convertible types (in the polymorphic sense). Forcing the cast of a pointer to a long (litb correctly suggests the static_assert to ensure the compatibility of the size) all the information about the type of the pointer are lost. There's no way to implement a safe_reinterpret_cast<> to obtain the pointer back: both value and type.

To clarify what I mean:

struct a_kind {}; 
struct b_kind {}; 

void function(long ptr) 
{} 

int 
main(int argc, char *argv[]) 
{ 
    a_kind * ptr1 = new a_kind; 
    b_kind * ptr2 = new b_kind;

    function( (long)ptr1 );
    function( (long)ptr2 );

    return 0;
}

There's no way for function() to determine the kind of pointer passed and "down" cast it to the proper type, unless either:

  • the long is wrapped by an object with some information of the type.
  • the type itself is encoded in the referenced object.

Both the solutions are ugly and should be avoided, since are RTTI surrogates.

Nicola Bonelli
A: 

You can use reinterpret_cast to cast to an integral type and back to the pointer type. If the integral type is large enough to store the pointer value, then that conversion will not change the pointer value.

As others already say, it is not defined behavior to use dynamic_cast on a non-polymorphic class (except when you do an upcast, which is implicit anyway and be ignored here), and it also only works on pointers or references. Not on integral types.

You better use ::intptr_t found in on various posix systems. You can use that type as your intermediate type you cast to.

Regarding your check whether the conversion will succeed, you can use sizeof:

BOOST_STATIC_ASSERT(sizeof(T1) >= sizeof(T2));

will fail at compile time if the conversion couldn't be done. Or continue to use assert with that condition, and it will assert at run-time instead.

Warning: This won't prevent you from casting T* to intptr_t back to U* with U another type than T. Thus, this only guarantees you the cast won't change the value of the pointer if you cast from T* to intptr_t and back to T*. (Thanks to Nicola pointing out you may expect another protection).

Johannes Schaub - litb
This static assert is obviously a necessary condition, but it's not sufficient. If he cast two different pointer to long there's not way to cast them back to the proper types. :-)
Nicola Bonelli
"if he cast two different pointers to long there's not way to cast them back to the proper pointers" . i don't understand that _.oO
Johannes Schaub - litb
unsigned long haha = reinterpret_cast<unsigned long>(fooptr); unsigned long haha1 = reinterpret_cast<unsigned long>(fooptr1); int *hahap = reinterpret_cast<int*>(haha); int *hahap1 = reinterpret_cast<int*>(haha1); Can you explain why that is not a way you could do it? (assuming long is large enough)
Johannes Schaub - litb
I mean in a safe manner, like the dynamic_cast<> does with convertible types. Let's say you have ptr_a * and ptr_b * and you convert both to long, there's no way for reinterpret_cast<> to guess the type of the object referenced.
Nicola Bonelli
It doesn't have to. If the long is large enough, the long will have an implementation defined type, but if you cast the long back to the pointer, the pointer will have the original value again. Indeed, if you cast back, you have to use the right type. But this is un-avoidable :)
Johannes Schaub - litb
Indeed and I agree with you, but it seems that it's what he was trying to achieve...
Nicola Bonelli
hmm ill add a warning
Johannes Schaub - litb
A: 

reinterpret_cast is the correct cast to use here.

This is pretty much the only thing it can do safely.

reinterpret_cast from a pointer type to a type T and back to the original pointer type yields the original pointer. (Assuming T is a pointer or integer type that is at least as big as the original pointer type)

Note that reinterpret_cast from a pointer type to T is unspecified. There are no guarantees about the value of the T type, except that if you then reinterpret_cast it back to the original type, you get the original value. So assuming you don't try to do anything with the intermediate long value in your case, reinterpret_cast is perfectly safe and portable.

Edit: Of course this doesn't help if you don't know at the second cast, what the original type was. In that case,you're screwed. The long can't possibly in any way carry type information about which pointer it was converted from.

jalf
you should add only pointers to object are safe to cast to and from. casting to void* and back is unspecified. static_cast must be used there. likewise, casting to function pointers from oject pointers or void* isn't allowed.
Johannes Schaub - litb
+1  A: 

Remember that in Windows 64, a pointer will be a 64-bit quantity but long will still be a 32-bit quantity and your code is broken. At the very least, you need to make the choice of integer type based on the platform. I don't know whether MSVC has support for uintptr_t, the type provided in C99 for holding pointers; that would be the best type to use if it is available.

As for the rest, others have addressed the why's and wherefore's of dynamic_cast vs reinterpret_cast sufficiently.

Jonathan Leffler
AFAIK long in 64bit mode is 64bits while int is still 32
acidzombie24
@acidzombie24: only on civilized systems - Win64 is LLP64; Unix uses LP64. Which, being translated, means that 'long long' and 'pointers' are 64-bit quantities in Win64, but 'long' and 'pointers' are 64-bit quantities on Unix (leaving open the possibility that 'long long' is 128-bit).
Jonathan Leffler
A: 

As soon as you decided to cast a pointer to a long, you threw type safety to the wind.

dynamic_cast is used to cast up & down a derivation tree. That is, from a base class pointer to a derived class pointer. If you have:

class Base
{
};

class Foo : public  Base
{
};

class Bar : public Base
{
};

You can use dynamic_cast in this way...

Base* obj = new Bar;

Bar* bar = dynamic_cast<Bar*>(obj); // this returns a pointer to the derived type because obj actually is a 'Bar' object
assert( bar != 0 );

Foo* foo = dynamic_cast<Foo*>(obj);  // this returns NULL because obj isn't a Foo
assert( foo == 0 );

...but you can't use dynamic cast to cast in to an out of a derivation tree. You need reinterpret_cast or C-style casts for that.

John Dibling
as i stated, dynamic_cast can't be used in that case. Base must have at least one virtual function (be polymorphic). Otherwise, the standard defines no behavior. (see 5.2.7/6 in the Standard or a draft).
Johannes Schaub - litb
A: 

What you want to do sounds like a really bad and dangerous idea, but if you MUST do it (i.e. you're working in a legacy system or on hardware that you know will never change), then I would suggest wrapping the pointer in some kind of simple struct that contains two members: 1) a void pointer to your object instance and a string, enum, or some other kind of unique identifier that will tell you what to cast the original void* to. Here's an example of what I meant (note: I didn't bother testing this so there may be syntactical errors in it):

struct PtrWrapper {
  void* m_theRealPointer;
  std::string m_type;
};

void YourDangerousMethod( long argument ) {

   if ( !argument ) 
     return;

   PtrWrapper& pw = *(PtrWrapper*)argument;

   assert( !pw.m_type.empty() );

   if ( pw.m_type == "ClassA" ) {
     ClassA* a = (ClassA*)pw.m_theRealPointer;
     a->DoSomething();
   } else if (...) { ... }

}
Michel
+1  A: 

I've had to do similar things when loading C++ DLLs in apps written in languages that only support a C interface. Here is a solution that will give you an immediate error if an unexpected object type was passed in. This can make things much easier to diagnose when something goes wrong.

The trick is that every class that you pass out as a handle has to inherit from a common base class.

#include <stdexcept>
#include <typeinfo>
#include <string>
#include <iostream>
using namespace std;


// Any class that needs to be passed out as a handle must inherit from this class.
// Use virtual inheritance if needed in multiple inheritance situations.
class Base
{

public:
    virtual ~Base() {} // Ensure a v-table exists for RTTI/dynamic_cast to work
};


class ClassA : public Base
{

};

class ClassB : public Base
{

};

class ClassC
{
public:
    virtual ~ClassC() {}
};

// Convert a pointer to a long handle.  Always use this function
// to pass handles to outside code.  It ensures that T does derive
// from Base, and that things work properly in a multiple inheritance
// situation.
template <typename T>
long pointer_to_handle_cast(T ptr)
{
    return reinterpret_cast<long>(static_cast<Base*>(ptr));
}

// Convert a long handle back to a pointer.  This makes sure at
// compile time that T does derive from Base.  Throws an exception
// if handle is NULL, or a pointer to a non-rtti object, or a pointer
// to a class not convertable to T.
template <typename T>
T safe_handle_cast(long handle)
{
    if (handle == NULL)
     throw invalid_argument(string("Error casting null pointer to ") + (typeid(T).name()));

    Base *base = static_cast<T>(NULL); // Check at compile time that T converts to a Base *
    base = reinterpret_cast<Base *>(handle);
    T result = NULL;

    try
    {
     result = dynamic_cast<T>(base);
    }
    catch(__non_rtti_object &)
    {
     throw invalid_argument(string("Error casting non-rtti object to ") + (typeid(T).name()));
    }

    if (!result)
     throw invalid_argument(string("Error casting pointer to ") + typeid(*base).name() + " to " + (typeid(T).name()));

    return result;
}

int main()
{
    ClassA *a = new ClassA();
    ClassB *b = new ClassB();
    ClassC *c = new ClassC();
    long d = 0; 


    long ahandle = pointer_to_handle_cast(a);
    long bhandle = pointer_to_handle_cast(b);
    // long chandle = pointer_to_handle_cast(c); //Won't compile
    long chandle = reinterpret_cast<long>(c);
    // long dhandle = pointer_to_handle_cast(&d); Won't compile
    long dhandle = reinterpret_cast<long>(&d);

    // send handle to library
    //...
    // get handle back
    try
    {
     a = safe_handle_cast<ClassA *>(ahandle);
     //a = safe_handle_cast<ClassA *>(bhandle); // fails at runtime
     //a = safe_handle_cast<ClassA *>(chandle); // fails at runtime
     //a = safe_handle_cast<ClassA *>(dhandle); // fails at runtime
     //a = safe_handle_cast<ClassA *>(NULL); // fails at runtime
     //c = safe_handle_cast<ClassC *>(chandle); // Won't compile
    }
    catch (invalid_argument &ex)
    {
     cout << ex.what() << endl;
    }

    return 0;
}
Eclipse
A: 

also, better use size_t instead of a long -- I think this type is ensured to be compatible with the size of the address space.