Boost type_index庫使用介紹

Boost type_index庫使用介紹

Boost type_index可以獲取某種類型的具體類型名稱,不管是靜態類型名稱,還是帶有cvr(const-volatile-reference)修飾的名稱,還可以獲取某個對象運行時的名稱。

並且Boost type_index的實現消耗也是非常非常小的,如果編譯器啓用了rtti特性,那麼他內部就實現type_info來輔助實現type_index。如果編譯器沒有啓用rtti特性,那麼他就自己實現type_index特性,利用模板+編譯器的獲取函數具體原型的相關宏(比如__PRETTY_FUNCTION__(GNU編譯器))+一個固定名稱的虛函數,就可以實現type_index的所要求的功能了。他的實現肯定小於編譯器內部的rtti特性所產生的消耗。

下面會介紹一些這個庫的組件和功能。

type_index類型

簡單描述:

type_index類型主要就是用來存儲某種類型的類型信息,關鍵信息就是類型的名稱相關信息

type_index的類型聲明位於boost/type_index.hpp中,他是一個typedef定義的類型。

當編譯器啓用rtti特性,或者當前編譯器爲vc++編譯器,那麼type_index實際上就是stl_type_index類型。

當編譯器關閉了rtti特性,並且當前編譯器不是vc++編譯器的話,那麼type_index實際上就是ctti_type_index類型。

主要方法:

//下面這個類是不存在,主要是爲了說明type_index實際類型的主要的方法
//再次說明這個類是不存在的
class type_index
{
public:
    // 下面的三個方法,一般是用戶來使用的,一般直接使用pretty_name即可
    inline const char* raw_name() const BOOST_NOEXCEPT;
    inline const char* name() const BOOST_NOEXCEPT;
    inline const string pretty_name() const BOOST_NOEXCEPT;

    // 獲取此type_index實例的hash_code
    inline std::size_t hash_code() const BOOST_NOEXCEPT;    

    // 下面三個方法是三個工廠方法,他們一般是通過全局模板方法來調用的
    // 用戶不用直接來調用它
    template <typename T>
    static type_index type_id() BOOST_NOEXCEPT;
    template <typename T>
    static type_index type_id_with_cvr() BOOST_NOEXCEPT;
    template <typename T>
    static type_index type_id_runtime() BOOST_NOEXCEPT;

    // 支持流運算符
    // 支持關係運算符
};
  • inline const char* raw_name() const BOOST_NOEXCEPT
    本意是用來返回原始的編譯器能夠認識的類型名稱。

    • 當編譯器是vc++編譯器的時候,不管rtti是否開啓,這個方法生成vc++編譯器能夠認識的名稱,比如: typeindex::type_id<int>()::raw_name()返回:.H… …
    • 當編譯器不是vc++編譯器時候,當rtti開啓的時候,這個方法返回類型的編譯器能夠識別的名稱,比如:對於mingw來講typeindex::type_id<int>()::raw_name()返回i … …。
    • 當編譯器不是vc++編譯器,並且rtti禁用的時候,這個方法返回的是原始類型名稱,這個這個名稱後面後多一些其他多餘字符,比如對於mingw編譯器來講,typeindex::type_id<int>()::raw_name()返回’int]’,其中’]’是多餘字符。
  • inline const string pretty_name() const
    用來輸出類型的可讀名稱,當然他的輸出內容還是和編譯器和rtti的狀態有關。

    • 如果使用vc++編譯器,如果是typedef定義類型,他會typedef定義的類型所輸出類型所參考的類型, 比如:typeindex::type_id<string>::pretty_name()輸出爲:class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >。對於基本類型他會輸出基本類型。
    • 對於mingw編譯器,比如mingw有這樣輸出方式:如果要輸出的類型名稱是typedef定義的,他會輸出typedef定義的類型名稱,比如:typeindex::type_id<string>().pretty_name(),輸出爲:std::string,並且輸出某種模板類型的話,他輸出的類型名稱前面也沒有class字符串(相比於vc++來講),比如:typeindex::type_id<vector<int> >().pretty_name()輸出爲:std::vector<int, std::allocator<int> >
    • 因爲我只有兩種編譯器來測試,其他編譯器情況應該也不一樣。但是從實現上來講,type_index只有兩種實現,一種ctti_type_index和stl_type_index,因爲對於vc++編譯器來講,不管rtti狀態,他總是採用stl_type_index,而對於非vc++編譯器來講,他的type_index具體就依賴於rtti的狀態了。如果開啓rtti,則type_index總是stl_type_index類型,否則就是ctti_type_index類型。所以我列舉了一種vc++編譯器類型和一種非vc++編譯器類型(mingw)。對於其他非vc++編譯器類型,自己可以測試測試。
  • inline const char* name() const BOOST_NOEXCEPT;
    還是和編譯器類型和rtti狀態有關。
    • 對於vc++編譯器,不論rtti的狀態如何,他輸出的都是人能夠直接閱讀的類型,即和調用pretty_name()方法行爲完全相同。
    • 對於非vc++來講,不論rtti的狀態如何,他的行爲都和raw_name()的行爲完全相同。
  • inline std::size_t hash_code() const BOOST_NOEXCEPT;
    獲取this這個實例的hash值
  • type_index實例,還支持關係運算符和運算符。對於流運算符他直接輸出的是pretty_name()中的返回值。
  • type_id、type_id_with_cvr、type_id_runtime是三個工廠方法,來讓此命名空間中對應的模板方法來調用。

typeindex::type_id函數

函數定義:


// type_index 他是一個typedef,他可能是stl_type_index,
// 也可能是ctti_type_index類型,不管是哪種,他們都有完全相同的方法,
// 可以用來輸出:raw_name(原始名稱), pretty_name(可讀名稱)
// 取決於編譯器類型和rtti的狀態,詳情,請參考後面的關於這個庫的內部實現細節部分
template <typename T>
type_index type_id()
{
    return type_index::type_id<T>();
}

函數具體使用樣例:

#include <iostream>
#include <string>
#include <boost/type_index.hpp>

using namespace std;
using namespace boost;

int main(int argc, char **argv)
{
    typeindex::type_index type;

    type = typeindex::type_id<int>();
    cout << type.pretty_name() << endl;

    type = typeindex::type_id<const int>();
    cout << type.pretty_name() << endl;

    type = typeindex::type_id<int&>();
    cout << type.pretty_name() << endl;

    type = typeindex::type_id<const int&>();
    cout << type.pretty_name() << endl;     
    return 0;
}

運行結果如下表:
int
int
int
int

功能總結:

上面使用typeindex::type_id,來獲取基本的int,和帶有cvr(const-volatile-reference)修飾的int,最後輸出名稱都是int,可以說明type_id獲取的類型是不帶cvr修飾的原始類型

typeindex::type_id<T>獲取是包含不帶cvr修飾的T的具體類型信息的type_index實例

typeindex::type_id_with_cvr

函數定義:

// 基本原型是
template <typename T>
type_index type_id_with_cvr()
{
    return type_index::type_id_with_cvr<T>();
}

函數測試樣例代碼:

#include <iostream>
#include <string>
#include <boost/type_index.hpp>

using namespace std;
using namespace boost;

int main(int argc, char **argv)
{
    typeindex::type_index type;

    type = typeindex::type_id_with_cvr<int>();
    cout << type.pretty_name() << endl;

    type = typeindex::type_id_with_cvr<const int>();
    cout << type.pretty_name() << endl;

    type = typeindex::type_id_with_cvr<int&>();
    cout << type.pretty_name() << endl;

    type = typeindex::type_id_with_cvr<const int&>();
    cout << type.pretty_name() << endl;     
    return 0;
}

運行結果:
int
int const
int &
int const &

功能總結:

typeindex::type_id_with_cvr<T>獲取的是帶有cvr修飾的類型信息type_index實例

typeindex::type_id_runtime

函數定義:

// 基本原型是
template <typename T>
type_index type_id_runtime(const T &t)
{
    return type_index::type_id_runtime(t);
}

函數測試樣例代碼:

#include <iostream>
#include <string>
#include <boost/type_index.hpp>

using namespace std;
using namespace boost;

class IFly
{
public:
    BOOST_TYPE_INDEX_REGISTER_CLASS
    virtual void fly() const = 0;
    virtual ~IFly() {}
};

class Bird : public IFly
{
public:
    BOOST_TYPE_INDEX_REGISTER_CLASS
    void fly() const
    {
        cout << "Bird fly" << endl;
    }
};

int main(int argc, char **argv)
{
    Bird bird;
    IFly &flyable = bird;
    typeindex::type_index type = typeindex::type_id_runtime(flyable);
    cout << type.pretty_name() << endl;
    return 0;
}

運行結果:
Bird

功能總結:

typeindex::type_id_runtime<T>獲取的是運行時類的具體類型

type_index庫一個具體使用樣例

這個例子是使用type_index庫來實現一個 帶有一個參數的仿函數適配器,可以他可以適配任何帶有一個參數(不管這個參數是什麼類型)的函數。

#include <iostream>
#include <boost/type_index.hpp>

using namespace std;
using namespace boost;

// 一個保留cvr的具體實例
// 一般來講的話,應該講這個類設計成一個模板類,但是也是可以不設置成模板類的。
// 因爲如果不設置的話,那麼可以選擇將部分方法導出到庫裏面

// 好處:可以在運行時綁定任意一個含有一個參數的函數指針
// 而且,調用函數的時候,類型是安全的。
class type_erased_unary_function
{
public:

    // 構造器是模板(使用它非常靈活)
    // ParamT類型,這兒這樣寫ParamT最後肯定也是不帶cvr特性的(我以前以爲可能帶的,但是最後肯定不帶)
    template <typename ParamT>
    type_erased_unary_function(void (*function_ptr)(ParamT))
        : function_ptr_(reinterpret_cast<void*>(function_ptr)),exact_param_t_(typeindex::type_index::type_id_with_cvr<ParamT>())
    {
        cout << typeindex::type_id_with_cvr<ParamT>().pretty_name() << endl;
    }

    // 其實這兒有時會產生非常大的消耗的,
    // 不管實際參數是什麼類型,對於這兒的param總是不帶cvr修飾的類型
    // 如果要獲取實際的傳入類型,可能可以使用c++ 11的完美轉發機制
    template <typename ParamT>
    void call(ParamT param)
    {
        if (exact_param_t_ != typeindex::type_id_with_cvr<ParamT>())
        {
            throw runtime_error("Error param type...");
        }
        reinterpret_cast<void (*)(ParamT)>(function_ptr_)(param);
    }

private:
    void * function_ptr_;
    typeindex::type_index exact_param_t_; // 包含cvr信息
};

// 一個測試函數
void func(int i)
{
    cout << "i: " << i << endl;
}

void func2(const int i) {}

int main(int argc, char **argv)
{
    // 這個類的強大之處,是他可以接收帶有一個參數的函數指針
    // 主要利用模板構造器,和type_index
    type_erased_unary_function function(func);
    function.call(10);

    function = type_erased_unary_function(func2);
    return 0;
}

目錄:
[TOC]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章