Safe variable type checking

For the system, I need to convert the pointer to long, then long back to pointer type. As you can guess, this is very dangerous. What I wanted to do was use dynamic_cast to convert, so if I mixed the two I get a null pointer. From this page 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 the type at runtime. The dynamic_cast operator guarantees conversion of a pointer to a base class to a pointer to a derived class, or an lvalue conversion by referring to a base class to a reference to a derived class. Thus, the program can use the hierarchy class safely. This operator and the typeid operator provide runtime (RTTI) in C ++.

and I would like to get an error if it was null, so I wrote my own dynamic casting

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 that have virtual functions ... WTF! I know the dynamic cast point was related to the top / bottom inheritance overlay issue, but I also thought it should solve the cast type issue dynamically. I know I can use reinterpret_cast, but that doesn't guarantee the same type of security.

What should I use to check if my type is the same type? I could compare the two types, but I would have a problem when I want the type to output to its base. So how can I solve this?

+1


source to share


9 replies


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

The trick is that every class that you pass as a descriptor must 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;
}

      

+1


source


dynamic_cast

can only be used between inheritance-related classes. To convert a pointer to long or vice versa, you can use reinterpret_cast

. To check if a pointer is null you can assert(ptr != 0)

. However, it is generally not recommended to use reinterpret_cast

. Why do you need to convert a pointer to long?

Another option is to use union:



union  U { 
int* i_ptr_;
long l;
}

      

Again, the union is too rarely needed.

+2


source


Remember that on Windows 64 the pointer will have a 64-bit count, but it long

will still have a 32-bit count and your code will be corrupted. At the very least, you need to make a platform-based integer choice. I don't know if MSVC supports the uintptr_t

type specified in C99 for holding pointers; this is the best type to use if available.

For the rest, others have turned to why and why dynamic_cast

vs is reinterpret_cast

enough.

+1


source


dynamic_cast<>

is a cast intended to be used only on convertible types (in the polymorphic sense). Forcing a conversion pointer

to long

(the flag correctly suggests static_assert to ensure size compatibility) all pointer type information is lost. There is no way to implement a safe_reinterpret_cast<>

to get a pointer back: both the value and the 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;
}

      

It is not possible to determine the function()

type of the passed pointer and "down" apply it to the corresponding type, unless:

  • long is wrapped in an object with some type information.
  • the type itself is encoded in the reference object.

Both solutions are ugly and should be avoided as they are surrogates for RTTI.

0


source


You can use reinterpret_cast

to convert to integral type and back to pointer type. If the integral type is large enough to store the pointer value, then this conversion will not change the pointer value.

As others already say, there is no defined behavior for using dynamic_cast on a non-polymorphic class (except when you are upcasting, which is implicitly and ignored anyway), and also only works with pointers or references. Not by integral types.

Better to use ::intptr_t

found in various posix systems. You can use this type as your intermediate type to which you added.

As for your checking if the conversion is successful, you can use sizeof:

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

      

will fail at compile time if the conversion cannot be performed. Or keep using assert with this condition and it will indicate runtime instead.

Warning: This does not prevent you from casting T*

in intptr_t

back to U*

using a U of a type other than T. Thus, it only ensures that you will not change the pointer value if you added from T*

to intptr_t

and reverted to T*

. (Thanks to Nicola pointing out, you can expect some more protection).

0


source


reinterpret_cast is the correct translation to use here.

This is almost the only thing that can be done safely.

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

Note that the reinterpret_cast from pointer type to T is not specified. There are no guarantees about a value of type T, except that if you then convert it back to its original type, you will get the original value. Therefore, assuming you are not trying to do anything with an intermediate long cost in your case, reinterpret_cast is completely safe and portable.

Edit: Of course it won't help unless you know, in the second sheet, what the original type was. In this case, you are screwed. Long ones can in no way carry information about the type from which it was converted.

0


source


What you want to do sounds like a very bad and dangerous idea, but if you MUST do it (that is, you are running on a legacy system or on hardware that you know will never change), I would suggest wrap a pointer to some simple structure that contains two members: 1) a pointer to the void of your object instance and a string, enumeration, or some other unique identifier that will tell you what to distinguish the original void *. Here's an example of what I had in mind (note: I didn't bother checking this so there might be syntax errors):

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 (...) { ... }

}

      

0


source


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

0


source


Once you decide to overlap a long pointer, you've thrown type safety into the wind.

dynamic_cast is used to create and drop 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 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 cannot use dynamic cast for insertion from the derivation tree. You need reinterpret_cast or C-style for this.

-1


source







All Articles