Boost type_index库的实现细节

来源:互联网 发布:我的同桌是极品网络剧 编辑:程序博客网 时间:2024/05/27 06:51

Boost type_index库的实现细节

前面,有一章节,介绍了boost type_index库中的基本使用方式,这章来介绍boost type_index库的具体的实现细节。

简单回顾一下boost type_index库的功能。主要有如下功能:

  • 使用typeindex::type_id来获取某种静态类型不带cvr修饰的类型信息,比如:typeindex::type_id().pretty_name(),他返回的是int,而不是const int
  • 使用typeindex::type_id_with_cvr来获取某种某种静态类型信息。比如:typeindex::type_id().pretty_name(),他返回的是int const。
  • 使用typeindex::type_id_runtime(const T &t)来,获取t的运行时类型信息。
  • 我们可以通过上面描述的个模板函数来创建type_index实例,我们可以调用type_index类型实例的一些方法来获取类型的一些相关名称。比如:
    • 调用type_index::raw_name()来获取编译器了解的类型名称
    • 调用type_index::pretty_name()来获取用户可以阅读的名称
    • 调用type_index::name()来获取类型名称。(其实,从实现角度上来讲,这个方法,可能有些多余)

接下来会介绍boost type_index库的具体的实现细节。

下面的三个模板函数和type_index类型定义都是位于:boost/type_index.hpp文件中

type_id模板函数的实现

template <typename T>type_index type_id(){    return type_index::type_id<T>();}

其实他就是一个辅助方法,他内部是直接调用type_index自身的type_id方法来产生type_index实例的。关于type_index类型,后面的相关章节。

type_id_with_cvr模板函数的实现

template <typename T>type_index type_id_with_cvr(){    return type_index::type_id_with_cvr<T>();}

他就是一个辅助方法,他内部是直接调用type_index自身的type_id_with_cvr方法来产生type_index实例的。关于type_index类型,后面的相关章节。

type_id_runtime模板函数的实现

template <typename T>type_index type_id_runtime(const T &t){    return type_index::type_id_runtime(t);}

他就是一个辅助方法,他内部是直接调用type_index自身的type_id_runtime方法来产生type_index实例的。关于type_index类型,后面的相关章节。

type_index类型

他是一个typedef定义的类型。具体的定义部分,参看下面的代码:

//其实这儿还有用户自己实现type_index库的情况,这种情况,我们不进行讨论,我们只看boost的自带的实现方式...#elif (!defined(BOOST_NO_RTTI) && !defined(BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY)) || defined(BOOST_MSVC)    typedef boost::typeindex::stl_type_index type_index;#else     typedef boost::typeindex::ctti_type_index type_index;#endif

从上面的条件宏语句中,我们可以很容易看出:

如果当前编译器vc+,或者当rtti开启的时候,type_index实际上是boost::typeindex::stl_type_index的别名。至于为什么会这样来设置,请看后面的stl_type_index实现部分。

如果当前编译器,不是vc++,并且rtti被禁用的时候或者BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY被定义的时候,type_index实际上是boost::typeindex::ctti_type_index的别名

下面会介绍boost::typeindex::stl_type_index和boost::typeindex::ctti_type_index的详细细节。

这面所描述的两个类所在的hpp文件,他是有选择的被包含在boost/type_index.hpp中文件中的。详细代码请看:

// 这儿是用户自己实现type_index库的情况,我们不进行讨论#if defined(BOOST_TYPE_INDEX_USER_TYPEINDEX)#   include BOOST_TYPE_INDEX_USER_TYPEINDEX#   ifdef BOOST_HAS_PRAGMA_DETECT_MISMATCH#       pragma detect_mismatch( "boost__type_index__abi", "user defined type_index class is used: " BOOST_STRINGIZE(BOOST_TYPE_INDEX_USER_TYPEINDEX))#   endif// 这儿是rtti被禁用,或者是vc++编译器的情况#elif (!defined(BOOST_NO_RTTI) && !defined(BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY)) || defined(BOOST_MSVC)//下面包含的hpp文件,就是boost::typeindex::stl_type_index的声明和实现部分#   include <boost/type_index/stl_type_index.hpp>// 当前编译器为vc++编译器,并且rtti被关闭的时候#   if defined(BOOST_NO_RTTI) || defined(BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY)// 内部定义了BOOST_TYPE_INDEX_REGISTER_CLASS宏,这个宏仅的实现细节,请参照后面章节#       include <boost/type_index/detail/stl_register_class.hpp>#       ifdef BOOST_HAS_PRAGMA_DETECT_MISMATCH#           pragma detect_mismatch( "boost__type_index__abi", "RTTI is off - typeid() is used only for templates")#       endif#   else#       ifdef BOOST_HAS_PRAGMA_DETECT_MISMATCH#           pragma detect_mismatch( "boost__type_index__abi", "RTTI is used")#       endif#   endif#else// 这儿就是当前编译器不是vc++编译器,并且rtti被禁用的情况,或者BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY被定义的时候禁止此分支// 下面的头文件是boost::typeindex::ctti_type_index类型定义的地方#   include <boost/type_index/ctti_type_index.hpp>// 他是boost::typeindex::ctti_type_index对应的BOOST_TYPE_INDEX_REGISTER_CLASS宏的定义部分#   include <boost/type_index/detail/ctti_register_class.hpp>#   ifdef BOOST_HAS_PRAGMA_DETECT_MISMATCH#       pragma detect_mismatch( "boost__type_index__abi", "RTTI is off - using CTTI")#   endif#endif

从上面代码可以看出,stl_type_index类的定义部分位于:boost/type_index/stl_type_index.hpp中,ctti_type_index类的定义部分位于:boost/type_index/ctti_type_index.hpp中。

type_index_facade

这个类是下面所要介绍的stl_type_index和ctti_type_index的门面类。他实现门面的方式是通过使用模板作为上面两个类的积累形式。

类简单原型如下:

// Derived 是派生类类型// TypeInfo 是派生类内部保存类型信息的类template <class Derived, class TypeInfo>class type_index_facade{public:    typedef TypeInfo type_info_t;    // 内部直接调用子类的方法    // 这儿就是利用模板实现的静态多态,这种技术在微软的wtl库中被大量使用       const char *name() const BOOST_NOEXCEPT    {        return derived().raw_name();    }    // 这儿调用的是派生类的name方法,他的两个子类都会重新定义pretty_name方法的    string pretty_name() const BOOST_NOEXCEPT    {        return derived().name();    }    //直接比较的是类型名称    bool equal(const Derived &rhs) const BOOST_NOEXCEPT    {        const char *left = derived().raw_name();        const char *right = rhs.raw_name();        return (left == right) || (0 == strcmp(left, right));    }    bool before(const Derived &rhs) const BOOST_NOEXCEPT    {        const char *left = derived().raw_name();        const char *right = rhs.raw_name();        return (left != right) || (strcmp(left, right) < 0);        }    // 获取hash值,注意内部实现依赖于,一个提前声明的模板方法    // hash_range,所以在实际使用这个hash_code方法的时候,必须要包含boost/functional/hash.hpp,或者实现自己的template<typename Iter> std::size_t hash_range(Iter begin, Iter end)。否则程序链接阶段会出现未定义问题。    std::size_t hash_code() const BOOST_NOEXCEPT    {        return hash_range(derived().name(), derived().name() + strlen(name()));    }    ...private:    const Derived& derived() const BOOST_NOEXCEPT    {        return *static_cast<const Derived*>(this);    }};

从上面可以看到,他只提供以下几种类型方法:

  1. name方法,pretty_name方法
  2. before方法,equal方法
  3. hash_code方法

其实他提供的三类方法,本质上将只是为派生类提供,比较功能,输出流功能和获取hash值功能,具体实现看下面代码:

template <class Derived, class TypeInfo>inline bool operator==(const type_index_facade<Derived, TypeInfo> &lhs, const type_index_facade<Derived, TypeInfo> &rhs) BOOST_NOEXCEPT{    // 将派生类类型传进来,这儿进行的static_cast就是静态转换    // 对于对于上面描述的两个类型:stl_type_index和ctti_type_index来讲,他们最终调用的是派生类的raw_name方法    return static_cast<const Derived&>(lhs).equal(static_cast<const Derived&>(rhs));}template <class Derived, class TypeInfo>inline bool operator<(const type_index_facade<Derived, TypeInfo> &lhs, const type_index_facade<Derived, TypeInfo> &rhs) BOOST_NOEXCEPT{    return static_cast<const Derived&>(lhs).before(static_cast<const Derived&>(rhs));}// 下面还要重载剩余的4从关系运算符,其实实现原理和stl 的utility库中的那个重载关系符实现原理是一样的,比如:template <class Derived, class TypeInfo>inline bool operator!=(const type_index_facade<Derived, TypeInfo> &lhs, const type_index_facade<Derived, TypeInfo> &rhs) BOOST_NOEXCEPT{    return !(lhs == rhs);}template <class Derived, class TypeInfo>inline bool operator>(const type_index_facade<Derived, TypeInfo> &lhs, const type_index_facade<Derived, TypeInfo> &rhs) BOOST_NOEXCEPT{    return rhs < lhs;}// 剩余的请参考boost/type_index/type_index_facade.hpp文件.(其实给出上面的例子,并且告诉你原理,你应该能够自己实现剩余的2个重载关系运算符函数)...// 还重载了TypeInfo和派生类的关系运算符。// 有一点非常非常重要,只有派生类支持使用TypeInfo来进行构造的时候,那么从才能进行TypeInfo和派生类直接进行比较,实现原理如下:template <class Derived, class TypeInfo>inline bool operator==(const TypeInfo &lhs, const type_index_facade<Derived, TypeInfo> &rhs) BOOST_NOEXCEPT{    return Derived(lhs) == rhs;}template <class Derived, class TypeInfo>inline bool operator==(const type_index_facade<Derived, TypeInfo> &lhs, const TypeInfo &rhs) BOOST_NOEXCEPT{    return lhs == Derived(rhs);}//剩余的关系运算符实现原理如上,具体请参考boost/type_index/type_index_facade.hpp文件。...// 重载了输出流运算符template <typename CharT, typename Trait, class Derived, class TypeInfo>inline std::basic_ostream<CharT, Trait> &operator<<(std::basic_ostream<CharT, Trait>& ostr, const type_index_facade<Derived, TypeInfo> &rhs) BOOST_NOEXCEPT{    ostr << static_cast<const Derived&>(rhs).pretty_name();    return ostr;}// 实现了对这个类的hash_code算法template <class Derived, class TypeInfo>inline std::size_t hash_code(const type_index_facade<Derived, TypeInfo>& rhs) BOOST_NOEXCEPT{    return static_cast<const Derived&>(rhs).hash_code();}

到此为止,这个type_index_facade功能和实际原理也完全展示出来了,下面在来总结一下:

  1. 实现对于自身的 关系运算符,依赖于派生类的raw_name方法,这个方法在type_index_facade中没有实现,那么在基类中一定要实现。
  2. 还支持TypeInfo和type_index_facade进行比较,前提是派生类一定要支持使用TypeInfo来直接进行构造
  3. 还提供了对于这个类实例取hash_code。

其实利用模板将派生类的类型传过来,并且实现派生类对于常见的流运算符,关系运算符,以及hash值。这种技术完全叶适用于其他类的

并且利用这种技术,我们还可以再某些方法中来调用某些子类的方法,这样的技术有时可以替代多态。并且使用这种技术,我们还可以实现内联优化,可以显著提供程序性能(对于虚函数是不能实现内联优化的)。

stl_type_index

这个类的实现,依赖于type_id运算符。这个类的type_id_runtime方法依赖于rtti信息(先提前说一下,其实这个类就是std::type_info的简单包装类)。

正如在boost/type_index.hpp中看到的那样,对于vc++编译器,他总是使用stl_type_index作为type_index所引用的类型。其实他这样设置也是有原因的,因为vc++编译器的rtti特性,不管是否启用,他的typeid运算符都不会失效的。所以他才,这样设置。但是当rtti特性被禁用的时候,他如果不进行额外操作的话,那么他是无法获取运行时信息的。所以他才会在开始的头文件中,有额外包含:boost/type_index/detail/stl_register_class.hpp文件。

这个类还是比较小的,将这个类的实现写在下面,里面会有详细的注释的。

namespace boost { namespace typeindex{class stl_type_index : type_index_facade<stl_type_index, // 这儿的条件宏是为了处理某些编译器的type_info实现没有放在std命名空间的情况。基本上现在都支持标准了#ifdef BOOST_NO_STD_TYPEINFOtype_info#elsestd::type_info#endif>{#ifdef BOOST_NO_STD_TYPEINFO    typedef type_info type_info_t;      //这时,表明type_info不是位于std命名空间中,而是位于全局命名空间中#else    typedef std::type_info type_info_t;#endif    // 构造器    // 自定义默认构造器,默认包含void类型信息    inline stl_type_index() BOOST_NOEXCEPT : data_(&typeid(void)) {}    // 支持使用type_info_t来进行构造,对于想支持type_info_t和stl_type_index进行比较的话,那么必须要支持这个构造器。    // 当然对于这个类设计来讲,本来就应该有这个构造器    inline stl_type_index(const type_info_t &data) BOOST_NOEXCEPT : data_(&data) {}    // 将内部包装的数据返回    inline const type_info_t &type_info() const BOOST_NOEXCEPT    {        return *data_;    }    // 输出类型名称    // 这儿输出的信息需要进行说明一下:    // 对于vc++编译器来讲,这儿输出的名称就是可读的名称    // 对于其他编译器来讲,这儿输出的名称应该是 编译器能够认识的名称,对于人来讲是没有可读性的,并且对于各个编译器的输出内容也不相同。这个时候,其实就相当于调用raw_name方法。    // 因为他输出内容的不固定性,所以请根据需求调用raw_name方法和pretty_name方法,不应该直接调用这个方法。当然,只是个人建议。    inline const char * name() const BOOST_NOEXCEPT    {        return data_->name();    }    // 输出编译器可以认识的名称    inline const char * raw_name() const BOOST_NOEXCEPT    {    // 从下面的宏可以看出微软编译器就是与众不同。呵呵!    #ifdef _MSC_VER        return data_->raw_name();    #else        return data_->name();    #endif    }    // 输出人可以阅读的类型名称    // 其实内部实现是非常有意思,是一种模板的妙用。    // 实现细节,先说明一下:    // 因为type_id他只能获取某种类(原始类型)的具体类型,他不能保存某种类类型的cvr特性。但是我们的需求是获取具体类的带有cvr(const-volatile-reference)特性的类型可读名称。boost库的解决方案是:将带有cvr的类型设置成某个模板的参数,然后利用typeid获取这个特例化模板类的type_info。    // 然后利用编译器提供的对于某种编译器识别类型名称进行解码来得到原始类型名称(就是源码中写的人可以直接阅读的类型名称)。    // 然后中上面获取的可读名称中,提取出具体的模板参数信息,就可以获取我们的可读的带有cvr修饰的类型名称了。    // 你可以单步调试一下,就知道上面的逻辑了:    // 只要在你的应用源码这样:    // #include <boost/type_index/st_type_index.hpp>    // #include <iostream>    // using namespace boost;    // using namespace std;    //    // int main(int argc, char **argv)    // {    //    cout << typeindex::stl_type_index::type_id_with_cvr<const int&>().pretty_name() << endl; //单步运行就可以发现他的运行逻辑了         // return 0;    // }    inline std::string pretty_name() const BOOST_NOEXCEPT    {        //其实这个静态名称是typeid(boost::typeindex::detail::cvr_saver<const int&>())可读名称的前半部        static const char cvr_save_name[] = "boost::typeindex::detail::cvr_saver<";        static const int len = std::strlen(cvr_save_name);        //demangled_name实现内部有意思(这儿就是真正解码部分,实现非常简单):        //对于gnu编译器的话,他的内部函数demangle_alloc执行真正解码操作(利用各个编译器的真正解码方法进行解码)        //而对于msvc编译器的话,因为data_->name()返回的就是实际名称,他会将实际名称直接        //赋值给scoped_demangled_name的内部成员就完事了        // 具体的实现,请看:boost/core/demangle.hpp,利用的是资源分配即实例化技术,并且在类实例销毁时,资源也销毁        boost::core::scoped_demangled_name demangled_name(data_->name());        const char * begin = demangled_name.get();        const char * end = begin + std::strlen(demangled_name.get());        // 如果外面传进来的类型带有cvr修饰的话,那么下面这个if肯定肯定成立,如果成立的话,那么begin就是cvr_save_name在原来begin中的位置        if (NULL != (begin = std::strstr(begin, cvr_save_name)))        {        // ==========其实,下面就是提取模板参数的部分===============            //跳到模板参数所在的位置            begin = begin + len;            //去除左侧空格            while ('\0' != *begin && ' ' == *begin) ++ begin;            //定位到模板参数最后一个位置            end = std::strchr(begin, '>');            -- end;            //去除末尾的空格            while (begin != end && ' ' == *end) -- end;            ++ end; //定位到结束位置            return std::string(begin, end);        }        return demangled_name.get();    }    // 获取hash_code值    // 隐藏了父类实现    // 其实,他不写也是可以的,因为基类就是安装这个逻辑来实现的,但是boost库中,他自己实现了。    inline std::size_t hash_code() const BOOST_NOEXCEPT    {        return hash_range(data_->name(), data_->name() + strlen(data_->name()));    }    // 隐藏父类实现,当然这儿的实现比父类实现要更加高效    inline bool equal(const stl_type_index &rhs) const BOOST_NOEXCEPT    {        return *data_ == *rhs.data_;    }    // 隐藏父类实现    inline bool before(const stl_type_index &rhs) const BOOST_NOEXCEPT    {        return data_->before(*(rhs.data_));    }    // ======================三个工厂方法,是非常重要的========================      // 获取是不带cvr修饰的T的类型信息    // 实现原理:    // 利用模板元编程技术来获取不带cvr修饰的类型。以后有时间,在写一下关于boost mpl库的实现细节。    // 然后使用typeid(不带cvr类型)来进行构造stl_type_index    template <typename T>    inline static stl_type_index type_id() BOOST_NOEXCEPT    {        //首先移除cvr特性        typedef typename boost::remove_reference<T>::type no_reference_t;        typedef typename boost::remove_cv<no_reference_t>::type no_cvr_t;        //获取移除类型后的类型信息        //还有隐藏的一步,隐式使用stl_type_index来进行构造        return typeid(no_cvr_t);    }    // 获取带有cvr修饰的类型信息    // 实现细节:    //   利用模板元编程实现,只要真正的类型T带有cvr修饰的话,那么返回的就是boost::type_index::detail::cvr_saver<T>的类型信息,否则就是原来的T的类型信息。    //   cvr_saver他其实就是一个空的类,定义为:    // namespace boost{ namespace typeindex { namepsace detail{    //    template <class T> class cvr_saver{};    //}}}    template <typename T>    inline static stl_type_index type_id_with_cvr() BOOST_NOEXCEPT    {        typedef typename boost::mpl::if_<boost::mpl::or_<boost::is_reference<T>,            boost::is_const<T>, boost::is_volatile<T> >, detail::cvr_saver<T>, T>::type type;        return typeid(type);    }    // 获取运行时t的真正信息    template <typename T>    inline static stl_type_index type_id_runtime(const T &t) BOOST_NOEXCEPT    {    // 当使用vc编时,这种情况可能会方法    #ifdef BOOST_NO_RTTI        // 这个方法是当rtti关闭的时候,BOOST_TYPE_INDEX_REIGISTER_CLASS宏定义的,在下面的章节,会介绍,其实是非常简单,其实就是定义一个虚方法,详情见下面。        return t.boost_type_index_type_id_runtime_();    #else        return typeid(t);    #endif    }private:    const type_info_t * const data_; // 里面包含了类型信息};}}

总结:

对于stl_type_index类的本质实现,其实这个类就是对std::type_info进行包装。

简单回顾一下,stl_type_index主要提供了如下几个功能:

  1. 因为他继承type_index_facade类,所以他支持自身的比较操作,以及和std::type_info的比较操作。
  2. 三个返回类型名称方法:
    • name方法,他直接调用的是data_->name()方法,因为std::type_info对于name()实现的不同,所以不建议直接使用这个方法,而应该根据明确目的来使用下面两个具体方法。
    • raw_name()方法,输出编译器能够认识的方法。
    • pretty_name()方法,输出人能够阅读的方法。(即原来源码中定义的类型)
  3. 三个工厂方法:
    • type_id方法,返回的是不带cvr修饰的类型信息
    • type_id_with_cvr方法,返回的是带有cvr修饰的类型信息
    • type_id_runtime方法,返回的是某种实例的运行时信息

基本使用样例

// 注意编译器环境:开启rtti,并且没有定义强制不兼容rtti宏(BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY)#include <iostream>#include <string>#include <vector>#include <assert.h>#include <stdio.h>#include <boost/type_index.hpp>// 为了支持type_index的hash_code方法#include <boost/functional/hash.hpp>using namespace std;using namespace boost;struct A{    BOOST_TYPE_INDEX_REGISTER_CLASS    virtual ~A() {}};struct B : public A{BOOST_TYPE_INDEX_REGISTER_CLASS};namespace ns1 {namespace {class a_class {    BOOST_TYPE_INDEX_REGISTER_CLASS    virtual ~a_class() {}};}namespace ns2 {class a_class {public:    BOOST_TYPE_INDEX_REGISTER_CLASS    static void hello()    {        cout << __PRETTY_FUNCTION__ << endl;    }    virtual ~a_class() {}};}}namespace nskskskkskskskkskskskkskskks1 { namespace nskskskkskskskkskskskkskskks2 { namespace nskskskkskskskkskskskkskskks3 { namespace nskskskkskskskkskskskkskskks4 { namespace nskskskkskskskkskskskkskskks5 {class a_class {};}}}}}int main(int argc, char **argv){    cout << "---------------------type_id begin------------------------------" << endl;    //对于gnu编译器来讲,居然没有溢出    cout << typeindex::type_id<int>().name() << endl;    cout << "int:" << typeindex::type_id<int>().raw_name() << endl;    cout << typeindex::type_id<nskskskkskskskkskskskkskskks1::nskskskkskskskkskskskkskskks2::nskskskkskskskkskskskkskskks3\            ::nskskskkskskskkskskskkskskks4::nskskskkskskskkskskskkskskks5::a_class>() << endl;    cout << "string:" << typeindex::type_id<string>().raw_name() << endl;    cout << "string:" << typeindex::type_id<string>().pretty_name() << endl;    cout << "vector<int>:" << typeindex::type_id<vector<int> >().raw_name() << endl;    cout << "vector<int>:" << typeindex::type_id<vector<int> >().pretty_name() << endl;    cout << "ns1::ns2::a_class:" << typeindex::type_id<ns1::ns2::a_class>().raw_name() << endl;    cout << "ns1::ns2::a_class:" << typeindex::type_id<ns1::ns2::a_class>().pretty_name() << endl;    cout << "ns1::a_class:" << typeindex::type_id<ns1::a_class>().raw_name() << endl;    cout << "ns1::a_class:" << typeindex::type_id<ns1::a_class>().pretty_name() << endl;    cout << "---------------------type_id end------------------------------" << endl;    cout << endl;    cout << "---------------------type_id_with_cvr begin--------------------------" << endl;    cout << "int:" << typeindex::type_id_with_cvr<int>().raw_name() << endl;    cout << "int:" << typeindex::type_id_with_cvr<int>().pretty_name() << endl;    cout << "const int:" << typeindex::type_id_with_cvr<const int>().raw_name() << endl;    cout << "const int:" << typeindex::type_id_with_cvr<const int>().pretty_name() << endl;    cout << "const int&:" << typeindex::type_id_with_cvr<const int&>().raw_name() << endl;    cout << "const int&:" << typeindex::type_id_with_cvr<const int&>().pretty_name() << endl;    cout << "---------------------type_id_with_cvr end--------------------------" << endl;    cout << endl;    cout << "---------------------type_id_runtime begin-------------------------------" << endl;    A a;    B b;    A &b_as_a = b;    cout << "b_as_a:" << typeindex::type_id_runtime(b_as_a).raw_name() << endl;    cout << "b_as_a:" << typeindex::type_id_runtime(b_as_a).pretty_name() << endl;    cout << "---------------------type_id_runtime end-------------------------------" << endl;    //=============================测试关闭运算符-==============================//  assert(typeid(b) == typeindex::type_id_runtime(b_as_a));//  assert(typeindex::type_id<A>() != typeindex::type_id<B>());    //==================================测试流运算符============================    cout << typeindex::type_id<int>() << endl;    cout << typeindex::type_id<int>().hash_code() << endl;    ns1::ns2::a_class::hello(); //名字很清晰    cout << __PRETTY_FUNCTION__ << endl;    printf("% ld", 123L);    return 0;}

stl_type_index对应的BOOST_REGISTER_CLASS实现

真正的实现位于boost/type_index/detail/stl_reigister_class.hpp文件中,主要实现如下:

namespace boost{ namespace typeindex { namespace detail {template <class T>inline const typeindex::stl_type_index::type_info_t& stl_construct_type_id_ref(const T *) BOOST_NOEXCEPT{    return typeid(T);}}}// 主要是为了支持type_id_runtime的// 当使用MSVC并且rtti被关闭的时候,这个宏才会起作用#define BOOST_TYPE_INDEX_REGISTER_CLASS \    public:\        virtual const boost::typeindex::stl_type_index::type_info_t& boost_type_index_type_id_runtime_() const BOOST_NOEXCEPT \        { \            return boost::typeindex::detail::stl_construct_type_id_ref(this); \        }}}}

正如上面所见到的,只是定义了一个虚方法,然后再这个虚方法中,直接返回typeid(this)就完事了。主要利用到的是OOP中的多态

ctti_type_index

正如在boost/type_index.hpp中所看到的那样,这个类主要在所使用编译器不是vc++编译器(必要前提),并且rtti被禁用,或者BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY被定义,他被作为boost::typeindex::type_index类型所引用。

这个类的接口和stl_type_index中提供的方法是完全一致的,他也是type_index_facade的基类,也提供了三个获取名称方法、三个工厂方法、获取hash_code代码。。。

但是他内部的实现是不依赖于typeid操作符的。boost对于ctti_type_index的源码文件为:boost/type_index/ctti_type_index.hpp。源码和分析(注释)如下:

namespace boost{ namespace typeindex{ namespace detail{// 他只是作为一种类型标示符存在,他没有任何方法,他不能被拷贝,也不能被复制class ctti_data{// 利用config中,关于编译器特性的相关宏,可以很容易写出兼容各种编译器的方法#if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)public:    ctti_data() = delete;    ctti_data(const ctti_data &) = delete;    ctti_data &operator=(const ctti_data &) = delete;#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)    ctti_data(ctti_data &&) = delete;    ctti_data &operator=(ctti_data &&) = delete;#endif#elseprivate:    ctti_data();    ctti_data(const ctti_data &);    ctti_data &operator=(const ctti_data &);#endif};} //namespace detail// 一个辅助函数,用于构造某种类型的ctti_data。// 细节://  内部是将boost::typeindex::detail::ctti<T>::n()返回的字符串直接转换为const ctti_data*类型。//  其实boost::typeindex::detail::ctti<T>::n()就是获取T类型的关键。他也是这个类实现最最核心的地方,最有营养的菜,当然只能长长你的见识,在实际项目中,基本用不到。//  这个方法在boost的实现实际位于。// boost/type_index/detail/complile_time_type_info.hpp文件中// 我为了看的方便,把它的定义写在下面,其实并不直接和ctti_type_index类定义在一起。切记!!!template <typename T>inline const detail::ctti_data &construct_ctti() BOOST_NOEXCEPT{    return *reinterpret_cast<const detail::ctti_data *>(boost::detail::ctti<T>::n());}//============下面是boost::detail::ctti<T>::n()的具体实现==============// 内部的几个方法,和几个全局静态常量是辅助实现下面的boost::detail::ctti<T>::n()的namespace detail{    //原代码中有一系列的宏,他们的实际用途是根据编译器的类型来定义几个全局静态常量,因为这儿测试的是mingw编译器,我只选取gnu变异的情况,并且对于boost::detail::ctti<T>::n()对于gnu编译器实现来讲,他所定义的最后一个静态常量,实际是用不到的,所以这儿我也省略掉    //下面是具体代码:    // 利用宏来简化重复代码的编写    // 简要说一个下面静态常量的作用:    // ctti_skip_size_at_begin 一个字符串中有效内容的起始部分,其实这儿原理类似于stl_type_index的pretty_name方法中的那种实现。具体说明放在赋值那儿,就是下面的条件选择宏那儿    // ctti_skip_size_at_end 一个字符串中有效字符串的中指部分    // [ctti_skip_size_at_begin, ctti_skip_size_at_end)可以确定一个有效子串    //ctti_skip_more_at_runtime 表示在运行时要忽略更多,对于gnu来讲,这个值为false    #define BOOST_TYPE_INDEX_REGISTER_CTTI_PARSING_PARAMS(begin_skip, end_skip, runtime_skip) \    BOOST_STATIC_CONSTEXPR std::size_t ctti_skip_size_at_begin = begin_skip;\    BOOST_STATIC_CONSTEXPR std::size_t ctti_skip_size_at_end = end_skip;\    BOOST_STATIC_CONSTEXPR bool ctti_skip_more_at_runtime = runtime_skip;    // 根据编译器类型来确定不同的静态常量值    #if defined(_MSC_VER_)    ...    #elif defined(__clang__) && defined(__APPLE__)    ...    // 这儿是gnu编译器的情况    // 在这儿说一下,为什么是下面的这些值:    // 其实下面的两个数字是为了定位某个模板方法描述中,具体参数类型的位置。比如对于namespace boost{namespace detail{ template <typename T> class ctti {inline static const char* n() BOOST_NOEXCPT {...}};,调用boost::detail::ctti<int>::n(),则他的运行时函数原型为:static const char* boost::detail::ctti<T>::n() [with T = int](获取运行时函数原型是通过编译器扩展功能实现,详情见下面)。那么:ctti_skip_size_at_begin 就是"[with T = "的末尾位置// ctti_skip_size_at_end为1,表示要忽略末尾的"]"字符// ctti_skip_more_at_runtime 表示在运行的时候,不用在扫描多余的字符    #elif defined(__GNUC__)        BOOST_TYPE_INDEX_REGISTER_CTTI_PARSING_PARAMS(57, 1, false) // 其实后面在原来源码中还是有一个值,只是对于gnu编译器来讲,这个值在实现ctti::n中没有用到    #elif ...        ...    #endif    #undef BOOST_TYPE_INDEX_REGISTER_CTTI_PARSING_PARAMS    // 辅助模板函数,内部使用BOOST_ASSERT_MSG,现在这个宏已经被c++11中的static_assert替代    template <bool Condition>    inline void assert_compile_time_length() BOOST_NOEXCEPT    {        BOOST_ASSERT_MSG(Condition ,"TypeIndex library is misconfigured for your compiler. "                "Please define BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING to correct values. See section "                "'RTTI emulation limitations' of the documentation for more information.");    }    //直接将结果内容返回    template <int ArrayLength>    inline const char *skip_begin_runtime(const char * begin, boost::mpl::false_) BOOST_NOEXCEPT    {        return begin;    }    // 对于gnu编译器来讲,因为ctti_skip_more_at_runtime静态常量值为false,所以下面方法是不执行的。写出来只是为了应个景。    template <int ArrayLength>    inline const char *skip_begin_runtime(const char * begin, boost::mpl::true_) BOOST_NOEXCEPT    {        // 如果 ctti_skip_more_at_runtime为true的话,这个方法还会被执行到的,因为使用gnu,这个值已经被设置成false了,所以为了专注于有用部分,这里面实现就暂时不研究了。        ...        return begin;    }    // 这个方法是关键代码,使用它是跳过begin中无效的字符串,跳到第一个有用的字符串。    // 有用字符串是指跳到模板实例化参数部分。    // 其实对于gnu编译器来讲,下面代码邪那么多,其实一句代码就能够搞定了,就是:    // return begin + ctti_skip_at_begin;    template <int ArrayLength>    inline const char *skip_begin(const char * begin)    {        assert_compile_time_length<ctti_skip_size_at_begin + ctti_skip_size_at_end >();        // 这儿有个提高程序性能的小技巧,利用模板编程中的bool_来实现函数自动调用,避免运行时使用if ... else ...语句,这样可以提高程序性能        return skip_begin_runtime<ArrayLength - ctti_skip_size_at_begin>(begin + ctti_skip_size_at_begin                ,boost::mpl::bool_<ctti_skip_more_at_runtime>());    }} // namespace detail} // namespace typeindexnamespace detail { //ctti位于全局的boost::detail命名空间中,有点意外啊// 主要是利用n的具体函数原型来提取T的类型// 原理:// 利用编译器的拓展功能来获取这个模板类中n的具体原型信息。// 然后从获取的信息中提取出T的部分。template <typename T>class ctti{public:    // 其实直接返回T的具体类型,只是在类型的右侧可能会有多余信息    inline static const char * n() BOOST_NOEXCEPT    {#   ifdef BOOST_TYPE_INDEX_FUNCTION_SIGNATURE        ...//  包含对GNU编译器的支持#   elif  defined(__PRETTY_FUNCTION__) \        || defined(__GNUC__) \        || (defined(__SUNPRO_CC) && (__SUNPRO_CC >= 0x5130)) \        || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) \        || (defined(__ICC) && (__ICC >= 600)) \        || defined(__ghs__) \        || defined(__DMC__)        // 对于gnu等编译器来讲,使用__PRETTY_FUNCTION__可以获取某个函数运行时的完整原型信息        return typeindex::detail::skip_begin<sizeof(__PRETTY_FUNCTION__)>(__PRETTY_FUNCTION__);#   else        ...#   endif    }};} // namespace boost::detail// 在这儿再次说明,上面的内容对于ctti_type_index中ctti_data的构建是非常非常重要。// ======================typeindex ===========================namespace typeindex {class ctti_type_index : public type_index_facade<ctti_type_index, detail::ctti_data>{public:    typedef detail::ctti_data type_info_t;    // 构造器,利用construct_ctti模板方法来进行构建,这个方法的具体实现看上面的部分    inline ctti_type_index() BOOST_NOEXCEPT : data_(&construct_ctti<void>()) {}    inline ctti_type_index(const type_info_t &type_info) BOOST_NOEXCEPT : data_(&type_info) {}    // 三个关于类型名称的方法    // 功能:因为不在使用rtti了,这儿输出的内容接近可读内容,只是右侧可能会多一些无关的内容    // 原理:    // 通过construct_ctti的实现,可以看到data_指针的真正类型是const char*类型,这儿是直接将data_中的输出进行输出    // 因为gnu编译器,通过__PRETTY_FUNCTION__获取模板方法的原型,所以右侧会有']',那么直接调用这个方法进行输出的时候,右侧也会多出字符']'    inline const char * raw_name() const BOOST_NOEXCEPT    {        return reinterpret_cast<const char*>(data_);    }    // 功能:获取可读的名称    // 细节:通过在boost/type_index/detail/compile_time_type_info.hpp定义的ctti_skip_size_at_end值,来舍去无效的内容    // 然后去除右端多余空格    // 将最终有效内容通过字符串返回    inline std::string pretty_name() const BOOST_NOEXCEPT    {        int len = get_raw_name_length();        while (raw_name()[len - 1] == ' ') -- len; // 去除右端空格        return std::string(raw_name(), len);    }    // 获取该对象的hash值    inline std::size_t hash_code() const BOOST_NOEXCEPT    {        return hash_range(raw_name(), raw_name() + get_raw_name_length());     }    // 获取内部数据    inline const type_info_t& type_info() const BOOST_NOEXCEPT    {        return *data_;    }    // ========================三个工厂方法===================    //功能:获取不带cvr修饰的类型信息    //细节:实现细节和stl_type_index的实现细节基本相同。    //  首先利用boost的模板元编程库去除cvr特性,得到no_cvr_t类型    //  然后利用construct_ctti模板方法来构建const ctti_data&    //  然后会发生隐式构造,构造ctti_type_index实例    template <typename T>    static inline ctti_type_index type_id() BOOST_NOEXCEPT    {        typedef typename boost::remove_reference<T>::type no_reference_t;        typedef typename boost::remove_cv<no_reference_t>::type no_cvr_t;        return construct_ctti<no_cvr_t>();    }    //功能:获取带有cvr修饰的类型信息    //细节:看源码,非常简单。    template <typename T>    static inline ctti_type_index type_id_with_cvr() BOOST_NOEXCEPT    {        return construct_ctti<T>();    }    // 功能:获取t的运行时的类型信息    // 细节:直接调用t对象的boost_type_index_type_id_runtime方法,这个方法是通过BOOST_TYPE_INDEX_REGISTER_CLASS定义的。ctti_type_index也有他对应的BOOST_TYPE_INDEX_REGISTER_CLASS宏的定义。    template <typename T>    static inline ctti_type_index type_id_runtime(const T &t) BOOST_NOEXCEPT    {        return t.boost_type_index_type_id_runtime();    }private:    const type_info_t *data_;    // 获取真正有效的内容长度    inline int get_raw_name_length() const BOOST_NOEXCEPT    {        // ctti_skip_size_at_end 是在boost/type_index/detail/compile_time_type_info.hpp中定义的。并且在上面,我也介绍了他的功能,以及对于gnu编译器他的设置值        return std::strlen(raw_name() + ctti_skip_size_at_end);    }};}}

总结:

这儿进行一下总结:

ctti_type_index的接口和stl_type_index的接口是完全一致的。

ctti_type_index和stl_type_index的实现是不一样的。stl_type_index利用的是c++编译器的rtti特性和typeid运算符。而ctti_type_index其实利用的是编译器的拓展功能。对于GNU编译器来讲,他利用的是__PRETTY_FUNCTION__宏来获取某个模板类中某个静态方法的完整原型信息,然后从这个信息中提取出完整参数类型。

具体使用实例,参考stl_type_index的实例实现。当然编译器环境要进行一些设置。设置如下:

  • 编译器不能使vc++编译器
  • 关闭rtti特性,或者定义BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY宏

ctti_type_index对应的BOOST_TYPE_INDEX_REGISTER_CLASS宏

实现非常简单,和stl_type_index对应的BOOST_TYPE_INDEX_REGISTER_CLASS宏实现类似,具体看如下代码:

namespace boost{ namespace typeindex { namespace detail {// 内部只是使用boost::typeindex::construct_ctti<T>进行构造const ctti_data &template <class T>inline const boost::typeindex::ctti_type_index::type_info_t& ctti_construct_type_id_ref(const T *) BOOST_NOEXCEPT{    return boost::typeindex::construct_ctti<T>();}}}}// 主要是为了支持type_id_runtime的// 实现,就是利用oop的多态了#define BOOST_TYPE_INDEX_REGISTER_CLASS \    public:\        virtual const typeindex::ctti_type_index::type_info_t& boost_type_index_type_id_runtime() const BOOST_NOEXCEPT \        { \            return boost::typeindex::detail::ctti_construct_type_id_ref(this); \        }

到此为止BOOST type_index库的整个内容都,介绍完毕了。

boost type_index所有库文件简介:

下面列出一张表,方便大家观看boost type_index源码。

文件名称 功能 boost/type_index.hpp 包含三个模板方法(typeindex::type_id(), typeindex::type_id_with_cvr(), typeindex::type_id_runtime(const T&)),和一个类型(typeindex::type_index,这个type_index类他是typedef定义的类型,可能是stl_type_index,也可能是ctti_type_index,具体情况,请看前面那个博客:关于这个库的使用细节) boost/type_index/type_index_facade.hpp 这个文件包含了stl_type_index和ctti_type_index公用基类stl_type_index_facade模板类的实现 boost/type_index/stl_type_index.hpp 这个文件中包含了stl_type_index类的完整实现细节 boost/type_index/detail/stl_register_class.hpp 包含了stl_type_index类所对应的BOOST_TYPE_INDEX_REGISTER_CLASS宏的实现 boost/type_index/ctti_type_index.hpp 这个文件包含了ctti_type_index类的完整实现细节 boost/type_index/detail/ctti_register_class.hpp 包含了ctti_type_index类所对应的BOOST_TYPE_INDEX_REGISTER_CLASS宏的实现 boost/type_index/detail/compile_time_type_info.hpp 包含了模板类ctti的完整实现,以及对于不同编译器中有效字符串相关的静态常量值,以及提取有效值的实现

总篇小结

第一次写这么长的内容,其实只是为了将自己看这个库的收获给记录下来,因为现在的技术实在是太多了,基本上有些东西如果时间长,不用的话,那么肯定会忘记掉的。现在有csdn这个好东西,当然要善于利用了。

其实对于boost type_index这个库来讲,我们直接用就行了。对于他的实现细节,完全不用了解。因为这个里面里面利用的技巧在真正的项目中,我认为用到的不多。

不过回过头来,你看这些使用ctti_type_index来实现type_index,其实他的消耗,并不多吧。我具体不知道他的消耗是否多余rtti的消耗,我认为应该比rtti的消耗小。所以有时时候,你可以关掉rtti来使用type_index这个库。这样也可以获取运行时信息,而且消耗应该也小于rtti(我也不确定,但是从ctti_type_index的实现上来看,他只定义了ctti_type_index类,然后为每个类要定义一个额外的虚函数,为一种类型都要生成一个ctti<T>实例,为每种类型要生成具体的内部模板成员方法,而且因为模板特性,你不使用时不生成实例的,所以我说ctti_type_index的实现应该小于rtti)。

真正能够学到的技术或者增长的见识是:

  • 有些编译器提供了对于编译器类型的解码操作,具体实现看:boost/core/demangle.hpp中的scoped_demangled_name类,这个类内部调用各个编译器的关于abi的api。比如:对于gnu编译器来讲,我们可以通过__cxa_demangle方法,来对于编译器认识的类型进行解码操作。
  • 学到了迂回地解决问题。就比如typeid运算符只能获取某种类的不带cvr的类型信息,那么我们可以讲带有cvr修饰的类型信息保存到某个模板参数中,然后通过typeid来获取这个模板类的类型信息,然后从该信息中提取出模板参数的部分,从获取带有cvr修饰的信息。
  • 编译器一些拓展宏,比如__PRETTY_FUNCTION____FUNCSIG__,前面那个宏是输出gnu编译器当前函数的函数原型,后者是输出vc++编译器中当前函数的函数原型。
0 0
原创粉丝点击