最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++利用宏實(shí)現(xiàn)類成員反射詳解

 更新時(shí)間:2024年01月10日 11:36:56   作者:ticking  
這篇文章主要為大家詳細(xì)介紹了C++如何利用宏實(shí)現(xiàn)類成員反射,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,有興趣的小伙伴可以了解一下

本文我們看下用宏來實(shí)現(xiàn)反射,在一些伙伴使用c++版本還不是那么高的情況下但又需要反射的一些技巧,這里使用的代碼是iguana里的實(shí)現(xiàn),我對它關(guān)于反射的地方提煉一下,稍微改動了下。iguana是比較優(yōu)秀的序列化庫,其中使用反射為基礎(chǔ),性能很好?,F(xiàn)在在yalantinglibs中也可以找到。

當(dāng)然使用的時(shí)候可以直接使用iguana,我這里解釋下其中的相關(guān)原理。

如何使用

以如下Person這個(gè)結(jié)構(gòu)體為例

struct Person{
    int a;
    float b;
};
REFLECTION(Person, a, b)

這里結(jié)構(gòu)體就是普通的結(jié)構(gòu)體,不過需要用戶做的是,需要定義REFLECTION宏,其中第一個(gè)參數(shù)是類(結(jié)構(gòu)體)名,然后是各個(gè)成員名。

然后其實(shí)就可以使用反射了:

using Members = decltype(iguana_reflect_members(std::declval<Person>()));
std::cout << Members::value() << std::endl; // count

auto membersPtr = Members::apply_impl(); // ptr(tuple)
Person p{};
p.*std::get<0>(membersPtr) = 34;
p.*std::get<1>(membersPtr) = 4.1f;

std::cout << p.a << std::endl; // 34
std::cout << p.b << std::endl; // 4.1

REFLECTION中會生成一個(gè)iguana_reflect_members函數(shù),該函數(shù)簡單展示下:

auto iguana_reflect_members(STRUCT_NAME const &) {                                                  
    struct reflect_members {                                                  
        // ...(略)
    };                               
    return reflect_members{}; 
}

可以看到iguana_reflect_members函數(shù)內(nèi)部定義了一個(gè)用來提供成員反射信息的b結(jié)構(gòu)體,然后構(gòu)造并返回。
繼續(xù)返回到使用示例那里,第一句通過decltypedeclval搭檔拿到了iguana_reflect_members返回值類型。第二句我們先打印出來Person這個(gè)結(jié)構(gòu)體的成員個(gè)數(shù)。然后再使用Members::apply_impl函數(shù)獲取到Person的成員指針。這里使用成員指針就可以對其成員進(jìn)行訪問了。返回值的類型是tuple,我們使用std::get來對tuple進(jìn)行遍歷訪問。

如何實(shí)現(xiàn)

那么如何實(shí)現(xiàn)獲取到成員的個(gè)數(shù),及存儲成員指針這些呢,我們?nèi)ソ议_REFLECTION的真面目;

#define REFLECTION(STRUCT_NAME, ...)                                    \
  MAKE_META_DATA_IMPL(STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)

看上去很簡單,調(diào)用MAKE_META_DATA_IMPL的宏,MAKE_META_DATA_IMPL需要STRUCT_NAME,count以及所有的剩余參數(shù),也就是類的各個(gè)成員??梢钥吹?code>GET_ARG_COUNT可以獲取到成員的個(gè)數(shù)。

成員個(gè)數(shù)

那我們先去看下GET_ARG_COUNT的實(shí)現(xiàn):

#define MARCO_EXPAND(...) __VA_ARGS__
#define GET_ARG_COUNT_INNER(...) MARCO_EXPAND(ARG_N(__VA_ARGS__))

#define GET_ARG_COUNT(...) GET_ARG_COUNT_INNER(__VA_ARGS__, RSEQ_N())

GET_ARG_COUNT調(diào)用GET_ARG_COUNT_INNER,將成員和RSEQ_N拼接起來,傳遞給ARG_N這個(gè)宏作為參數(shù)調(diào)用,那就要看下ARG_N和RSEQ_N的聲明:

#define ARG_N(_1, _2, _3, _4, _5, _6, _7, _8,    \
              _9, _10, _11, _12, _13, _14, _15,  \
              _16, _17, _18, _19, _20, _21, _22, \
              _23, _24, _25, _26, _27, _28, _29, \
              _30, _31, _32, N, ...)             \
 N

#define RSEQ_N()  \
 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, \
 5, 4, 3, 2, 1, 0

ARG_N可以看到接收32個(gè)參數(shù)及N,然后就能表示N。RSEQ_N()僅僅就是32~0的數(shù)字序列,那么將成員(假設(shè)3個(gè)成員)和RSEQ_N()組合起來就是類似這樣

member1, member2, member3, 32, 31, 30, ... 0

然后傳遞給ARG_N時(shí),member1對應(yīng)_1,member2對應(yīng)_2,member3對應(yīng)_3,32對應(yīng)_4,... 這樣計(jì)算參數(shù)個(gè)數(shù)就是3(3個(gè)成員)+ 33(32~0),那么進(jìn)一步這里N就是第33個(gè)元素,再進(jìn)一步可以這樣理解:如果沒有成員,僅僅RSEQ_N()傳遞進(jìn)來時(shí),一一匹配到0正好對應(yīng)N,那么當(dāng)前邊加了3個(gè)成員,那么就是將RSEQ_N()后移3個(gè)元素,那就是N正好對應(yīng)3,也就是成員的個(gè)數(shù)。

成員指針

再次回到我們實(shí)現(xiàn)的最開始部分:

#define REFLECTION(STRUCT_NAME, ...)                                    \
 MAKE_META_DATA_IMPL(STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)

GET_ARG_COUNT這個(gè)我們已經(jīng)明白了,那么我們繼續(xù)去看下MAKE_META_DATA_IMPL宏做了什么:

#define MAKE_META_DATA_IMPL(STRUCT_NAME, N, ...)    \
  [[maybe_unused]] inline static auto               \
  iguana_reflect_members(STRUCT_NAME const &)   {   \
    struct reflect_members {                        \
      constexpr decltype(auto) static apply_impl(){ \
        return std::make_tuple(                     \
            MAKE_ARG_LIST(N, &STRUCT_NAME::FIELD,   \
            __VA_ARGS__));                          \
      }                                             \
      using size_type =                             \
            std::integral_constant<size_t, N>;      \
      constexpr static size_t value() {             \ 
        return size_type::value;                    \
      }                                             \
    };                                              \
    return reflect_members{};                       \
  }

MAKE_META_DATA_IMPL這個(gè)宏就是定義了iguana_reflect_members(STRUCT_NAME const &)這樣一個(gè)函數(shù),大致的結(jié)構(gòu)我們前邊也說過,值得注意的有兩個(gè)點(diǎn):

  • 因?yàn)閰?shù)會根據(jù)傳入的類名各不一樣,所以不用擔(dān)心函數(shù)簽名重復(fù)的問題;
  • 定義了函數(shù)內(nèi)部結(jié)構(gòu)體,返回了內(nèi)部結(jié)構(gòu)體對象,但是如我們最開始使用那樣,僅僅通過decval拿到這個(gè)內(nèi)部結(jié)構(gòu)體的類型,而不會真正調(diào)用iguana_reflect_members函數(shù)。

繼續(xù)看reflect_members結(jié)構(gòu)體,從簡單的看起,value函數(shù)就是返回剛剛傳進(jìn)來的N,這里size_type就是一個(gè)值為N的結(jié)構(gòu)體,正好也是返回size_type::value, 所以就是N。

apply_impl函數(shù)里邊稍微有點(diǎn)復(fù)雜,因?yàn)槌蓡T的指針類型各不一樣,所以使用tuple來存放,內(nèi)部就是對MAKE_ARG_LIST的調(diào)用,那我們也再跳轉(zhuǎn)到實(shí)現(xiàn)瞧瞧:

#define MACRO_CONCAT(A, B) MACRO_CONCAT1(A, B)
#define MACRO_CONCAT1(A, B) A##_##B

#define MAKE_ARG_LIST(N, op, arg, ...)  \
  MACRO_CONCAT(MAKE_ARG_LIST, N)(op, arg, __VA_ARGS__)

由于宏的一些特性,我們不得不使用MACRO_CONCAT1MACRO_CONCAT對宏與一些字符進(jìn)行拼接。這里是把MAKE_ARG_LIST和下劃線以及N進(jìn)行拼接,那么MAKE_ARG_LIST實(shí)際上調(diào)用的是MAKE_ARG_LIST_N,但是這里的N是實(shí)際的成員個(gè)數(shù),還是假定是3個(gè)成員,那么調(diào)用就是這樣的MAKE_ARG_LIST_3(op, arg, __VA_ARGS__),同時(shí)這里還用arg來拆出來第一個(gè)元素,類似于我們解參數(shù)包方式。

那么我們還需要再次去看MAKE_ARG_LIST_3的實(shí)現(xiàn):

#define MAKE_ARG_LIST_1(op, arg, ...) op(arg)
#define MAKE_ARG_LIST_2(op, arg, ...)   \
  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_1(op, __VA_ARGS__))
#define MAKE_ARG_LIST_3(op, arg, ...)  \
  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_2(op, __VA_ARGS__))
#define MAKE_ARG_LIST_4(op, arg, ...)  \
  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_3(op, __VA_ARGS__))

//...(略)

#define MAKE_ARG_LIST_32(op, arg, ...) \
  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_31(op, __VA_ARGS__))

因?yàn)?code>MAKE_ARG_LIST_N和成員個(gè)數(shù)有關(guān),這里也還是定義了32個(gè)宏,中間我們省略了很多,實(shí)現(xiàn)很簡單,先看MAKE_ARG_LIST_1,就是對op的調(diào)用,再看MAKE_ARG_LIST_2首先對第一個(gè)參數(shù)進(jìn)行op調(diào)用,剩下的參數(shù)去調(diào)用MAKE_ARG_LIST_1,然后使用逗號拼接。以此類推,如果是32的話,就是對32個(gè)參數(shù)分別op調(diào)用。

我們繼續(xù)跳回到成員指針獲取的那里:

#define FIELD(t) t

struct reflect_members {        
  constexpr decltype(auto) static apply_impl() {
    return std::make_tuple(                                
        MAKE_ARG_LIST(N, 
            &STRUCT_NAME::FIELD,__VA_ARGS__)
    );
  }
};                                                                     

我們明白了MAKE_ARG_LIST的含義,就是分別對各個(gè)參數(shù)進(jìn)行op操作,這里op正好對應(yīng)于&STRUCT_NAME::FIELD, FIELD就是一個(gè)封裝了一個(gè)括號方便調(diào)用,那也就是&STRUCT_NAME::各個(gè)成員,這也就是成員的指針。

最終推導(dǎo)展示

有了上邊的講解,我們使用clion可以看到最開始PersonREFLECTION(Person, a, b)表示的是啥:

增加成員類型

盡管可以通過指針獲取到各個(gè)成員的類型,但是為了使用方便,我們在reflect_members中增加一個(gè)各個(gè)成員的類型,我們使用類型列表來存放:

template<typename... Types>
struct TypeList {};

這樣reflect_members中成員類型列表可能實(shí)現(xiàn)就是這樣:

struct reflect_members {
  using member_types = TypeList<
    MAKE_ARG_LIST(N, decltype, MAKE_ARG_LIST(N,
        STRUCT_NAME::FIELD, __VA_ARGS__))
    >;
};

可以看到,TypeList中使用兩個(gè)MAKE_ARG_LIST嵌套實(shí)現(xiàn),首先對每個(gè)成員參數(shù)STRUCT_NAME::FIELD操作,然后在對操作后的成員參數(shù)進(jìn)行decltype操作,以上面Person為例可以看到最終member_types是這樣的:

using member_types = TypeList<decltype(Person::a), decltype(Person::b)>;

然后我們?nèi)绾问褂肨ypeList,需要配套一些操作方法,我這里目前只實(shí)現(xiàn)了根據(jù)順序來獲取成員的類型,類似這樣:

using MemberTypes = Members::member_types;
   
TypeByIndex<0, MemberTypes>::type a1 = 12;    // int
TypeByIndex<1, MemberTypes>::type b1 = 12.87; // float

我們也簡單看下TypeByIndex如何實(shí)現(xiàn):

template<int Index, typename TL>
struct TypeByIndex {
    using type = typename TypeByIndex<Index - 1, 
      typename ListPop<TL>::type>::type;
};

template<typename TypeList>
struct TypeByIndex<0, TypeList> {
    using type = typename ListHead<TypeList>::type;
};

TypeByIndex模板元函數(shù)實(shí)現(xiàn)如上,針對于Index為0進(jìn)行特化,那就是說只需要獲取TypeList中第一個(gè)類型即可,也就是這里的ListHead。否則就走主模板,主模板是一個(gè)遞歸的,將Index減1,TypeList給pop出第一個(gè)元素,也即ListPop操作,繼續(xù)調(diào)用TypeByIndex,直到Index為0,正好對應(yīng)于相對應(yīng)的類型。

也看下ListHeadListPop的實(shí)現(xiàn):

template<typename TL>
struct ListHead;

template<typename Head, typename... Args>
struct ListHead<TypeList<Head, Args...>> {
    using type = Head;
};

template<typename Tp>
struct ListPop {
    using type = TypeList<>;
};

template<typename Head, typename... Args>
struct ListPop<TypeList<Head, Args...>> {
    using type = TypeList<Args...>;
};
  • 先看ListHead,主模板僅僅是一個(gè)聲明,特化版本特化出來TypeList<Head, Args...>直接獲取到Head。
  • 再來看ListPop,主模板認(rèn)為是一個(gè)空的TypeList,特化模板則是特化出來TypeList<Head, Args...>形式,那樣正好把第一個(gè)元素和后邊元素分開,進(jìn)一步拿到后邊類型重新組裝成新的TypeList。

總結(jié)

這里我們使用宏來實(shí)現(xiàn)了結(jié)構(gòu)體(或類)成員的反射,包括成員的個(gè)數(shù),成員的指針,成員的類型。有了這些我們就可以做一些基本的操作了,比如說一些序列化結(jié)構(gòu)體等等。

我們還展示了TypeList及相關(guān)的簡單操作。當(dāng)然你如果需要的話,也可以將TypeList操作豐富起來。

以上就是C++利用宏實(shí)現(xiàn)類成員反射詳解的詳細(xì)內(nèi)容,更多關(guān)于C++反射的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言實(shí)現(xiàn)三角函數(shù)表

    C語言實(shí)現(xiàn)三角函數(shù)表

    這篇文章主要為大家詳細(xì)介紹了C語言三角函數(shù)表,打印出相對應(yīng)的三角函數(shù)值,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • LoadLibrary深入案例詳解

    LoadLibrary深入案例詳解

    這篇文章主要介紹了LoadLibrary深入案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C++ 的三種訪問權(quán)限與三種繼承方式

    C++ 的三種訪問權(quán)限與三種繼承方式

    我們知道C++中的類,有三種訪問權(quán)限(也稱作訪問控制),它們分別是public、protected、private,C++中繼承的方式還有多種。下面通過本文給大家詳細(xì)介紹,對c++中的訪問權(quán)限和繼承方式感興趣的朋友一起看看吧
    2016-11-11
  • C++中與輸入相關(guān)的istream類成員函數(shù)簡介

    C++中與輸入相關(guān)的istream類成員函數(shù)簡介

    這篇文章主要介紹了C++中與輸入相關(guān)的istream類成員函數(shù)簡介,包括eof函數(shù)和peek函數(shù)以及putback函數(shù)還有ignore函數(shù),需要的朋友可以參考下
    2015-09-09
  • c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程

    c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程

    拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),它在創(chuàng)建對象時(shí),是使用同一類中之前創(chuàng)建的對象來初始化新創(chuàng)建的對象。拷貝構(gòu)造函數(shù)通常用于:通過使用另一個(gè)同類型的對象來初始化新創(chuàng)建的對象。?復(fù)制對象把它作為參數(shù)傳遞給函數(shù)。復(fù)制對象,并從函數(shù)返回這個(gè)對象
    2022-05-05
  • 淺析C++模板類型中的原樣轉(zhuǎn)發(fā)和可變參數(shù)的實(shí)現(xiàn)

    淺析C++模板類型中的原樣轉(zhuǎn)發(fā)和可變參數(shù)的實(shí)現(xiàn)

    可變參數(shù)模板(variadic templates)是C++11新增的強(qiáng)大的特性之一,它對模板參數(shù)進(jìn)行了高度泛化,能表示0到任意個(gè)數(shù)、任意類型的參數(shù),這篇文章主要介紹了C++可變參數(shù)模板的展開方式,需要的朋友可以參考下
    2022-08-08
  • 基于Matlab制作一個(gè)數(shù)獨(dú)求解器

    基于Matlab制作一個(gè)數(shù)獨(dú)求解器

    這篇文章主要為大家詳細(xì)介紹了如何利用Matlab制作一個(gè)數(shù)獨(dú)求解器,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Matlab有一定幫助,需要的可以參考一下
    2022-05-05
  • C語言Tinyhttpd服務(wù)器源碼剖析

    C語言Tinyhttpd服務(wù)器源碼剖析

    這篇文章主要為大家介紹了C語言Tinyhttpd服務(wù)器源碼剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Qt實(shí)現(xiàn)發(fā)送HTTP請求的示例詳解

    Qt實(shí)現(xiàn)發(fā)送HTTP請求的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何通過Qt實(shí)現(xiàn)發(fā)送HTTP請求,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-03-03
  • C++引用、內(nèi)聯(lián)函數(shù)與nullptr超全解析(新手避坑指南)

    C++引用、內(nèi)聯(lián)函數(shù)與nullptr超全解析(新手避坑指南)

    這篇文章主要介紹了C++引用、內(nèi)聯(lián)函數(shù)與nullptr的相關(guān)資料,引用提供別名,簡化代碼和提高性能,內(nèi)聯(lián)函數(shù)通過減少棧幀開銷優(yōu)化性能,但不能濫用,nullptr替代NULL,提供類型安全的空指針表示,需要的朋友可以參考下
    2025-12-12

最新評論

正镶白旗| 酉阳| 白玉县| 嘉黎县| 吉林省| 北海市| 定结县| 禄丰县| 四会市| 无棣县| 句容市| 星座| 东乡族自治县| 疏勒县| 九寨沟县| 开阳县| 高密市| 卓资县| 定日县| 宁阳县| 阜康市| 禹城市| 斗六市| 湛江市| 怀柔区| 金乡县| 香港| 龙岩市| 昌吉市| 东乡县| 龙胜| 巢湖市| 子洲县| 周口市| 巴塘县| 车险| 嘉鱼县| 甘泉县| 忻州市| 泌阳县| 揭阳市|