Jonathan Mee
Jonathan Mee

Reputation: 38919

Run-Time Checking of a Cast from a void*

Say that I have a void* containing a pointer to an unknown class. I want to use dynamic_cast to do run-time checking on the type of class I actually have. For example:

class Foo {};

void* bar = new Foo;

If I attempt to do dynamic_cast<Foo*>(bar) I get:

'void *': invalid expression type for dynamic_cast

However I need dynamic_cast because in my actual situation I'm not sure that bar is in fact a Foo*.

I've read here that one solution to this is to create a base class for all objects that bar could contain, reinterpret_cast to a pointer to that base class, and then try to dynamic_cast from that object pointer to Foo.

This is difficult for me because the objects that may be stored in bar are not all under my control. (And cause trying to recreate Java gives me heartburn.) Is there another way to do this?

Upvotes: 4

Views: 2030

Answers (3)

Guillaume Racicot
Guillaume Racicot

Reputation: 41770

dynamic_cast is use to cast down a polymorphic object to a class that has the type of the object you're trying to cast as it's parent.

void* is completely different from that. with a pointer to void, you are literally stripping every type information away.

dynamic_cast know that there's a base class and can do type checking through RTTI.

When you cast down a void pointer, you're saying to the compiler: "yeah you know this place in the memory? well, use it as this type" and if the memory is invalid, UB is invoked.

you have three choices here.

Option 1 Use an interface. Well, a polymorphic base class is the only way to do a dynamic_cast. There is no other way, no hacks, it's the only way. Simple as that.

struct Base { virtual ~Base() = default; };

struct Derived : Base {};

// ...

void test (Base* base) {
    auto derived = dynamic_cast<Derived*>(base);

    if (derived) {
        // derived is valid here.
    }
}

Option 2 Identify the type with the pointer I use a method to have a unique identifier per type and use the identifier to validate the cast. Done without any RTTI

using type_id_t = void(*)();
template <typename T> void type_id() {}

// now we can use a map or a vector.
std::vector<std::pair<type_id_t, void*>> vec;

template<typename T>
void insertToMyVector(T* obj) {
    vec.emplace_back(type_id<T>, obj);
}

template<typename T>
T* getObj(int index) {
    auto item = vec[index];

    return static_cast<T*>(item.first == &type_id<T> ? item.second : nullptr);
}

// ...

int main () {
    auto foo = new Foo;

    insertToMyVector(foo);

    auto maybeFoo = getObj<Foo>(0);

    if (maybeFoo) {
        // you have a valid Foo here
    }
}

Option 3 Generate derived class for any type This one is quite useful as it can hold any type while keeping type safety. I look like solution 1 but offer more flexibility. The trick it to generate a derived class for any type using templates. The advantage is you can hold any type, but may complexify you cade a bit.

struct Base { virtual ~Base() = default; };

template<typename T>
struct Derived : Base {
    Derived(T&& obj) : _obj{std::move(obj)} {}
    Derived(const T& obj) : _obj{obj} {}

    T& get() {
        return _obj;
    }

    const T& get() const {
        return _obj;
    }

private:
    T _obj;
};

// ...

void test (Base* base) {
    auto derived = dynamic_cast<Derived<int>*>(base);

    if (derived) {
        int i = derived->get();
        // derived is valid here, and we can safely access the int
    }
}

Upvotes: 1

Richard Hodges
Richard Hodges

Reputation: 69882

As I understand it you want a polymorphic object but no common base class.

There is already a fairly standard idiom for this - it's called boost::any.

A boost::any carries your object plus some type information. The interface allows you to query the type and to attempt to cast the any to the type you're looking for.

http://www.boost.org/doc/libs/1_59_0/doc/html/any.html

Upvotes: 0

Richard Dally
Richard Dally

Reputation: 1450

To ensure dynamic_cast compiles and works, you should create an abstract or interface class with a virtual method.

#include <cassert>

class Bar
{
public:
    Bar() = default;
     virtual ~Bar() = default;
};

class Foo : public Bar
{
public:
    Foo() = default;
    virtual ~Foo() = default;
};

int main()
{
    Bar* bar = new Foo;
    Foo* foo = dynamic_cast<Foo*>(bar);
    assert(foo != nullptr);
}

Upvotes: 0

Related Questions