<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C++反射:深入探究function實(shí)現(xiàn)機(jī)制!

          共 21171字,需瀏覽 43分鐘

           ·

          2022-03-02 07:47


          導(dǎo)語(yǔ)?|?本文將深入Function這部分進(jìn)行介紹,主要內(nèi)容是如何利用模板完成對(duì)C++函數(shù)的類(lèi)型擦除,以及如何在運(yùn)行時(shí)調(diào)用類(lèi)型擦除后的函數(shù)。有的時(shí)候我們需要平衡類(lèi)型擦除與性能的沖突,所以本文也會(huì)以lua function wrapper這種功能為例,簡(jiǎn)單介紹這部分。


          在上篇C++反射:全面解讀property的實(shí)現(xiàn)機(jī)制!中我們對(duì)反射中的Property實(shí)現(xiàn)做了相關(guān)的介紹,本篇將深入Function這部分進(jìn)行介紹。


          一、 Function示例代碼


          //-------------------------------------//declaration//-------------------------------------class Vector3 { public:  double x;  double y;  double z; public:  Vector3() : x(0.0), y(0.0), z(0.0) {}  Vector3(double _x, double _y, double _z) : x(_x), y(_y), z(_z) {}  double DotProduct(const Vector3& vec) const;};
          //-------------------------------------//register code//-------------------------------------__register_type("Vector3") .constructor() .constructor() .function("DotProduct", &Vector3::DotProduct);
          //-------------------------------------//use code//-------------------------------------auto* metaClass = __type_of();ASSERT_TRUE(metaClass != nullptr);
          auto obj = runtime::CreateWithArgs(*metaClass, Args{1.0, 2.0, 3.0});ASSERT_TRUE(obj != UserObject::nothing);
          const reflection::Function* dotProductFunc = nullptr;metaClass->TryGetFunction("DotProduct", dotProductFunc);ASSERT_TRUE(dotProductFunc != nullptr);math::Vector3 otherVec(1.0, 2.0, 3.0);auto dotRet = runtime::Call(*dotProductFunc, obj, otherVec);ASSERT_DOUBLE_EQ(dotRet.to<double>(), 14.0);


          (一)注冊(cè)的代碼


          上述代碼中,我們通過(guò)__register_type()創(chuàng)建的ClassBuilder提供的。function(name,func)函數(shù)來(lái)完成注冊(cè)。


          __register_type("Vector3").function("DotProduct", &Vector3::DotProduct);


          上例中我們就將Vector3::DotProduct()函數(shù)注冊(cè)到MetaClass中了。



          (二)使用的代碼


          運(yùn)行時(shí)我們獲取到的也是類(lèi)型擦除后的Function對(duì)象,如上例中的 dotProductFunc,所以運(yùn)行時(shí)我們需要通過(guò)runtime命名空間下提供的輔助設(shè)施runtime::call()來(lái)完成對(duì)應(yīng)函數(shù)的調(diào)用,c++的動(dòng)態(tài)版函數(shù)類(lèi)型擦除后的入口參數(shù)是統(tǒng)一的Args,出口參數(shù)是Value,runtime::call()提供了任意輸入?yún)?shù)到Args的轉(zhuǎn)換,如下所示, 我們即可完成對(duì)obj對(duì)象上的DotProduct函數(shù)的調(diào)用:


          auto dotRet = runtime::Call(*dotProductFunc, obj, otherVec);



          (三)整體文章的展開(kāi)思路


          本篇文章的展開(kāi)思路與Property那篇基本保持一致:


          • 一些基本知識(shí)。


          • 運(yùn)行時(shí)函數(shù)的表達(dá)-Function類(lèi)。


          • 反射函數(shù)的注冊(cè)。


          • Lua版本反射函數(shù)的實(shí)現(xiàn)。


          • 反射函數(shù)的運(yùn)行時(shí)分析。



          二、?基本知識(shí)


          Function Traits和Type Traits在c++11推出后都逐漸變得成熟,一個(gè)適配C++14/17的函數(shù)&類(lèi)型萃取庫(kù)對(duì)于像反射這種庫(kù)也是至關(guān)重要的,但Function Traits和Type Traits本質(zhì)還是依賴(lài)SIFINAE做各種類(lèi)型特化和推導(dǎo),屬于細(xì)節(jié)非常多但真正的技巧比較少的部分,本文就直接略過(guò)對(duì)Function Traits和Type Traits細(xì)節(jié)的分析推導(dǎo),假定Function Traits和Type Traits已經(jīng)是成熟穩(wěn)定的代碼部分,我們基于這部分穩(wěn)定代碼做上層的設(shè)計(jì)編碼。


          另外本文主要分析函數(shù)部分的處理過(guò)程,所以主要關(guān)注Function Traits的提供的特性,而不對(duì)每種函數(shù)的特化實(shí)現(xiàn)進(jìn)行展開(kāi)。


          反射庫(kù)所使用的TFunctionTratis包含的主要信息如下圖所示:



          (一)TFunctionTraits<>::kind


          FunctionKind枚舉,主要有以下值:


          /** * \brief Enumeration of the kinds of function recognised * * \sa Function */enum class FunctionKind{    kNone,               ///< not a function    kFunction,           ///< a function    kMemberFunction,     ///< function in a class or struct    kFunctionWrapper,    ///< `std::function<>`    kBindExpression,     ///< `std::bind()`    kLambda              ///< lambda function `[](){}`};



          ?(二)TFunctionTraits<>::ExposedType


          返回值類(lèi)型。



          (三)TFunctionTraits<>::Details::FunctionCallTypes


          std::tuple<>類(lèi)型,函數(shù)所有參數(shù)的tuple<>類(lèi)型,注意類(lèi)的成員函數(shù)首個(gè)參數(shù)是類(lèi)對(duì)象本身。



          三、?運(yùn)行時(shí)函數(shù)的表達(dá)——Function類(lèi)


          為了實(shí)現(xiàn)類(lèi)中函數(shù)的動(dòng)態(tài)調(diào)用過(guò)程,我們需要對(duì)類(lèi)的成員函數(shù)進(jìn)行類(lèi)型擦除,形成統(tǒng)一的MetaFunction后,方便運(yùn)行時(shí)獲取和調(diào)用,以獲得運(yùn)行時(shí)的動(dòng)態(tài)調(diào)用能力。在framework反射庫(kù)的實(shí)現(xiàn)中,Function是一個(gè)虛基類(lèi),定義如下(節(jié)選):


          class Function : public Type { public:  inline IdReturn name() const { return name_; }
          inline IdReturn class_name() const { return class_name_; }
          FunctionKind kind() const { return kind_; }
          ValueKind return_type() const;
          policy::ReturnKind return_policy() const;
          virtual size_t GetParamCount() const = 0;
          virtual ValueKind GetParamType(size_t index) const = 0;
          virtual std::string_view GetParamTypeName(size_t index) const = 0;
          virtual TypeId GetParamTypeIndex(size_t index) const = 0;
          virtual TypeId GetParamBaseTypeIndex(size_t index) const = 0;
          virtual TypeId GetReturnTypeIndex() const = 0;
          virtual TypeId GetReturnBaseTypeIndex() const = 0;
          virtual bool ArgsMatch(const Args& arg) const = 0;};


          接口包括獲取函數(shù)名,父類(lèi)名,也包括像獲取調(diào)用參數(shù)個(gè)數(shù),類(lèi)型,返回值類(lèi)型這些常規(guī)方法,不一一列舉了。需要注意的是并沒(méi)有Invoke的方法,這個(gè)主要是因?yàn)椴煌猛?如純C++的調(diào)用,和for lua的Invoke,類(lèi)型擦除后的調(diào)用方式會(huì)略有差異)。C++的調(diào)用(依托Args和Value來(lái)完成調(diào)用參數(shù)和返回值類(lèi)型的統(tǒng)一):


           virtual Value Execute(const Args& args) const = 0;


          lua的調(diào)用(依托Lua虛擬機(jī)的調(diào)用機(jī)制來(lái)完成函數(shù)類(lèi)型的統(tǒng)一):


          virtual int CallStraight(lua_State* L) const = 0;



          四、反射函數(shù)的注冊(cè)


          函數(shù)的注冊(cè)過(guò)程本質(zhì)上是類(lèi)的成員函數(shù),經(jīng)由類(lèi)型擦除后,變?yōu)榻y(tǒng)一的類(lèi)型(上一節(jié)中Function對(duì)象)存入MetaClass中組織起來(lái),方便運(yùn)行時(shí)動(dòng)態(tài)使用的過(guò)程。大致流程如下(略過(guò)declare<>獲取ClassBuilder的這步)


          (一)從ClassBuilder創(chuàng)建一個(gè)function說(shuō)起


          template template ClassBuilder& ClassBuilder::function(IdRef name, F function, P... policies){    // Construct and add the metafunction    return addFunction(detail::newFunction(name, function, policies...));}



          (二)由newFunction()到FunctionImpl(),真正實(shí)現(xiàn)函數(shù)類(lèi)型擦除的地方


          // Used by ClassBuilder to create new function instance.template <typename F, typename... P>static inline Function* newFunction(IdRef name, F function, P... policies){    typedef detail::FunctionTraits FuncTraits;        static_assert(FuncTraits::kind != FunctionKind::None, "Type is not a function");        return new FunctionImpl(name, function, policies...);}


          (注意此處對(duì)FuncTraits的使用,另外框架相關(guān)單元測(cè)試?yán)镆步o出了大量的Ponder Type Traits的測(cè)試代碼。)



          (三)FunctionImpl()的具體實(shí)現(xiàn)


          FunctionImpl(IdRef name, F function, P... policies) : Function(name){    m_name = name;    m_funcType = FuncTraits::kind;    m_returnType = mapType();    m_returnPolicy = ReturnPolicy ::kind;    m_paramInfo = FunctionApplyToParams    FunctionMapParamsToValueKind>::foreach();    Function::m_usesData = &m_userData;
          processUses(m_name, function); PONDER_IF_LUA(processUses(m_name, function);)}


          注意ponder實(shí)現(xiàn)函數(shù)多用途的方式,用了一個(gè)枚舉的模板和相關(guān)的特化實(shí)現(xiàn),打開(kāi)Lua支持后,會(huì)執(zhí)行兩次processUses<>,分別對(duì)應(yīng)processUses()和processUses< uses::Uses::eLuaModule >,一個(gè)用來(lái)實(shí)現(xiàn)標(biāo)準(zhǔn)的C++反射支持,另外一個(gè)則是用于Lua的導(dǎo)出支持。


          這個(gè)地方的實(shí)現(xiàn)比較復(fù)雜,Ponder借助了一些輔助的設(shè)施來(lái)完成同一函數(shù)不同用途的注冊(cè)方式的分離,我們先來(lái)看一下這些輔助設(shè)施的定義,再結(jié)合processUses<>()簡(jiǎn)單說(shuō)明實(shí)現(xiàn)機(jī)制:


          /** * \brief Global information on the compile-time type Uses. * *  - This can be extended for other modular uses */struct Uses{    enum {        eRuntimeModule,                 ///< Runtime module enumeration        PONDER_IF_LUA(eLuaModule,)      ///< Lua module enumeration        eUseCount    };         /// Metadata uses we are using.    typedef std::tuple                       PONDER_IF_LUA(,LuaUse)                      > Users;
          /// Type that stores the per-function uses data typedef std::tuple< runtime::detail::FunctionCaller* PONDER_IF_LUA(,lua::detail::FunctionCaller*) > PerFunctionUserData; // Access note: // typedef typename std::tuple_element::type PerFunc_t; // PerFunc_t* std::get(getUsesData());};


          此處定義了兩個(gè)tuple,根據(jù)相關(guān)的定義也能大概猜到,大致是通過(guò)定義的enum值去匹配相關(guān)tuple中不同位置type的一種做法,能夠比較好的實(shí)現(xiàn)基于enum->tuple index->types的一種dispatcher,compiler階段就能完成的匹配,還是比較巧妙的,后續(xù)會(huì)結(jié)合具體的代碼說(shuō)明這部分的詳細(xì)使用。



          (四)processUse<>的具體實(shí)現(xiàn)


          processUses<>的代碼實(shí)現(xiàn)如下:


          uses::Uses::PerFunctionUserData m_userData;
          template <int M>void processUses(IdRef name, F function){ typedef typename std::tuple_element::type Processor;
          std::get(m_userData) = Processor::template perFunction(name, function);}


          主要是對(duì)上文中的Uses結(jié)構(gòu)體中的兩個(gè)tuple類(lèi)型的使用(Uses::PerFunctionData,Uses::Users),以枚舉值 eRuntimeModule,eLuaModule作為processUses的非類(lèi)型模板參數(shù),兩次調(diào)用該模板函數(shù),我們即可得到兩個(gè)不同類(lèi)型的FunctionCaller存儲(chǔ)至m_userData,這部分只包含了對(duì)tuple的訪問(wèn)(std::tuple_element<>,std::get<>()),通過(guò)Uses結(jié)構(gòu)體的特殊構(gòu)造和tuple的輔助函數(shù),可以借助不同的enum值來(lái)完成不同用途和不同類(lèi)型的FunctionCaller的生成和存儲(chǔ)。大部分是編譯期行為,很值得借鑒的一種方式。


          下面我們來(lái)具體看一下Ponder完成函數(shù)類(lèi)型擦除的過(guò)程,也就是上述Process::template perFunction<>()的具體實(shí)現(xiàn) (注意此處template關(guān)鍵字的作用是告訴編譯器perFunction本身也是模板函數(shù),不加在GCC等編譯器上可能會(huì)報(bào)錯(cuò))。



          (五)C++版本反射函數(shù)的實(shí)現(xiàn)(RuntimeUse::perFunction())


          我們先來(lái)看一下RuntimeUse::perFunction()的實(shí)現(xiàn):


          struct RuntimeUse{    /// Factory for per-function runtime data    template <typename F, typename FTraits, typename Policies_t>    static runtime::detail::FunctionCaller* perFunction(IdRef name, F function){        return new runtime::detail::FunctionCallerImpl(name, function);    }};


          perFunction的作用主要是完成對(duì)不同函數(shù)(參數(shù)與返回值可能都不一樣)的類(lèi)型擦除,形成統(tǒng)一類(lèi)型的FunctionCaller。下面我們具體來(lái)看一下FunctionCallerImpl<>的具體實(shí)現(xiàn)。


          Ponder C++反射實(shí)現(xiàn)函數(shù)類(lèi)型擦除的方式比較特殊,不是通過(guò)得到一個(gè)統(tǒng)一類(lèi)型的函數(shù)對(duì)象來(lái)實(shí)現(xiàn)的類(lèi)型擦除,而是通過(guò)類(lèi)繼承和虛函數(shù)的方式來(lái)實(shí)現(xiàn)的類(lèi)型擦除,代碼如下:


          //-----------------------------------------------------------------------------// Base for runtime function caller
          class FunctionCaller{public: FunctionCaller(const IdRef name) : m_name(name) {} virtual ~FunctionCaller() {}
          FunctionCaller(const FunctionCaller&) = delete; // no copying const IdRef name() const { return m_name; } virtual Value execute(const Args& args) const = 0; private: const IdRef m_name;};
          // The FunctionImpl class is a template which is specialized according to the// underlying function prototype.template <typename F, typename FTraits, typename FPolicies>class FunctionCallerImpl final : public FunctionCaller{public:
          FunctionCallerImpl(IdRef name, F function) : FunctionCaller(name) , m_function(function) {} private:
          typedef typename FTraits::Details::FunctionCallTypes CallTypes; typedef FunctionWrapper<typename FTraits::ExposedType, CallTypes> DispatchType; typename DispatchType::Type m_function; // Object containing the actual function to call Value execute(const Args& args) const final{ return DispatchType::template call<decltype(m_function), FTraits, FPolicies>(m_function, args); }};


          如上所示,特化的FunctionCallerImpl<>會(huì)實(shí)現(xiàn)基類(lèi)的Value excute(const Args& args)方法,基類(lèi)的excute方法的參數(shù)和返回值是固定的,這樣我們針對(duì)不同的函數(shù)會(huì)最終得到一個(gè)有統(tǒng)一excute()函數(shù)的FunctionCaller對(duì)象,間接完成了函數(shù)的類(lèi)型擦除。(另外一種方式是通過(guò)模板推導(dǎo)存儲(chǔ)一個(gè)固定參數(shù)表和返回值的lambda,也可以完成函數(shù)的類(lèi)型擦除)


          我們上述僅介紹了ponder內(nèi)部最終存儲(chǔ)函數(shù)的方式和基本的使用形式( 統(tǒng)一的excute()接口),具體的函數(shù)到最終存儲(chǔ)形式的過(guò)程被忽略了,這里基于前文提到的成熟的Function Traits功能展開(kāi)一下中間的處理部分。


          • FunctionWrapper<>模板類(lèi)


          通過(guò)FunctionWrapper<>模板類(lèi)完成std::function<>函數(shù)對(duì)象的生成以及統(tǒng)一參數(shù)和返回值的call<>()方法的支持。注意FunctionCallerImpl中對(duì)FunctionWrapper類(lèi)的使用:


          typedef typename FTraits::Details::FunctionCallTypes CallTypes;typedef FunctionWrapper<typename FTraits::ExposedType, CallTypes> DispatchType;


          注意此處使用Function Traits直接為FunctionWrapper提供參數(shù)列表和返回值(FunctionTraits<>::Details::FunctionCallTypes和FunctionTraits<>::ExposedType)。


          FunctionWrapper的代碼以及使用到的CallHelper的實(shí)現(xiàn)代碼如下:


          template <typename R, typename FTraits, typename FPolicies>class CallHelper{public:
          template<typename F, typename... A, size_t... Is> static Value call(F func, const Args& args, std::index_sequence){ typedef typename ChooseCallReturner::type CallReturner; return CallReturner::value(func(ConvertArgs::convert(args, Is)...)); }};//-----------------------------------------------------------------------------// Convert traits to callable function wrapper. Generic for all function types.template <typename R, typename A> struct FunctionWrapper;template <typename R, typename... A> struct FunctionWrapper>{ typedef typename std::function Type; template <typename F, typename FTraits, typename FPolicies> static Value call(F func, const Args& args){ typedef std::make_index_sequence<sizeof...(A)> ArgEnumerator; return CallHelper::template call(func, args, ArgEnumerator()); }};


          此處重點(diǎn)關(guān)注std::make_index_sequence<>和std::index_sequence<>的使用,借助index_sequence相關(guān)的函數(shù),我們可以很方便的對(duì)varidic template進(jìn)行處理,此處通過(guò)index_sequence的使用,我們可以很好的完成args中包含的arg到函數(shù)需要的正確類(lèi)型參數(shù)的轉(zhuǎn)換:


          ConvertArgs<A>::convert(args, Is)...


          ConvertArgs<>和ChooseCallReturner<>一個(gè)是將從args中取到的Value置換為具體類(lèi)型的參數(shù),一個(gè)是將具體類(lèi)型的返回值置換為Value,通過(guò)這種方式,最終實(shí)現(xiàn)了函數(shù)的調(diào)用參數(shù)和返回值的統(tǒng)一,通過(guò)這段代碼,我們也能看到在C++14/17后,相關(guān)的函數(shù)類(lèi)型擦除的代碼對(duì)比原來(lái)的實(shí)現(xiàn)會(huì)簡(jiǎn)化非常多,已經(jīng)很容易理解了。


          另外,對(duì)于沒(méi)有返回值的函數(shù),也有專(zhuān)門(mén)特化的CallHelper,代碼如下:


          // Specialization of CallHelper for functions returning voidtemplate <typename FTraits, typename FPolicies>class CallHelper{public:
          template<typename F, typename... A, size_t... Is> static Value call(F func, const Args& args, PONDER__SEQNS::index_sequence){ func(ConvertArgs
          ::convert(args,Is)...); return Value::nothing; }};


          對(duì)比有返回值的版本,差異主要是直接返回Value::nothing,所以我們也可以簡(jiǎn)單的通過(guò)call的返回值是否為Value::nothing來(lái)判斷反射函數(shù)是否有返回值,這也是Rpc庫(kù)使用的方式。


          上面我們有提到ConvertArgs<>和ChooseCallReturner<>,通過(guò)這兩者我們很好的實(shí)現(xiàn)了調(diào)用函數(shù)的參數(shù)統(tǒng)一以及返回值統(tǒng)一,這里我們也對(duì)其實(shí)現(xiàn)做一下具體的拆解,當(dāng)然, 主要的類(lèi)型轉(zhuǎn)換的實(shí)現(xiàn)其實(shí)更多的是依賴(lài)Value和UserObject本身的實(shí)現(xiàn),此處我們不對(duì)這兩者做具體的展開(kāi),與Function Traits一樣,我們把這兩者當(dāng)成即有成熟功能,來(lái)方便理清函數(shù)類(lèi)型擦除相關(guān)的核心代碼。



          CovertArgs<>整體實(shí)現(xiàn)代碼如下:


          //-----------------------------------------------------------------------------    /* * Helper function which converts an argument to a C++ type * * The main purpose of this function is to convert any BadType error to * a BadArgument one. */template <int TFrom, typename TTo>struct ConvertArg{    typedef typename std::remove_reference::type ReturnType;    static ReturnType    convert(const Args& args, size_t index){        try {            return args[index].to<typename std::remove_reference::type>();        }        catch (const BadType&) {            PONDER_ERROR(BadArgument(args[index].kind(), mapType(), index, "?"));        }    }};
          // Specialisation for returning references.template <typename TTo>struct ConvertArg<(int)ValueKind::User, TTo&>{ typedef TTo& ReturnType; static ReturnType convert(const Args& args, size_t index){ auto&& uobj = const_cast(args[index]).ref(); if (uobj.pointer() == nullptr) PONDER_ERROR(NullObject(&uobj.getClass())); return uobj.ref(); }};
          // Specialisation for returning const references.template <typename TTo>struct ConvertArg<(int)ValueKind::User, const TTo&>{ typedef const TTo& ReturnType; static ReturnType convert(const Args& args, size_t index){ auto&& uobj = args[index].cref(); if (uobj.pointer() == nullptr) PONDER_ERROR(NullObject(&uobj.getClass())); return uobj.cref(); }};
          //-----------------------------------------------------------------------------// Object function call helper to allow specialisation by return type. Applies policies.
          template <typename A>struct ConvertArgs{ typedef typename ponder::detail::DataType
          ::Type Raw; static constexpr ValueKind kind = ponder_ext::ValueMapper::kind; typedef ConvertArg<(int)kind, A> Convertor; static typename Convertor::ReturnType convert(const Args& args, size_t index){ return Convertor::convert(args, index); }};


          首先是templatestruct ConvertArg的實(shí)現(xiàn),前面的TForm是ValueKind值,后面的TTo是目標(biāo)類(lèi)型,對(duì)于非User類(lèi)型的Value,模板推導(dǎo)出的是最前面的實(shí)現(xiàn),最后直接執(zhí)行Value::to<>()模板函數(shù)來(lái)完成Value到目標(biāo)類(lèi)型的轉(zhuǎn)換,注意此處對(duì)于Covert錯(cuò)誤的處理是直接拋異常。后續(xù)的兩個(gè)特化實(shí)現(xiàn)分別針對(duì)reference和const reference,主要依賴(lài)UserObject的ref<>()和cref<>()模板函數(shù),最后就是CallHelper<>模板類(lèi)使用到的的templatestruct ConvertArgs實(shí)現(xiàn),其實(shí)就是對(duì)templatestruct ConvertArg的簡(jiǎn)單包裝。



          ChooseCallReturner<>的具體實(shí)現(xiàn)代碼如下:


          //-----------------------------------------------------------------------------// Handle returning copies    template <typename R, typename U = void> struct CallReturnCopy;
          template <typename R>struct CallReturnCopy::value>::type>{ static inline Value value(R&& o) {return Value(o);}};
          template <typename R>struct CallReturnCopy::value>::type>{ static_assert(!std::is_pointer::value, "Cannot return unowned pointer. Use ponder::policy::ReturnInternalRef?"); static inline Value value(R&& o) {return Value(UserObject::makeCopy(std::forward(o)));}};
          //-----------------------------------------------------------------------------// Handle returning internal references template <typename R, typename U = void> struct CallReturnInternalRef;
          template <typename R>struct CallReturnInternalRef typename std::enable_if< !ponder::detail::IsUserType::value && !std::is_same::Type, UserObject>::value >::type>{ static inline Value value(R&& o) {return Value(o);}};
          template <typename R>struct CallReturnInternalRef typename std::enable_if< ponder::detail::IsUserType::value || std::is_same::Type, UserObject>::value >::type>{ static inline Value value(R&& o) {return Value(UserObject::makeRef(std::forward(o)));}};
          //-----------------------------------------------------------------------------// Choose which returner to use, based on policy// - map policy kind to actionable policy type template <typename Policies_t, typename R> struct ChooseCallReturner;
          template <typename... Ps, typename R>struct ChooseCallReturner, R>{ typedef CallReturnCopy type;};
          template <typename... Ps, typename R>struct ChooseCallReturner, R>{ typedef CallReturnInternalRef type;};
          template <typename R>struct ChooseCallReturner, R> // default{ typedef CallReturnCopy type;};
          template <typename P, typename... Ps, typename R>struct ChooseCallReturner, R> // recurse{ typedef typename ChooseCallReturner<std::tuple, R>::type type;};


          此處注意注意Return Policy的實(shí)現(xiàn),通過(guò)policy::ReturnCopy和policy::ReturnInternalRef我們可以控制Value的創(chuàng)建方式,默認(rèn)是Copy方式創(chuàng)建Value,其余的主要是Value本身支持從不同類(lèi)型T構(gòu)造的特性來(lái)完成的。


          Value對(duì)不同類(lèi)型T的支持特性可以自行查閱Value的實(shí)現(xiàn),目前版本的Value的內(nèi)部通過(guò)ponder自己實(shí)現(xiàn)的variants來(lái)完成對(duì)不同類(lèi)型T的存取,但其實(shí)第一版的ponder重度依賴(lài)boost,所以第一版的實(shí)現(xiàn)也是直接使用的boost::variants,后續(xù)V2版本解除了對(duì)boost的依賴(lài),但variants的實(shí)現(xiàn)也大量參考了boost的實(shí)現(xiàn),所以對(duì)這部分細(xì)節(jié)感興趣的可以直接查閱boost::variants相關(guān)的文檔和源碼,更容易理解其中的細(xì)節(jié)。



          五、?Lua版本反射函數(shù)的實(shí)現(xiàn)

          ?——LuaUse::perFunction()


          LuaUse::perFunction()的目的與C++反射函數(shù)的目的一致,也是完成對(duì)普通函數(shù)的類(lèi)型擦除,形成統(tǒng)一的函數(shù)對(duì)象類(lèi)型,只是生成的統(tǒng)一FunctionCaller對(duì)象不同。


          struct LuaUse{    /// Factory for per-function runtime data    template <typename F, typename FTraits, typename Policies_t>    static lua::detail::FunctionCaller* perFunction(IdRef name, F function){        return new lua::detail::FunctionCallerImpl(name, function);    }};


          具體的實(shí)現(xiàn)與上一節(jié)的很多地方都一樣,我們主要關(guān)注針對(duì)Lua的那部分特性。


          // Base for runtime function callerclass FunctionCaller{public:    FunctionCaller(const IdRef name, int (*fn)(lua_State*) = nullptr)        :   m_name(name)        ,   m_luaFunc(fn)    {}        FunctionCaller(const FunctionCaller&) = delete; // no copying    virtual ~FunctionCaller() {}        const IdRef name() const { return m_name; }
          void pushFunction(lua_State* L){ lua_pushlightuserdata(L, (void*) this); lua_pushcclosure(L, m_luaFunc, 1); } private: const IdRef m_name; int (*m_luaFunc)(lua_State*);};
          // The FunctionImpl class is a template which is specialized according to the// underlying function prototype.template <typename F, typename FTraits, typename FPolicies>class FunctionCallerImpl : public FunctionCaller{public: FunctionCallerImpl(IdRef name, F function) : FunctionCaller(name, &call) , m_function(function) {}private: typedef FunctionCallerImpl ThisType; typedef typename FTraits::Details::FunctionCallTypes CallTypes; typedef FunctionWrapper<typename FTraits::ExposedType, CallTypes> DispatchType; typename DispatchType::Type m_function; // Object containing the actual function to call static int call(lua_State *L){ lua_pushvalue(L, lua_upvalueindex(1)); ThisType *self = reinterpret_cast(lua_touserdata(L, -1)); lua_pop(L, 1); return DispatchType::template call<decltype(m_function), FTraits, FPolicies>(self->m_function, L); }};


          首先看到的差異點(diǎn)是FunctionCaller對(duì)象上的m_luaFunc成員:


          int (*m_luaFunc)(lua_State*);


          以及pushFunction()成員函數(shù):


           void pushFunction(lua_State* L)    {        lua_pushlightuserdata(L, (void*) this);        lua_pushcclosure(L, m_luaFunc, 1);    }


          先忽略類(lèi)型擦除的過(guò)程,我們先來(lái)看Lua版的FunctionCaller,對(duì)比C++的FunctionCaller,差異之處為所有函數(shù)會(huì)被處理為標(biāo)準(zhǔn)Lua C函數(shù)的類(lèi)型(lua_CFunction類(lèi)型,int為返回值,lua_State*作為入口參數(shù)),另外通過(guò)額外多出來(lái)的pushFunction()函數(shù)可以將m_luaFunc作為c closure入棧,當(dāng)然FunctionCaller本身的this指針被當(dāng)成light userdata作為這個(gè)c closure的up value被傳入lua虛擬機(jī)中。


          我們接下來(lái)看看FunctionCallerImpl,對(duì)比C++版的實(shí)現(xiàn),區(qū)別最大的是call函數(shù),此處的call函數(shù)也是個(gè)lua_CFunction類(lèi)型的函數(shù),同時(shí)我們也很容易觀察到生成的靜態(tài)call函數(shù)被當(dāng)成構(gòu)造函數(shù)的參數(shù),最終賦值給了FunctionCaller內(nèi)的m_luaFunc,我們知道Lua與C++的交互主要是通過(guò)lua_State來(lái)完成的,要在Lua中調(diào)用C++函數(shù),我們需要間接的通過(guò)lua_State來(lái)傳入?yún)?shù)和輸出返回值,所以對(duì)應(yīng)的FunctionWrapper對(duì)比C++版本也是特殊實(shí)現(xiàn)的,并且都帶入了lua_State作為額外的參數(shù)。類(lèi)同上文第二部分第四小節(jié), 我們也深入分析FunctionWrapper的實(shí)現(xiàn)以及從Lua虛擬機(jī)上傳入?yún)?shù)以及傳出返回值的過(guò)程。


          (一)FunctionWrapper<>模板類(lèi)


          template <typename R, typename FTraits, typename FPolicies>class CallHelper{public:        template<typename F, typename... A, size_t... Is>    static int call(F func, lua_State* L, std::index_sequence){        typedef typename ChooseCallReturner::type CallReturner;        return CallReturner::value(L, func(ConvertArgs::convert(L, Is)...));    }};//-----------------------------------------------------------------------------// Convert traits to callable function wrapper. Generic for all function types.template <typename R, typename P> struct FunctionWrapper;template <typename R, typename... P> struct FunctionWrapper>{    typedef typename std::function Type;        template <typename F, typename FTraits, typename FPolicies>    static int call(F func, lua_State* L){        typedef std::make_index_sequence<sizeof...(P)> ArgEnumerator;                return CallHelper::template call(func, L, ArgEnumerator());    }};


          與C++版本一致的部分我們不再展開(kāi)講解,首先我們注意到與C++版本一樣,F(xiàn)unctionCallerImpl中存儲(chǔ)的std::function函數(shù)對(duì)象類(lèi)型與C++版本實(shí)現(xiàn)一致,同樣,CallHelper也有無(wú)返回值的版本,主要差別是CovertArgs<>()和ChooseCallReturner<>()的實(shí)現(xiàn),都變成了帶lua_State參數(shù)的版本,原因也是顯而意見(jiàn)的,需要通過(guò)lua_State來(lái)交換需要的數(shù)據(jù),Lua版與C++版本的實(shí)現(xiàn)主要的差異也在這里,我們接下來(lái)具體看看這兩個(gè)模板函數(shù)的實(shí)現(xiàn)。



          (二)CovertArgs<>模板類(lèi)


          //-----------------------------------------------------------------------------// Object function call helper to allow specialisation by return type. Applies policies.template <typename P>struct ConvertArgs{    typedef LuaValueReader

          Convertor; static typename Convertor::ParamType convert(lua_State* L, size_t index){ return Convertor::convert(L, index+1); }};


          很容易發(fā)現(xiàn)Lua版的ConvertArgs僅是對(duì)LuaValueReader<>的簡(jiǎn)單包裝和使用,而閱讀LuaValueReader的實(shí)現(xiàn)發(fā)現(xiàn)是對(duì)各種數(shù)據(jù)類(lèi)型的特化實(shí)現(xiàn),包含了各種lua c api的訪問(wèn),比較特殊的是對(duì)lua table,c++側(cè)的UserObject等的處理,熟悉lua c api的話這些代碼都比較容易讀懂,此處不再展開(kāi)了,僅給出string_view的實(shí)現(xiàn)供參考:


          template <>struct LuaValueReader{    typedef ponder::detail::string_view ParamType;    static inline ParamType convert(lua_State* L, size_t index){        return ParamType(luaL_checkstring(L, (int)index));    }};



          (三)ChooseCallReturner<>模板類(lèi)


          // Handle returning copiestemplate <typename R, typename U = void> struct CallReturnCopy;template <typename R>struct CallReturnCopy::value>::type>{    // "no member named push" error here means the type returned is not covered.    static inline int value(lua_State *L, R&& o) {return LuaValueWriter::push(L, o);}};


          ChooseCallReturner<>因?yàn)镻olicy的存在,實(shí)現(xiàn)版本較多,此處僅貼出其中一個(gè)實(shí)現(xiàn)供參考。與CovertArgs一樣,ChooseCallRetruner<>也是對(duì)LuaValueWriter<>模板類(lèi)的包裝和使用,我們同樣給出其中一個(gè)LuaValueWriter的實(shí)現(xiàn)供參考:


          template <typename T>struct LuaValueWriter::value>::type>{    static inline int push(lua_State *L, T value){        return lua_pushnumber(L, value), 1;    }};



          (四)小結(jié)


          其實(shí)對(duì)于c++?->lua的Wrapper,我們當(dāng)然可以復(fù)用第4節(jié)的設(shè)施,直接針對(duì):


          virtual Value Execute(const Args& args) const = 0;


          包裝lua c function,也是極簡(jiǎn)單的,但考慮到性能,ponder的做法是復(fù)用了相關(guān)的Traits實(shí)現(xiàn),重新包裝了第5節(jié)的Function實(shí)現(xiàn),這樣可以得到更高性能的跨語(yǔ)言調(diào)用設(shè)施。所以很多時(shí)候,我們應(yīng)該是在整個(gè)系統(tǒng)不同層面去衡量性價(jià)比,像上述代碼實(shí)現(xiàn)不那么繁復(fù),又能夠得到更好的性能的實(shí)現(xiàn),我們肯定會(huì)更多考慮。


          通過(guò)上述C++版和Lua版的函數(shù)反射實(shí)現(xiàn),我們其實(shí)可以發(fā)現(xiàn)在Ponder已有的設(shè)施下,實(shí)現(xiàn)不同目的反射函數(shù)變得相當(dāng)?shù)暮?jiǎn)單,基于C++版本反射函數(shù)的實(shí)現(xiàn)思路,可以非常方便的實(shí)現(xiàn)其他目的版本的反射函數(shù)(如Lua版),這也是Ponder本身實(shí)現(xiàn)的完備和強(qiáng)大之處。


          另外對(duì)于lua bridge來(lái)說(shuō),光一個(gè)function的實(shí)現(xiàn)肯定是不夠的,后續(xù)將會(huì)相對(duì)完整的介紹怎么基于已有的c++反射特性來(lái)實(shí)現(xiàn)一個(gè)項(xiàng)目級(jí)的lua bridge。



          六、 反射函數(shù)的運(yùn)行時(shí)分析


          (一)c++::function的執(zhí)行分析


          與Property篇類(lèi)同,我們也給出一個(gè)運(yùn)行時(shí)的分析,方便大家更好的了解整個(gè)Function機(jī)制的運(yùn)轉(zhuǎn)方式。運(yùn)行時(shí)的測(cè)試選用的依然是最前面示例的代碼:


          math::Vector3 otherVec(1.0, 2.0, 3.0);auto dotRet = runtime::Call(*dotProductFunc, obj, otherVec);


          簡(jiǎn)潔起見(jiàn),僅給出最頂層call stack的展開(kāi):



          相關(guān)的最頂層代碼:



          最終執(zhí)行的模板實(shí)例格式化后如下所示:


          framework::reflection::runtime::detail::TCallHelper<    double /*return value type*/ ,framework::reflection::detail::TFunctionTraits<  //deduced TFunctionTraits<>        double (__cdecl framework::math::Vector3::*)(framework::math::Vector3 const &)const>, //Vector3::DotProduct() type    std::tuple<>>::Call<    std::functionconst &,framework::math::Vector3 const &)>,    framework::math::Vector3 const &,    framework::math::Vector3 const &,0,1>(  //argument list    const std::functionconst &,framework::math::Vector3 const &)> & func,     const framework::reflection::Args & args,     std::integer_sequence0,1> __formal)


          通過(guò)層層嵌套的模板特化,我們最后完成了運(yùn)行時(shí)函數(shù)的動(dòng)態(tài)調(diào)用。



          (二)lua::function的執(zhí)行分析


          lua::function的執(zhí)行與c++::function的執(zhí)行過(guò)程非常類(lèi)同,這里不重復(fù)展開(kāi),有興趣的同學(xué)可以自行嘗試。



          七、 總結(jié)


          至此整體反射的實(shí)現(xiàn)的理論介紹已經(jīng)靠一段路,本系列文章后續(xù)會(huì)繼續(xù)介紹剩下更側(cè)重應(yīng)用的幾篇:



          參考資料:

          1.[github ponder庫(kù)]?

          https://github.com/billyquith/ponder



          ?作者簡(jiǎn)介


          沈芳

          騰訊后臺(tái)開(kāi)發(fā)工程師

          IEG研發(fā)效能部開(kāi)發(fā)人員,畢業(yè)于華中科技大學(xué)。目前負(fù)責(zé)CrossEngine Server的開(kāi)發(fā)工作,對(duì)GamePlay技術(shù)比較感興趣。



          ?推薦閱讀


          C++反射:全面解讀property的實(shí)現(xiàn)機(jī)制!

          C++反射:深入淺出剖析ponder庫(kù)實(shí)現(xiàn)機(jī)制!

          擁抱云原生!COS數(shù)據(jù)湖加速器GooseFS存算分離實(shí)踐及性能優(yōu)化

          微前端究竟是什么?微前端核心技術(shù)揭秘!



          瀏覽 96
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操屄视频网站 | 人人爱,天天草 | 中文字幕无码日韩 | 国产精品粉嫩福利在线 | 亚洲 国产 另类 无码 日韩 |