Date: Tue, 16 Apr 2024 12:08:59 +0100
On Tue, Apr 16, 2024 at 10:26 AM Sebastian Wittmeier wrote:
>
> Yes, at runtime in can be detected, but not at compile-time.
>
> dynamic_cast can (at least sometimes or always) detect this at compile-time.
>
> <source>:12:5: error: ambiguous conversion from derived class 'D' to base class 'A':
> class D -> B -> A
> class D -> C -> A
> 12 | dynamic_cast<A&>(d)
> | ^~~~~~~~~~~~~~~~~~~
> 1 error generated.
So we won't have this compile-time error when using std::any::base<T>.
Instead we get, at runtime, a nullptr or an exception (i.e.
'std::bad_any_cast').
In fact, thinking about it a little bit more now, if we're dealing
with a polymorphic type, then the implementation could really be as
simple as:
template<typename T>
Base *any::base(void) noexcept
{
#ifdef _MSC_VER
return __RTDynamicCast( this->p_value, 0,
this->p_type_info, &typeid(Base), false );
#else
return __dynamic_cast( this->p_value, this->p_type_info,
&typeid(Base), -1 /* no hint */ );
#endif
}
The only thing that complicates this a tiny bit is, that on the
Microsoft compiler, the polymorphic object's pointer to its vtable
might not be at the beginning of the object -- that's why
__RTDynamicCast has an extra argument to tell it where the vtable
pointer is (that's the 0 you see in the above code snippet).
Therefore, on the Microsoft compiler, when we only have the type_info,
we need to be able to find the vtable pointer inside the object -- but
I think this is possible by utilising the type_info to retrieve the
RTTICompleteObjectLocator.
On Microsoft, you need a ThrowInfo struct to throw an exception, but
I'm pretty sure that Microsoft can utilise a type_info to get a
ThrowInfo (or a RTTICompleteObjectLocator).
On the Microsoft compiler, we really need to test it with the following class:
class Base { int i, j; }
class Derived : public Base { virtual ~Derived(void){} };
An object of type 'Derived' will be laid out in memory by the
Microsoft compiler as follows:
- - - 4 bytes = i
- - - 4 bytes = j
- - - 8 bytes = pointer to vtable
So having a type-erased pointer to a Derived, and typeid(Derived), we
need to calculate that the vtable pointer is at ((char*)obj + 8) -- so
therefore we need somehow some way to get this offset from the
type_info.
But everything I've written here only covers polymorphic objects! I
think the best thing to do as a 'proof of concept' is to utilise
exception handling to pull this off, then hand it over to the compiler
vendors and say "Implement this more efficiently if you want to, but
we can at least prove to you that it's possible with your compiler
without a ABI break".
>
> Yes, at runtime in can be detected, but not at compile-time.
>
> dynamic_cast can (at least sometimes or always) detect this at compile-time.
>
> <source>:12:5: error: ambiguous conversion from derived class 'D' to base class 'A':
> class D -> B -> A
> class D -> C -> A
> 12 | dynamic_cast<A&>(d)
> | ^~~~~~~~~~~~~~~~~~~
> 1 error generated.
So we won't have this compile-time error when using std::any::base<T>.
Instead we get, at runtime, a nullptr or an exception (i.e.
'std::bad_any_cast').
In fact, thinking about it a little bit more now, if we're dealing
with a polymorphic type, then the implementation could really be as
simple as:
template<typename T>
Base *any::base(void) noexcept
{
#ifdef _MSC_VER
return __RTDynamicCast( this->p_value, 0,
this->p_type_info, &typeid(Base), false );
#else
return __dynamic_cast( this->p_value, this->p_type_info,
&typeid(Base), -1 /* no hint */ );
#endif
}
The only thing that complicates this a tiny bit is, that on the
Microsoft compiler, the polymorphic object's pointer to its vtable
might not be at the beginning of the object -- that's why
__RTDynamicCast has an extra argument to tell it where the vtable
pointer is (that's the 0 you see in the above code snippet).
Therefore, on the Microsoft compiler, when we only have the type_info,
we need to be able to find the vtable pointer inside the object -- but
I think this is possible by utilising the type_info to retrieve the
RTTICompleteObjectLocator.
On Microsoft, you need a ThrowInfo struct to throw an exception, but
I'm pretty sure that Microsoft can utilise a type_info to get a
ThrowInfo (or a RTTICompleteObjectLocator).
On the Microsoft compiler, we really need to test it with the following class:
class Base { int i, j; }
class Derived : public Base { virtual ~Derived(void){} };
An object of type 'Derived' will be laid out in memory by the
Microsoft compiler as follows:
- - - 4 bytes = i
- - - 4 bytes = j
- - - 8 bytes = pointer to vtable
So having a type-erased pointer to a Derived, and typeid(Derived), we
need to calculate that the vtable pointer is at ((char*)obj + 8) -- so
therefore we need somehow some way to get this offset from the
type_info.
But everything I've written here only covers polymorphic objects! I
think the best thing to do as a 'proof of concept' is to utilise
exception handling to pull this off, then hand it over to the compiler
vendors and say "Implement this more efficiently if you want to, but
we can at least prove to you that it's possible with your compiler
without a ABI break".
Received on 2024-04-16 11:09:11