#pragma once
#include <map>
#include <typeinfo>
#include <stdint.h>
#include <stddef.h>
#include <type_traits>
namespace panda {
namespace detail { namespace cast {
typedef std::map<intptr_t, ptrdiff_t> DynCastCacheMap;
template <class DERIVED, class BASE>
DynCastCacheMap& get_map () {
thread_local DynCastCacheMap* map;
if (!map) {
thread_local struct { DynCastCacheMap map; } wrp;
map = &wrp.map;
}
return *map;
}
constexpr const ptrdiff_t INCORRECT_PTRDIFF = PTRDIFF_MAX;
}}
template <class DERIVED_PTR, class BASE>
DERIVED_PTR dyn_cast (BASE* obj) {
using namespace detail::cast;
using DERIVED = typename std::remove_pointer<DERIVED_PTR>::type;
if (std::is_same<BASE,DERIVED>::value) return (DERIVED_PTR)((void*)obj);
if (!obj) return NULL;
intptr_t key = (intptr_t)typeid(*obj).name();
auto& map = get_map<DERIVED,BASE>();
//auto& map = DynCastCache<DERIVED,BASE>::map;
DynCastCacheMap::iterator it = map.find(key);
if (it != map.end())
return it->second != INCORRECT_PTRDIFF ? reinterpret_cast<DERIVED*>((char*)obj - it->second) : NULL;
DERIVED* ret = dynamic_cast<DERIVED*>(obj);
if (ret) map[key] = (char*)obj - (char*)ret;
else map[key] = INCORRECT_PTRDIFF;
return ret;
}
template <class DERIVED_REF, class BASE>
DERIVED_REF dyn_cast (BASE& obj) {
using namespace detail::cast;
using DERIVED = typename std::remove_reference<DERIVED_REF>::type;
if (std::is_same<BASE,DERIVED>::value) return reinterpret_cast<DERIVED_REF>(obj);
intptr_t key = (intptr_t)typeid(obj).name();
auto& map = get_map<DERIVED,BASE>();
DynCastCacheMap::iterator it = map.find(key);
if (it != map.end() && it->second != INCORRECT_PTRDIFF)
return *(reinterpret_cast<DERIVED*>((char*)&obj - it->second));
// dont cache fails, as exceptions are much slower than dynamic_cast, let it always fall here
DERIVED& ret = dynamic_cast<DERIVED&>(obj);
map[key] = (char*)&obj - (char*)&ret;
return ret;
}
}