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 cl
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(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 void type_id() {}
// now we can use a map or a vector.
std::vector> vec;
template
void insertToMyVector(T* obj) {
vec.emplace_back(type_id, obj);
}
template
T* getObj(int index) {
auto item = vec[index];
return static_cast(item.first == &type_id ? item.second : nullptr);
}
// ...
int main () {
auto foo = new Foo;
insertToMyVector(foo);
auto maybeFoo = getObj(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
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*>(base);
if (derived) {
int i = derived->get();
// derived is valid here, and we can safely access the int
}
}