Reputation: 38919
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
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
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
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