Is there a way to determine from two const ::std::type_info
objects, let's name them B
and D
if the type described by D is derived from type B?
I ask because I want to erase the type of an object I get but later on be able to check if it can be safely promoted.
void* data;
const ::std::type_info* D;
template<typename D>
void store(D&& object)
{
D = &typeid(object);
data = ::std::addressof(object);
}
template<typename B>
B& load()
{
// if(typeid(B) != (*D)) throw ::std::bad_cast{};
return *reinterpret_cast<B*>(data); // <- also problematic
}
I want to be able to use it like this:
class Base {};
class Derived : Base {};
Derived d;
store(d);
// ....
load<Base>();
Thus it is not suitable to just use an equality compare for the typeids. I am pretty sure this could be possible in a similar way that dynamic_cast can figure this out. What I want is that in every case where D&
could be assigned to B&
allowing B as the type argument of load()
- without knowing D
at that time.
I found a way to let the compiler and interal mechanism to figure it out for me. I don't have a problem with cross compiling, in that case ::std::type_info
isn't consistent, either.
typedef void (*throw_op)(void*);
throw_op dataThrow;
template<typename T>
[[ noreturn ]] void throwing(void* data)
{
throw static_cast<T*>(data);
}
[[ noreturn ]] void bad_cast()
{
throw ::std::bad_cast{};
}
template<typename B>
B& poly_load()
{
if(data == nullptr)
bad_cast();
try {
dataThrow(data);
} catch (B* ptr) {
return *ptr;
} catch (...) {
bad_cast();
}
}
All you have to do is add the following line to the store operation:
dataThrow = throwing<D>;
How does this work? It takes advantages of exceptions and how they are caught. Note here that this makes poly_load
?much? slower than the simple load
function, thus I'll keep the both around.
C++ says that when an exception of type D*
is being thrown you can catch that exception with a catch-clause B*
where B
is any ancestor of D
.
Minimal example:
struct Base {
virtual ~Base() {}
virtual void foo() = 0;
};
struct Derived : public virtual Base {
void foo() override {
::std::cout << "Hello from Derived" << ::std::endl;
}
};
int main() {
Derived d{};
store(d);
// .....
poly_load<Base>().foo();
}