If I want to use std::any
I can use it with RTTI switched off. The following example compiles and runs as expected also with -fno-rtti
with gcc.
int main() { std::any x; x=9.9; std::cout << std::any_cast<double>(x) << std::endl; }
But how std::any
stores the type information? As I see, if I call std::any_cast
with the "wrong" type I got std::bad_any_cast
exception as expected.
How is that realized or is this maybe only a gcc feature?
I found that boost::any
did also not need RTTI, but I found also not how that is solved. Does boost::any need RTTI?.
Digging into the STL header itself gives me no answer. That code is nearly unreadable to me.
Since this function is specific to a given type, you don't need RTTI to perform the operations required by std::any .
std::any Class in C++ any is one of the newest features of C++17 that provides a type-safe container to store single value of any type. In layman's terms, it is a container which allows one to store any value in it without worrying about the type safety.
TL;DR; std::any
holds a pointer to a static member function of a templated class. This function can perform many operations and is specific to a given type since the actual instance of the function depends on the template arguments of the class.
The implementation of std::any
in libstdc++ is not that complex, you can have a look at it:
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/any
Basically, std::any
holds two things:
void (*_M_manager)(_Op, const any*, _Arg*);
When you construct or assign a new std::any
with an object of type T
, _M_manager
points to a function specific to the type T
(which is actually a static member function of class specific to T
):
template <typename _ValueType, typename _Tp = _Decay<_ValueType>, typename _Mgr = _Manager<_Tp>, // <-- Class specific to T. __any_constructible_t<_Tp, _ValueType&&> = true, enable_if_t<!__is_in_place_type<_Tp>::value, bool> = true> any(_ValueType&& __value) : _M_manager(&_Mgr::_S_manage) { /* ... */ }
Since this function is specific to a given type, you don't need RTTI to perform the operations required by std::any
.
Furthermore, it is easy to check that you are casting to the right type within std::any_cast
. Here is the core of the gcc implementation of std::any_cast
:
template<typename _Tp> void* __any_caster(const any* __any) { if constexpr (is_copy_constructible_v<decay_t<_Tp>>) { if (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage) { any::_Arg __arg; __any->_M_manager(any::_Op_access, __any, &__arg); return __arg._M_obj; } } return nullptr; }
You can see that it is simply an equality check between the stored function inside the object you are trying to cast (_any->_M_manager
) and the manager function of the type you want to cast to (&any::_Manager<decay_t<_Tp>>::_S_manage
).
The class _Manager<_Tp>
is actually an alias to either _Manager_internal<_Tp>
or _Manager_external<_Tp>
depending on _Tp
. This class is also used for allocation / construction of object for the std::any
class.
Manual implementation of a limited RTTI is not that hard. You're gonna need static generic functions. That much I can say without providing a complete implementation. here is one possibility:
class meta{ static auto id(){ static std::atomic<std::size_t> nextid{}; return ++nextid;//globally unique }; std::size_t mid=0;//per instance type id public: template<typename T> meta(T&&){ static const std::size_t tid{id()};//classwide unique mid=tid; }; meta(meta const&)=default; meta(meta&&)=default; meta():mid{}{}; template<typename T> auto is_a(T&& obj){return mid==meta{obj}.mid;}; };
This is my first observation; far from ideal, missing many details. One may use one instance of meta
as a none-static data member of his supposed implementation of std::any
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With