嵌入式无RTTI场景下高效dynamic_cast实现的二进制优化问题
我正在开发一款面向嵌入式系统的库,因此无法使用包括dynamic_cast在内的任何RTTI特性。为了实现类似dynamic_cast的功能,我参考相关技术文章自行实现了RTTI的必要组件,部分实现细节有调整但核心思路一致。
初始实现代码
#include <iostream> constexpr uint32_t primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 }; #define DYNAMIC_POINTER_CAST_INIT(...) \ virtual uint32_t dynamicPointerCastObjectId() override \ { \ return DynamicPointerCastable<__VA_ARGS__>::dynamicPointerCastObjectId(); \ } \ \ using DynamicPointerCastable<__VA_ARGS__>::dynamicPointerCastClassId; \ using DynamicPointerCastable<__VA_ARGS__>::dynamicPointerCastClassPrimeId namespace { uint32_t numberOfDynamicPointerCastables = 0; template <typename... Bs> struct genSubClassId { static uint32_t value; }; template <typename B, typename... Bs> struct genSubClassId<B, Bs...> { static uint32_t value; }; template <typename B> struct genSubClassId<B> { static uint32_t value; }; template <> struct genSubClassId<> { static uint32_t value; }; template <typename B, typename... Bs> uint32_t genSubClassId<B, Bs...>::value = B::dynamicPointerCastClassId() * genSubClassId<Bs...>::value; template <typename B> uint32_t genSubClassId<B>::value = B::dynamicPointerCastClassId(); uint32_t genSubClassId<>::value = 1; } template <typename Sub, typename... Bases> class DynamicPointerCastable { private: static uint32_t subPrimeId; public: static uint32_t dynamicPointerCastClassPrimeId() { return subPrimeId; } static uint32_t dynamicPointerCastClassId() { return subPrimeId * genSubClassId<Bases...>::value; } virtual uint32_t dynamicPointerCastObjectId() { return DynamicPointerCastable<Sub, Bases...>::dynamicPointerCastClassId(); } }; template <typename T, typename Cast> Cast *dynamicCastableCast(T *obj) { if (obj->dynamicPointerCastObjectId() % Cast::dynamicPointerCastClassPrimeId() == 0) return (Cast *)obj; return nullptr; } template <typename Sub, typename... Bases> uint32_t DynamicPointerCastable<Sub, Bases...>::subPrimeId = primes[numberOfDynamicPointerCastables++];
核心实现思路
我设置了计数器numberOfDynamicPointerCastables作为素数数组primes(库内该数组共包含1000个素数)的索引,通过CRTP(奇异递归模板模式)在子类中实现DynamicPointerCastable的相关方法,dynamicCastableCast函数通过检查对象ID是否能被目标转换类型关联的素数整除,完成类型转换合法性校验。
使用示例
struct Sub1 : public Base, public DynamicPointerCastable<Sub1, Base> { DYNAMIC_POINTER_CAST_INIT(Sub1, Base); virtual std::string getName() { return "Sub1"; } }; struct SubGroup : public Base, public DynamicPointerCastable<SubGroup, Base> { DYNAMIC_POINTER_CAST_INIT(SubGroup, Base); virtual std::string getName() { return "SubGroup"; } }; struct Sub2 : public SubGroup, public DynamicPointerCastable<Sub2, SubGroup> { DYNAMIC_POINTER_CAST_INIT(Sub2, SubGroup); virtual std::string getName() { return "Sub2"; } }; int main() { Base* sub1 = new Sub1; Base* sub2 = new Sub2; std::cout << "Expected Sub1: " << dynamicCastableCast<Base, Sub1>(sub1)->getName() << std::endl; std::cout << "Expected nullptr: " << dynamicCastableCast<Base, Sub2>(sub1) << std::endl; std::cout << "Expected Sub2: " << dynamicCastableCast<Base, SubGroup>(sub2)->getName() << std::endl; std::cout << "Expected Sub2: " << dynamicCastableCast<Base, Sub2>(sub2)->getName() << std::endl; std::cout << "Expected nullptr: " << dynamicCastableCast<Base, Sub1>(sub2) << std::endl; }
待解决问题
目前我未能实现编译期计数,导致整个素数数组都会被编入二进制(我使用gcc-11,编译参数为-Os -ffunction-sections -fdata-sections -Wl,--gc-sections -DNDEBUG)。我想咨询以下问题:
- 如果实现了编译期类计数,编译器能否只将用到的素数编入二进制?
- 如果可以的话,如何实现编译期计数器?
- 是否有其他方案可以尽可能缩小二进制体积?
后续更新
我已经实现了一个元函数,在默认-ftemplate-depth=900的参数下最多可以生成第131个素数,131个素数基本可以满足需求,后续我会优化该实现降低模板深度。我也将测试其他方案的程序体积,目前的问题是prime<...>::atIndex()方法的编译耗时过长。
新实现代码
#include <iostream> template<size_t n, size_t i = 2> constexpr typename std::enable_if<!(i * i <= n), bool>::type isPrime() noexcept { return true; } template<size_t n, size_t i = 2> constexpr typename std::enable_if<i * i <= n, bool>::type isPrime() noexcept { return (n % i != 0) && isPrime<n, i + 1>(); } template<size_t i, size_t counter = 0, size_t k = 3, typename Enabled = void> struct prime; template<size_t i, size_t counter, size_t k> struct prime<i, counter, k, typename std::enable_if<isPrime<k>() && counter < i>::type> { static constexpr size_t atIndex() noexcept { return prime<i, counter + 1, k + 1>::atIndex(); } }; template<size_t i, size_t counter, size_t k> struct prime<i, counter, k, typename std::enable_if<!isPrime<k>() && counter < i>::type> { static constexpr size_t atIndex() noexcept { return prime<i, counter, k + 1>::atIndex(); } }; template<size_t i, size_t counter, size_t k> struct prime<i, counter, k, typename std::enable_if<isPrime<k>() && counter >= i>::type> { static constexpr size_t atIndex() noexcept { return k - 1; } }; template<size_t i, size_t counter, size_t k> struct prime<i, counter, k, typename std::enable_if<!isPrime<k>() && counter >= i>::type> { static constexpr size_t atIndex() noexcept { return k - 1; } }; template<int until, int step, template<int> typename op, int i = 0> struct constFor { constFor() { op<i>{}; constFor<until, step, op, i + step>{}; } }; template<int until, int step, template<int> typename op> struct constFor<until, step, op, until> { constFor() = default; }; template<int i> struct print { print() { std::cout << prime<i>::atIndex() << std::endl; } }; int main() { constFor<131, 1, print>{}; }
内容的提问来源于stack exchange,提问作者Gian Laager
相关产品推荐
相关产品推荐

