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
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);
}
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
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
}
}