如何讓編譯器寫代碼,提升生產效率,避免996

對,你沒看錯,是讓編譯器寫代碼,編譯器不只是能編譯代碼,還能寫代碼。linux

廢話少說,直接上代碼,先看一個例子:ios

#include <string>數據庫

#include<iostream>函數

struct stObject
{
    char one;
    int  tow;
    float three;
    std::string four;    
};性能

template<typename T>
void Print( T & obj)
{
    //想在此,打印輸出obj對象的各個字段的值,怎麼作?
}優化

int main()
{
    stObject obj;
    Print(obj);調試

    printf("Enter any key for exit!");
    getchar();
    return 0;
}對象

有一個結構體stObject,以及一個模板函數Print,該函數想打印輸出該結構體對象的各個字段,這個函數應該怎麼實現呢?blog

先定義一個模板類:遞歸

template <int size> 
struct Int2Type

    enum { Size = size }; 
};

這個模板類的類型參數是int, 當這個整數值不一樣時,就是不一樣的類型,例如 Int2Type<1> ,和Int2Type<2>,Int2Type<3>等等,都不一樣的類型。

接着咱們給 stObject結構體,增長几個成員函數,以下:

struct stObject
{
    char one;
    int  tow;
    float three;
    std::string four;    

    ////增長成員函數獲取字段值的引用
    auto get(Int2Type<1>) -> decltype((one)) { return one;}
    auto get(Int2Type<2>) -> decltype((tow)) { return tow; }
    auto get(Int2Type<3>) -> decltype((three)) { return three; }
    auto get(Int2Type<4>) -> decltype((four)) { return four; }
};

於時咱們的Print模板函數就能夠這樣實現了:

template<typename T>
void Print( T & obj)
{
    //想在此,打印輸出obj對象的各個字段的值,怎麼作?
    std::cout << obj.get(Int2Type<1>()) << ","
              << obj.get(Int2Type<2>()) << ","
              << obj.get(Int2Type<3>()) << ","
              << obj.get(Int2Type<4>()) << std::endl;

}

而後在咱們的main函數中,給ojb對象的字段賦一些值,以下:

int main()
{
    stObject obj;
    obj.one = 100; //賦值
    obj.tow = 2;
    obj.three = 3;
    obj.four = "4";
    Print(obj);

    printf("Enter any key for exit!");
    getchar();
    return 0;
}

程序運行輸出:

 

後來,咱們又給stObject增長了一個字段 : short five;

程序運行後Print模板函數,只能輸出前四個字段,第五個沒有輸出。這樣Print函數也須要跟着改動,這樣太煩人了,有沒有更好的辦法呢。辦法是讓Print函數知道obj對像共有幾個字段。咱們給stObject結構體增長一個枚舉,這個枚舉值指定本結構體共有幾個字段,同時修改Print函數:

struct stObject
{
    enum {Size = 5}; //指明本結構體有5個字段
    char one;
    int  tow;
    float three;
    std::string four;    
    short five; //新增長的字段

    //增長成員函數獲取字段值的引用
    auto get(Int2Type<1>) -> decltype((one)) { return one;}
    auto get(Int2Type<2>) -> decltype((tow)) { return tow; }
    auto get(Int2Type<3>) -> decltype((three)) { return three; }
    auto get(Int2Type<4>) -> decltype((four)) { return four; }
    auto get(Int2Type<5>) -> decltype((five)) { return five; } //新增長的函數
};


template<typename T>
void Print( T & obj)
{
    //想在此,打印輸出obj對象的各個字段的值,怎麼作?
    Print_i(obj,Int2Type<1>()); //先輸出第一個字段
}

template<typename T, typename int size>
void Print_i(T & obj, Int2Type<size> index)
{
    //想在此,打印輸出obj對象的各個字段的值,怎麼作?
    std::cout << obj.get(index) << ",";
    Print_i(obj,Int2Type<size + 1>());  //遞歸輸出下一個字段

}
template<typename T>
void Print_i(T & obj, Int2Type<obj.Size+1>) //遞歸結束
{
    std::cout << std::endl;
}

在main函數中給obj.five = 101;後,程序運行結果以下:

這樣,後面還給stObject增長字段,Print函數都不須要修改了。只須要修改stObject的枚舉值,以及增長要應的get成員函數獲取字段值。這樣仍是顯得有些煩鎖。咱們進一步優化。

先定義幾個宏:

#define FIELD_BEGIN() enum{Begin = __COUNTER__}; 

#define FIELD_END() enum{Size = __COUNTER__ - Begin -1 };
#define FIELD(type,name) FIELD_INDEX(type,name,(__COUNTER__- Begin))

#define FIELD_INDEX(type,name,index) DEFINE_FILED(type,name,index) 
#define DEFINE_FILED(type,name,index) type name; auto get(Int2Type<index>) -> decltype((name)) { return name;}

同時把結構體stObject修改以下:

struct stObject
{
    /*
    enum {Size = 5};
    char one;
    int  tow;
    float three;
    std::string four;    
    short five; //新增長的字段

    //增長成員函數獲取字段值的引用
    auto get(Int2Type<1>) -> decltype((one)) { return one;}
    auto get(Int2Type<2>) -> decltype((tow)) { return tow; }
    auto get(Int2Type<3>) -> decltype((three)) { return three; }
    auto get(Int2Type<4>) -> decltype((four)) { return four; }
    auto get(Int2Type<5>) -> decltype((five)) { return five; } //新增長的函數
    
    */
    FIELD_BEGIN()
        FIELD(char, one)
        FIELD(int, tow)
        FIELD(float, three)
        FIELD(std::string, four)
        FIELD(short, five)       
   FIELD_END()
};

先看宏FIELD_BEGIN(),該宏不帶參數,它後面跟着的代碼是:enum{Begin = __COUNTER__};   把FIELD_BEGIN()放在stObject結構體的定義中,當編譯器把宏展開以後,下面的兩段代碼是相同的:

struct stObject
{

    FIELD_BEGIN()

}

等同於:

struct stObject
{

    enum{Begin = __COUNTER__};   //即定義了一個枚舉,

}

而宏__COUNTER__是編譯器內置的宏,編譯器第一次遇到它時,用0來替換該宏,第二次遇到它時,用1來替換,依次類推。

再看第二個宏FIELD_END()該宏也不帶參數,後面跟的代碼時enum{Size = __COUNTER__ - Begin -1 };,也是定義了一個枚舉。

那麼

struct stObject
{

   FIELD_BEGIN() //假設__COUNTER__的值爲n

   FIELD_END()  //這裏__COUNTER__的值爲n+1,那麼枚舉Size的值爲n+1 - n -1 =0,表明這個結構體有0個成員字段。

}

再看宏#define FIELD(type,name)  FIELD_INDEX(type,name,(__COUNTER__- Begin)),該宏帶有兩個參數,第一個參數表明 結構體要定義的字段類型,第二個參數,表明結構體要定義的字段名字,該宏調用了下面的宏:

#define FIELD_INDEX(type,name,index) DEFINE_FILED(type,name,index)

參數type,name的意義和宏FIELD同樣,而第三個參數index表明這是宏的第幾個字段。該宏又調用了下面的宏:

#define DEFINE_FILED(type,name,index) type name; auto get(Int2Type<index>) -> decltype((name)) { return name;}

該宏的參數和FIELD_INDEX同樣,後面跟的代碼 type name; 表示給結構體定義一個字段,類型爲type, 字段名爲name, 後面還跟了一個get成員函數獲取該字段的值。

因此下面的結構體定義,宏展開後,和/**/中的代碼是等同的:

struct stObject
{
    FIELD_BEGIN()
        FIELD(char, one)
        FIELD(int, tow)
        FIELD(float, three)
        FIELD(std::string, four)
        FIELD(short, five)       
        FIELD_END()

    /*宏展開後,同等於下面的代碼:
    enum {Begin = __COUNTER__}    
    char one;
    int  tow;
    float three;
    std::string four;    
    short five; //新增長的字段

    //增長成員函數獲取字段值的引用
    auto get(Int2Type<1>) -> decltype((one)) { return one;}
    auto get(Int2Type<2>) -> decltype((tow)) { return tow; }
    auto get(Int2Type<3>) -> decltype((three)) { return three; }
    auto get(Int2Type<4>) -> decltype((four)) { return four; }
    auto get(Int2Type<5>) -> decltype((five)) { return five; } //新增長的函數 

    enum {Size = 5};
    */

};

須要注意的是每一個FIELD宏須要單獨佔一行,不然__COUNTER__計算會錯亂。

後面須要給結構體增長新字段時,只須要增長一行FIELD(),例如 FIELD(long , six)

當結構體的字段比較多時,Print函數,只輸出字段值,沒有什麼意義,假如能連字段名也輸出就行了。說幹就幹。

先定義一個新宏:

#define DEFINE_NAME_FUNC(name,index) const char* get_name(Int2Type<index>){return #name;}

這個宏帶兩個參數,一個字段名,一個是字段索引(即表明是第幾個字段),宏的代碼是定義一個成員函數,獲取字段名,而後修改宏FIELD_INDEX:

#define FIELD_INDEX(type,name,index) DEFINE_FILED(type,name,index) DEFINE_NAME_FUNC(name,index)

這樣就成功給結構體的每一個字段增長一個獲取字段名的成員函數。再把Print_i函數修改以下:

template<typename T, typename int size>
void Print_i(T & obj, Int2Type<size> index)
{
    //想在此,打印輸出obj對象的各個字段的值,怎麼作?
    std::cout << obj.get_name(index) << ":" << obj.get(index) << ","; //先輸出結構體字段的名字
    Print_i(obj,Int2Type<size + 1>());  //遞歸輸出下一個字段
}

最後,給結構體stOjbect增長兩個字段:

        FIELD(long, six)
        FIELD(long, seven)

而後在main函數中賦值

    obj.six = obj.Begin;
    obj.seven = obj.Size;

程序運行輸出:

在linux中調試程序就很方便啦,一條語句就能夠把結構體打印輸出,增長字段也不須要修改Print函數。使用相同的方法,很容易,讓一個結構體和.ini文件綁定,一條語句就把整個.ini的字段讀到結構體中。還有在數據庫方面的應用,一個結構體和一個數據庫表綁定。一條語名就能夠把數據庫表讀到結構體vector中。大大的增長開發效率,見過不少操做數據庫的代碼,不停的重複着一個一個字段的綁定輸入參數,而後查詢數據庫,而後獲取查詢結果集,而後一個一個字段給結構體賦值。這樣的代碼是醜陋無比的,也容易出錯,這樣的髒活累活交給編譯器寫代碼完成就啦。並且模板都是在編譯值求值,並且是內聯函數。因此性能也扛扛的。不再用996。最後附上該例子的完整代碼:

 


#include <string>
#include<iostream>

#define FIELD_BEGIN() enum{Begin = __COUNTER__}; 
#define FIELD(type,name) FIELD_INDEX(type,name,(__COUNTER__- Begin))
#define FIELD_END() enum{Size = __COUNTER__ - Begin -1 };
#define FIELD_INDEX(type,name,index) DEFINE_FILED(type,name,index) DEFINE_NAME_FUNC(name,index)
#define DEFINE_FILED(type,name,index) type name; auto get(Int2Type<index>) -> decltype((name)) { return name;}
#define DEFINE_NAME_FUNC(name,index) const char* get_name(Int2Type<index>){return #name;}


template <int size>
struct Int2Type
{
  enum { Size = size };
};

struct stObject
{
    FIELD_BEGIN()
        FIELD(char, one)
        FIELD(int, tow)
        FIELD(float, three)
        FIELD(std::string, four)
        FIELD(short, five)
        FIELD(long, six)
        FIELD(long, seven)
        FIELD_END()

    /*宏展開後,同等於下面的代碼:
    enum {Begin = __COUNTER__}    
    char one;
    int  tow;
    float three;
    std::string four;    
    short five; //新增長的字段

   long six;

  long seven;

    //增長成員函數獲取字段值的引用
    auto get(Int2Type<1>) -> decltype((one)) { return one;}
    auto get(Int2Type<2>) -> decltype((tow)) { return tow; }
    auto get(Int2Type<3>) -> decltype((three)) { return three; }
    auto get(Int2Type<4>) -> decltype((four)) { return four; }
    auto get(Int2Type<5>) -> decltype((five)) { return five; } //新增長的函數
    auto get(Int2Type<6>) -> decltype((six)) { return six; } //新增長的函數
    auto get(Int2Type<7>) -> decltype((seven)) { return seven; } //新增長的函數
    enum {Size = 7};
    */

};


template<typename T>
void Print( T & obj)
{
    //想在此,打印輸出obj對象的各個字段的值,怎麼作?
    Print_i(obj,Int2Type<1>()); //先輸出第一個字段
}

template<typename T, typename int size>
void Print_i(T & obj, Int2Type<size> index)
{
    //想在此,打印輸出obj對象的各個字段的值,怎麼作?
    std::cout << obj.get_name(index) << ":" << obj.get(index) << ","; //先輸出結構體名字
    Print_i(obj,Int2Type<size + 1>());  //遞歸輸出下一個字段
}
template<typename T>
void Print_i(T & obj, Int2Type<obj.Size+1>) //遞歸結束
{
    std::cout << std::endl;
}

int main()
{
    stObject obj;
    obj.one = 100;
    obj.tow = 2;
    obj.three = 3;
    obj.four = "4";
    obj.five = 101;
    obj.six = obj.Begin;
    obj.seven = obj.Size;
    Print(obj);

    printf("Enter any key for exit!");     getchar();     return 0; }

相關文章
相關標籤/搜索