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.
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:
- A pointer to a (dynamically) allocated storage;
- A pointer to a "storage manager function":
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.
One of possible solutions is generating unique id for every type possibly stored in any
(I assume You know moreless how any
internally works). The code that can do it may look something like this:
struct id_gen{
static int &i(){
static int i = 0;
return i;
}
template<class T>
struct gen{
static int id() {
static int id = i()++;
return id;
}
};
};
With this implemented You can use the id of the type instead of RTTI typeinfo
to quickly check the type.
Notice the usage of static variables inside functions and static functions. This is done to avoid the problem of undefined order of static variable initialization.
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
.
来源:https://stackoverflow.com/questions/51361606/stdany-without-rtti-how-does-it-work