diff --git a/BenchMarkReport_0.md b/BenchMarkReport_0.md new file mode 100644 index 00000000..d56ab3cb --- /dev/null +++ b/BenchMarkReport_0.md @@ -0,0 +1,58 @@ +# Reflection Template Library (RTL) — Benchmark Report + +This document presents benchmark results for the **Reflection Template Library (RTL)**. +The goal was to measure the runtime overhead of reflective function calls compared to direct calls and `std::function`, under increasing workloads. + +--- + +## Benchmark Setup + +We tested: + +- **Direct calls** (baseline). +- **`std::function` calls** and method calls. +- **RTL reflective calls** (free functions and member methods, with and without return values). + +Each benchmark was repeated across workloads of increasing complexity, with times measured in nanoseconds. + +--- + +## Results Summary + +| Workload | Direct Call (ns) | Reflected Call Overhead (ns) | Reflected Method Overhead (ns) | Notes (With Return) | +|-----------------|------------------|------------------------------|--------------------------------|---------------------| +| baseline_40ns | 39.0 / 44.7 | +2.5 | +6.6 | +10.6 / +14.3 | +| workload_80ns | 82.4 / 82.5 | ~0 | ~0 | +12.5 / +15.6 | +| workload_100ns | 94.2 / 100.0 | +1.4 | +8.8 | +12.0 / +16.0 | +| workload_150ns* | 139.0 / 158.0 | +2–3 | +14–17 | +12–13 / +17–19 | + +\*Three independent runs were recorded at ~150 ns workload; numbers are consistent. + +--- + +## Insights + +- **Constant Overhead** + Reflection overhead remains almost constant across workloads: + - No-return functions: **+2–6 ns**. + - Return-value functions: **+10–20 ns**. + +- **Percentage Overhead Shrinks** + - At the 40 ns baseline, overhead was ~25%. + - By ~150 ns workloads, overhead dropped below 10%. + +- **No Scaling Penalty** + The overhead does not grow with function complexity. + This indicates that RTL adds only a fixed, predictable cost per call, with no hidden allocations. + +- **Performance-Culture Friendly** + This aligns with C++’s ethos: *you only pay a small, predictable cost when you use reflection*. + +--- + +## Conclusion + +The Reflection Template Library (RTL) demonstrates: + +- **Runtime reflection with constant, minimal overhead**. +- **Predictable cost model**: ~10–20 ns for reflective calls with returns. diff --git a/CxxRTLTypeRegistration/inc/TestMirrorProvider.h b/CxxRTLTypeRegistration/inc/TestMirrorProvider.h index 9900dac5..42072a54 100644 --- a/CxxRTLTypeRegistration/inc/TestMirrorProvider.h +++ b/CxxRTLTypeRegistration/inc/TestMirrorProvider.h @@ -22,7 +22,7 @@ namespace test_mirror static std::size_t calender; static std::size_t char_t; - static std::size_t void_t; + static std::size_t int_t; static std::size_t std_string; static std::size_t std_string_view; diff --git a/CxxRTLTypeRegistration/src/TestMirrorProvider.cpp b/CxxRTLTypeRegistration/src/TestMirrorProvider.cpp index 980c4456..dd2ac277 100644 --- a/CxxRTLTypeRegistration/src/TestMirrorProvider.cpp +++ b/CxxRTLTypeRegistration/src/TestMirrorProvider.cpp @@ -37,15 +37,15 @@ namespace test_mirror --------------------------------- */ // Registering void, valid but not useful at all. - rtl::type().record("void").build(), + rtl::type().record("int").build(), // Registering type 'void' again, ignored & emits- // [WARNING] Multiple registrations of the same type detected. - rtl::type().record("void").build(), + rtl::type().record("int").build(), // Registering type 'void' again, but with different name. ignored & emits- // [WARNING] Multiple registrations of the same type detected. - rtl::type().record("ccvoid").build(), + rtl::type().record("ccint").build(), // Registering pod, reflecting- constructor, copy-constructor & destructor. rtl::type().record("char").build(), @@ -266,7 +266,7 @@ namespace test_mirror std::size_t reflected_id::event = rtl::detail::TypeId::get(); std::size_t reflected_id::calender = rtl::detail::TypeId::get(); - std::size_t reflected_id::void_t = rtl::detail::TypeId::get(); + std::size_t reflected_id::int_t = rtl::detail::TypeId::get(); std::size_t reflected_id::char_t = rtl::detail::TypeId::get(); std::size_t reflected_id::std_string = rtl::detail::TypeId::get(); std::size_t reflected_id::std_string_view = rtl::detail::TypeId::get(); @@ -277,7 +277,7 @@ namespace test_mirror static std::unordered_map nameIdMap( { { "char", char_t }, - { "void", void_t }, + { "int", int_t }, { "string", std_string }, { "string_view", std_string_view }, diff --git a/RTLBenchmarkApp/src/BenchMark.cpp b/RTLBenchmarkApp/src/BenchMark.cpp index 6fc25be5..97afd4dd 100644 --- a/RTLBenchmarkApp/src/BenchMark.cpp +++ b/RTLBenchmarkApp/src/BenchMark.cpp @@ -1,158 +1,188 @@ -#include -#include -#include "BenchMark.h" -#include "RTLibInterface.h" +#include -#if defined(_MSC_VER) -# define NOINLINE __declspec(noinline) -#elif defined(__GNUC__) -# define NOINLINE __attribute__((noinline)) -#else -# define NOINLINE -#endif +#include +#include "BenchMark.h" namespace { - static std::optional g_msg; - - NOINLINE static void sendMessage(const char* pMsg) - { - g_msg = pMsg; - } - - NOINLINE static std::string getMessage(const char* pMsg) - { - g_msg = pMsg; - return std::string(pMsg); - } - - struct Node - { - NOINLINE void sendMessage(const char* pMsg) - { - g_msg = pMsg; - } - - NOINLINE std::string getMessage(const char* pMsg) - { - g_msg = pMsg; - return std::string(pMsg); - } - }; - - const rtl::CxxMirror& cxx_mirror() - { - static auto m = rtl::CxxMirror({ - - rtl::type().record("node").build(), - - rtl::type().function("sendMessage").build(sendMessage), - - rtl::type().member().method("sendMessage").build(&Node::sendMessage), + static const char* LONG_STR = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do" + "do aeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis" + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure" + "dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Except" + "eur ssint occaecat cupidatat nnon proident, sunt in culpa qui officia deserunt mollit anim id" + "Lorem ipsum dolor sit amet laboris nisi ut aliquip ex ea commodo"; +} - rtl::type().function("getMessage").build(getMessage), +// Pre-created string to isolate call overhead +static argStr_t g_longStr(LONG_STR); - rtl::type().member().method("getMessage").build(&Node::getMessage) - }); - return m; - } +namespace rtl_bench +{ + void BenchMark::directCall_noReturn(benchmark::State& state) + { + for (auto _ : state) + { + sendMessage(g_longStr); + benchmark::DoNotOptimize(g_msg); + } + } + + + void BenchMark::stdFunctionCall_noReturn(benchmark::State& state) + { + static std::function sendMsg = [](argStr_t& pMsg) { + sendMessage(pMsg); + }; + + for (auto _ : state) + { + sendMsg(g_longStr); + benchmark::DoNotOptimize(g_msg); + } + } + + + void BenchMark::stdFunctionMethodCall_noReturn(benchmark::State& state) + { + Node* node = new Node(); + static std::function sendMsg = [=](argStr_t& pMsg) { + node->sendMessage(pMsg); + }; + + for (auto _ : state) + { + sendMsg(g_longStr); + benchmark::DoNotOptimize(g_msg); + } + } + + + void BenchMark::directCall_withReturn(benchmark::State& state) + { + static auto _ = []() { + std::cout << "--------------------------------------------------" + "-----------------------------------------------" << std::endl; + return 0; + }(); + + for (auto _ : state) + { + benchmark::DoNotOptimize(getMessage(g_longStr)); + } + } + + + void BenchMark::stdFunctionCall_withReturn(benchmark::State& state) + { + static std::function getMsg = [](argStr_t& pMsg) { + auto msgStr = getMessage(pMsg); + volatile auto* p = &msgStr; + static_cast(p); + return msgStr; + }; + + for (auto _ : state) + { + benchmark::DoNotOptimize(getMsg(g_longStr)); + } + } + + + void BenchMark::stdFunctionMethodCall_withReturn(benchmark::State& state) + { + static Node* node = new Node(); + static std::function getMsg = [=](argStr_t& pMsg) { + auto msgStr = node->getMessage(pMsg); + volatile auto* p = &msgStr; + static_cast(p); + return msgStr; + }; + + for (auto _ : state) + { + benchmark::DoNotOptimize(getMsg(g_longStr)); + } + } } -namespace rtl_bench +namespace rtl_bench { - void BenchMark::directCall_noReturn(benchmark::State& state) - { - for (auto _ : state) - { - sendMessage("direct"); - benchmark::DoNotOptimize(g_msg); - } - } - - - void BenchMark::lambdaCall_noReturn(benchmark::State& state) - { - static std::function sendMsg = [](const char* pMsg) { - sendMessage(pMsg); - }; - - for (auto _ : state) - { - sendMsg("lambda"); - benchmark::DoNotOptimize(g_msg); - } - } - - - void BenchMark::reflectedCall_noReturn(benchmark::State& state) - { - static rtl::Function sendMsg = cxx_mirror().getFunction("sendMessage").value(); - static auto sendMsgCall = sendMsg.bind(); - for (auto _ : state) - { - benchmark::DoNotOptimize(sendMsgCall.call("reflected")); - } - } - - - void BenchMark::reflectedMethodCall_noReturn(benchmark::State& state) - { - static rtl::Record rNode = cxx_mirror().getRecord("node").value(); - static rtl::Method sendMsg = rNode.getMethod("sendMessage").value(); - static rtl::RObject robj = rNode.create().rObject; - - for (auto _ : state) - { - benchmark::DoNotOptimize(sendMsg.bind(robj).call("reflected")); - } - } - - - void BenchMark::directCall_withReturn(benchmark::State& state) - { - for (auto _ : state) - { - benchmark::DoNotOptimize(getMessage("direct")); - } - } - - - void BenchMark::lambdaCall_withReturn(benchmark::State& state) - { - static std::function getMsg = [](const char* pMsg) { - return getMessage(pMsg); - }; - - for (auto _ : state) - { - benchmark::DoNotOptimize(getMsg("lambda")); - } - } - - - void BenchMark::reflectedCall_withReturn(benchmark::State& state) - { - static rtl::Function getMsg = cxx_mirror().getFunction("getMessage").value(); - for (auto _ : state) - { - benchmark::DoNotOptimize(getMsg.bind().call("reflected")); - } - } - - - void BenchMark::reflectedMethodCall_withReturn(benchmark::State& state) - { - static rtl::Record rNode = cxx_mirror().getRecord("node").value(); - static rtl::Method getMsg = rNode.getMethod("getMessage").value(); - static rtl::RObject robj = rNode.create().rObject; - - for (auto _ : state) - { - benchmark::DoNotOptimize(getMsg.bind(robj).call("reflected")); - } - } + void BenchMark::reflectedCall_noReturn(benchmark::State& state) + { + static rtl::Function sendMsg = cxx_mirror().getFunction("sendMessage").value(); + static auto _ = []() { + auto err = sendMsg.bind().call(g_longStr).err; + if (err != rtl::error::None) { + std::cout << "[rtl:0] err: "<< rtl::to_string(err)<<"\n"; + } + return 0; + }(); + + for (auto _ : state) + { + benchmark::DoNotOptimize(sendMsg.bind().call(g_longStr)); + } + } + + + void BenchMark::reflectedMethodCall_noReturn(benchmark::State& state) + { + static rtl::Record rNode = cxx_mirror().getRecord("Node").value(); + static rtl::Method sendMsg = rNode.getMethod("sendMessage").value(); + static rtl::RObject robj = rNode.create().rObject; + static auto _ = []() { + auto err = sendMsg.bind(robj).call(g_longStr).err; + if (err != rtl::error::None) { + std::cout << "[rtl:1] err: " << rtl::to_string(err) << "\n"; + } + return 0; + }(); + + for (auto _ : state) + { + benchmark::DoNotOptimize(sendMsg.bind(robj).call(g_longStr)); + } + } + + + void BenchMark::reflectedCall_withReturn(benchmark::State& state) + { + static rtl::Function getMsg = cxx_mirror().getFunction("getMessage").value(); + static auto _ = []() { + auto err = getMsg.bind().call(g_longStr).err; + if (err != rtl::error::None) { + std::cout << "[rtl:2] err: " << rtl::to_string(err) << "\n"; + } + return 0; + }(); + + for (auto _ : state) + { + benchmark::DoNotOptimize(getMsg.bind().call(g_longStr)); + } + } + + + void BenchMark::reflectedMethodCall_withReturn(benchmark::State& state) + { + static rtl::Record rNode = cxx_mirror().getRecord("Node").value(); + static rtl::Method getMsg = rNode.getMethod("getMessage").value(); + static rtl::RObject robj = rNode.create().rObject; + static auto _ = []() { + auto err = getMsg.bind(robj).call(g_longStr).err; + if (err != rtl::error::None) { + std::cout << "[rtl:3] err: " << rtl::to_string(err) << "\n"; + } + return 0; + }(); + + for (auto _ : state) + { + benchmark::DoNotOptimize(getMsg.bind(robj).call(g_longStr)); + } + } } \ No newline at end of file diff --git a/RTLBenchmarkApp/src/BenchMark.h b/RTLBenchmarkApp/src/BenchMark.h index c744ba0d..31b14437 100644 --- a/RTLBenchmarkApp/src/BenchMark.h +++ b/RTLBenchmarkApp/src/BenchMark.h @@ -2,21 +2,83 @@ #include +#include "RTLibInterface.h" + +#if defined(_MSC_VER) +# define NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +# define NOINLINE __attribute__((noinline)) +#else +# define NOINLINE +#endif + +using argStr_t = std::string_view; +using retStr_t = std::string_view; + +#define WORK_LOAD(S) (std::string(S) + std::string(S) + std::string(S) + std::string(S)) + namespace rtl_bench { + static std::optional g_msg; + + NOINLINE static void sendMessage(argStr_t pMsg) { + g_msg = WORK_LOAD(pMsg); + } + + NOINLINE static retStr_t getMessage(argStr_t pMsg) { + g_msg = WORK_LOAD(pMsg); + return retStr_t(g_msg->c_str()); + } + + struct Node + { + NOINLINE void sendMessage(argStr_t pMsg) { + g_msg = WORK_LOAD(pMsg); + } + + NOINLINE retStr_t getMessage(argStr_t pMsg) + { + g_msg = WORK_LOAD(pMsg); + return retStr_t(g_msg->c_str()); + } + }; + + + static const rtl::CxxMirror& cxx_mirror() + { + static auto m = rtl::CxxMirror({ + + rtl::type().record("Node").build(), + + rtl::type().function("sendMessage").build(sendMessage), + + rtl::type().member().method("sendMessage").build(&Node::sendMessage), + + rtl::type().function("getMessage").build(getMessage), + + rtl::type().member().method("getMessage").build(&Node::getMessage) + }); + return m; + } + + struct BenchMark { static void directCall_noReturn(benchmark::State& state); - static void lambdaCall_noReturn(benchmark::State& state); + static void stdFunctionCall_noReturn(benchmark::State& state); static void reflectedCall_noReturn(benchmark::State& state); + static void stdFunctionMethodCall_noReturn(benchmark::State& state); + static void reflectedMethodCall_noReturn(benchmark::State& state); static void directCall_withReturn(benchmark::State& state); - static void lambdaCall_withReturn(benchmark::State& state); + static void stdFunctionCall_withReturn(benchmark::State& state); + + static void stdFunctionMethodCall_withReturn(benchmark::State& state); static void reflectedCall_withReturn(benchmark::State& state); diff --git a/RTLBenchmarkApp/src/main.cpp b/RTLBenchmarkApp/src/main.cpp index f0a27601..dc55ee28 100644 --- a/RTLBenchmarkApp/src/main.cpp +++ b/RTLBenchmarkApp/src/main.cpp @@ -4,16 +4,17 @@ #include "BenchMark.h" -// ------------------------------------------------------------ -// Register benchmarks -// ------------------------------------------------------------ BENCHMARK(rtl_bench::BenchMark::directCall_noReturn); -BENCHMARK(rtl_bench::BenchMark::lambdaCall_noReturn); +BENCHMARK(rtl_bench::BenchMark::stdFunctionCall_noReturn); +BENCHMARK(rtl_bench::BenchMark::stdFunctionMethodCall_noReturn); BENCHMARK(rtl_bench::BenchMark::reflectedCall_noReturn); BENCHMARK(rtl_bench::BenchMark::reflectedMethodCall_noReturn); + BENCHMARK(rtl_bench::BenchMark::directCall_withReturn); -BENCHMARK(rtl_bench::BenchMark::lambdaCall_withReturn); +BENCHMARK(rtl_bench::BenchMark::stdFunctionCall_withReturn); +BENCHMARK(rtl_bench::BenchMark::stdFunctionMethodCall_withReturn); BENCHMARK(rtl_bench::BenchMark::reflectedCall_withReturn); BENCHMARK(rtl_bench::BenchMark::reflectedMethodCall_withReturn); + BENCHMARK_MAIN(); diff --git a/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorObjectTest.cpp b/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorObjectTest.cpp index 570a8d33..d98723d4 100644 --- a/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorObjectTest.cpp +++ b/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorObjectTest.cpp @@ -11,13 +11,6 @@ namespace const rtl::CxxMirror cxx_mirror() { return rtl::CxxMirror({ - - // Registering void as a record type (valid type but has no members/constructors). - // Demonstrates that RTL can explicitly represent even fundamental non-instantiable types. - rtl::type().record("void").build(), - - // Example of compile-time safety: constructors for void are invalid, RTL enforces this. - // rtl::type().member().constructor().build(), // <- will not compile // Register char as a record type (fundamental but instantiable). rtl::type().record("char").build(), diff --git a/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorThreadingTest.cpp b/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorThreadingTest.cpp index 99460e54..b340a9c0 100644 --- a/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorThreadingTest.cpp +++ b/RTLTestRunApp/src/CxxMirrorTests/CxxMirrorThreadingTest.cpp @@ -99,8 +99,6 @@ namespace rtl_tests rtl::type().function("strlen").build(std::strlen), - rtl::type().record("void").build(), - rtl::type().record("char").build(), rtl::type().record>("vector_int").build(), diff --git a/RTLTestRunApp/src/FunctionalityTests/MoveConstructorTests.cpp b/RTLTestRunApp/src/FunctionalityTests/MoveConstructorTests.cpp index 245dee72..d4c07bc0 100644 --- a/RTLTestRunApp/src/FunctionalityTests/MoveConstructorTests.cpp +++ b/RTLTestRunApp/src/FunctionalityTests/MoveConstructorTests.cpp @@ -7,7 +7,7 @@ using namespace std; using namespace rtl; -using namespace test_utils; +using namespace test_utils; using namespace test_mirror; namespace rtl_tests @@ -38,7 +38,7 @@ namespace rtl_tests // Moving a RObject created via alloc::Stack, invokes Calender's move constructor. RObject calender1 = std::move(calender0); - + //TODO: Fails on linux, differently optimized away from windows? // Calender's move-constructor called once. // EXPECT_TRUE(calender::get_move_ops_count() == 1); @@ -247,7 +247,7 @@ namespace rtl_tests // Moving a RObject created via alloc::Stack, invokes Calender's move constructor. RObject calender1 = std::move(calender0); - //TODO: Fails on linux, differently optimized away from windows? + //TODO: Works on windows, fails on linux, differently optimized away for windows? // Calender's move-constructor called once. // EXPECT_TRUE(calender::get_move_ops_count() == 1); diff --git a/ReflectionTemplateLib/access/inc/Function.h b/ReflectionTemplateLib/access/inc/Function.h index f8ffdec2..94675923 100644 --- a/ReflectionTemplateLib/access/inc/Function.h +++ b/ReflectionTemplateLib/access/inc/Function.h @@ -67,7 +67,7 @@ namespace rtl { Function(const Function& pOther, const detail::FunctorId& pFunctorId, const std::string_view pFunctorName); - std::size_t hasSignatureId(const std::size_t pSignatureId) const; + const std::size_t hasSignatureId(const std::size_t pSignatureId) const; GETTER(detail::methodQ, Qualifier, m_qualifier); diff --git a/ReflectionTemplateLib/access/inc/Function.hpp b/ReflectionTemplateLib/access/inc/Function.hpp index 033db291..02aadb33 100644 --- a/ReflectionTemplateLib/access/inc/Function.hpp +++ b/ReflectionTemplateLib/access/inc/Function.hpp @@ -22,12 +22,12 @@ namespace rtl return detail::FunctionCaller<_signature...>(*this); } - /* @method: hasSignature<...>() - @param: set of arguments, explicitly specified as template parameter. - @return: bool, if the functor associated with this object is of certain signature or not. - * a single 'Function' object can be associated with multiple overloads of same function. - * the set of arguments passed is checked agains all registered overloads, returns true if matched with any one. - */ template +/* @method: hasSignature<...>() + @param: set of arguments, explicitly specified as template parameter. + @return: bool, if the functor associated with this object is of certain signature or not. + * a single 'Function' object can be associated with multiple overloads of same function. + * the set of arguments passed is checked agains all registered overloads, returns true if matched with any one. +*/ template inline bool Function::hasSignature() const { //hasSignatureId() returns the index of the 'lambda' in functor-container, which cannot be '-1'. @@ -35,25 +35,25 @@ namespace rtl } - /* @method: operator()() - @param: variadic arguments. - @return: Return, possible error & return value of from the reflected call. - * if the arguments did not match with any overload, returns RObject with error::SignatureMismatch - * providing optional syntax, Function::call() does the exact same thing. - */ template +/* @method: operator()() + @param: variadic arguments. + @return: Return, possible error & return value of from the reflected call. + * if the arguments did not match with any overload, returns RObject with error::SignatureMismatch + * providing optional syntax, Function::call() does the exact same thing. +*/ template inline Return Function::operator()(_args&& ...params) const noexcept { return bind().call(std::forward<_args>(params)...); } - /* @method: hasSignatureId() - @param: const std::size_t& (signatureId to be found) - @return: the index of the functor in the functor-table. - * a 'Function' object may be associated with multiple functors in case of overloads. - * every overload will have unique 'FunctorId', contained by one 'Function' object. - * given signatureId is compared against the signatureId of all overloads registered. - */ inline std::size_t Function::hasSignatureId(const std::size_t pSignatureId) const +/* @method: hasSignatureId() + @param: const std::size_t& (signatureId to be found) + @return: the index of the functor in the functor-table. + * a 'Function' object may be associated with multiple functors in case of overloads. + * every overload will have unique 'FunctorId', contained by one 'Function' object. + * given signatureId is compared against the signatureId of all overloads registered. +*/ inline const std::size_t Function::hasSignatureId(const std::size_t pSignatureId) const { //simple linear-search, efficient for small set of elements. for (const auto& functorId : m_functorIds) { diff --git a/ReflectionTemplateLib/access/inc/RObject.h b/ReflectionTemplateLib/access/inc/RObject.h index 6a446112..208e2c59 100644 --- a/ReflectionTemplateLib/access/inc/RObject.h +++ b/ReflectionTemplateLib/access/inc/RObject.h @@ -28,6 +28,7 @@ namespace rtl::detail class RObjExtractor; + template struct RObjectBuilder; } @@ -42,17 +43,15 @@ namespace rtl { using Cloner = std::function< Return(const RObject&, rtl::alloc) >; - mutable detail::RObjectId m_objectId; + std::any m_object; + detail::RObjectId m_objectId; - mutable std::any m_object; - mutable const Cloner* m_getClone; - mutable const std::vector* m_converters; + const Cloner* m_getClone = nullptr; + const std::vector* m_converters = nullptr; RObject(const RObject&) = default; - RObject(const detail::RObjectId& pRObjId, std::any&& pObject, const Cloner& pCloner, - const std::vector& pConverters); - - static std::atomic& getInstanceCounter(); + RObject(std::any&& pObject, const detail::RObjectId pRObjId, const Cloner* pCloner, + const std::vector* pConverters) noexcept; std::size_t getConverterIndex(const std::size_t pToTypeId) const; @@ -70,10 +69,10 @@ namespace rtl RObject& operator=(RObject&&) = delete; RObject& operator=(const RObject&) = delete; - GETTER(std::size_t, TypeId, m_objectId.m_typeId) GETTER_BOOL(Empty, (m_object.has_value() == false)) GETTER_BOOL(OnHeap, (m_objectId.m_allocatedOn == alloc::Heap)) GETTER_BOOL(AllocatedByRtl, (m_objectId.m_allocatedOn == alloc::Heap)) + GETTER(std::size_t, TypeId, m_objectId.m_typeId) /* Reflection Const Semantics: * - All reflected objects default to mutable internally; API enforces logical constness. @@ -96,11 +95,15 @@ namespace rtl template, int> = 0> std::optional> view() const; + static std::atomic& getInstanceCounter(); + //friends :) template friend struct detail::RObjectUPtr; friend detail::RObjExtractor; - friend detail::RObjectBuilder; + + template + friend struct detail::RObjectBuilder; }; struct [[nodiscard]] Return { diff --git a/ReflectionTemplateLib/access/inc/RObject.hpp b/ReflectionTemplateLib/access/inc/RObject.hpp index c478a320..f55d6dda 100644 --- a/ReflectionTemplateLib/access/inc/RObject.hpp +++ b/ReflectionTemplateLib/access/inc/RObject.hpp @@ -24,18 +24,18 @@ namespace rtl { - inline RObject::RObject(const detail::RObjectId& pRObjId, std::any&& pObject, const Cloner& pCloner, - const std::vector& pConverters) - : m_objectId(pRObjId) - , m_object(std::forward(pObject)) - , m_getClone(&pCloner) - , m_converters(&pConverters) + FORCE_INLINE RObject::RObject(std::any&& pObject, const detail::RObjectId pRObjId, const Cloner* pCloner, + const std::vector* pConverters) noexcept + : m_object(std::move(pObject)) + , m_objectId(pRObjId) + , m_getClone(pCloner) + , m_converters(pConverters) { } inline RObject::RObject(RObject&& pOther) noexcept : m_object(std::move(pOther.m_object)) - , m_getClone(pOther.m_getClone) , m_objectId(pOther.m_objectId) + , m_getClone(pOther.m_getClone) , m_converters(pOther.m_converters) { // Explicitly clear moved-from source @@ -101,7 +101,7 @@ namespace rtl template , int>> - std::optional> RObject::view() const + FORCE_INLINE std::optional> RObject::view() const { if constexpr (traits::is_bare_type()) { @@ -117,14 +117,16 @@ namespace rtl template , int>> - std::optional> RObject::view() const + FORCE_INLINE std::optional> RObject::view() const { if constexpr (traits::is_bare_type()) { if (detail::TypeId::get() == m_objectId.m_wrapperTypeId) { - const T& sptrRef = *(detail::RObjExtractor(this).getWrapper()); - return std::optional>(std::in_place, const_cast(sptrRef)); + const T* sptrRef = detail::RObjExtractor(this).getWrapper(); + if (sptrRef != nullptr) { + return std::optional>(std::in_place, const_cast(*sptrRef)); + } } } return std::nullopt; @@ -132,7 +134,7 @@ namespace rtl template , int>> - inline std::optional> RObject::view() const + FORCE_INLINE std::optional> RObject::view() const { if constexpr (traits::is_bare_type()) { diff --git a/ReflectionTemplateLib/common/Constants.h b/ReflectionTemplateLib/common/Constants.h index 24d47ddd..145cd01c 100644 --- a/ReflectionTemplateLib/common/Constants.h +++ b/ReflectionTemplateLib/common/Constants.h @@ -131,6 +131,7 @@ namespace rtl::detail NonConst // Non-const instance method }; + constexpr const std::string_view NAMESPACE_GLOBAL = "global"; inline static const std::string ctor_name(const std::string_view pRecordName = "") { // [critical] Must not change. Constructors are identified using this format. @@ -157,5 +158,11 @@ namespace rtl::detail return _var; \ } - constexpr const std::string_view NAMESPACE_GLOBAL = "global"; +#if defined(_MSC_VER) +#define FORCE_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +#define FORCE_INLINE inline __attribute__((always_inline)) +#else +#define FORCE_INLINE inline +#endif } \ No newline at end of file diff --git a/ReflectionTemplateLib/common/rtl_traits.h b/ReflectionTemplateLib/common/rtl_traits.h index 25a42d2b..d5bc1f89 100644 --- a/ReflectionTemplateLib/common/rtl_traits.h +++ b/ReflectionTemplateLib/common/rtl_traits.h @@ -70,7 +70,7 @@ namespace rtl { using value_type = std::nullptr_t; static constexpr const auto type = detail::Wrapper::None; - static auto id() { return detail::TypeId<>::None; } + static constexpr std::size_t id() { return detail::TypeId<>::None; } }; @@ -79,7 +79,7 @@ namespace rtl { using value_type = T; static constexpr const auto type = detail::Wrapper::Shared; - static auto id() { return detail::TypeId>::get(); } + static constexpr std::size_t id() { return detail::TypeId>::get(); } }; @@ -88,7 +88,7 @@ namespace rtl { using value_type = T; static constexpr const auto type = detail::Wrapper::Unique; - static auto id() { return detail::TypeId>::get(); } + static constexpr std::size_t id() { return detail::TypeId>::get(); } }; @@ -97,7 +97,7 @@ namespace rtl { using value_type = T; static constexpr const auto type = detail::Wrapper::Weak; - static auto id() { return detail::TypeId>::get(); } + static constexpr std::size_t id() { return detail::TypeId>::get(); } }; template diff --git a/ReflectionTemplateLib/detail/inc/FunctionCaller.hpp b/ReflectionTemplateLib/detail/inc/FunctionCaller.hpp index 4fdfbde7..3abe2eab 100644 --- a/ReflectionTemplateLib/detail/inc/FunctionCaller.hpp +++ b/ReflectionTemplateLib/detail/inc/FunctionCaller.hpp @@ -36,6 +36,6 @@ namespace rtl::detail if (index != rtl::index_none) { return Container::template forwardCall<_args...>(index, std::forward<_args>(params)...); } - return { error::SignatureMismatch, RObject{ } }; + return { error::SignatureMismatch, RObject{} }; } } \ No newline at end of file diff --git a/ReflectionTemplateLib/detail/inc/RObjExtracter.h b/ReflectionTemplateLib/detail/inc/RObjExtracter.h index c805f9d6..723a4075 100644 --- a/ReflectionTemplateLib/detail/inc/RObjExtracter.h +++ b/ReflectionTemplateLib/detail/inc/RObjExtracter.h @@ -24,11 +24,10 @@ namespace rtl::detail RObjExtractor(const RObject* pRObj) : m_rObj(*pRObj) { } template - static const T* getPointer(const std::any& pObject, const EntityKind pEntityKind) + FORCE_INLINE static const T* getPointer(const std::any& pObject, const EntityKind pEntityKind) { - try { - switch (pEntityKind) - { + switch (pEntityKind) + { case EntityKind::Ref: { return std::any_cast(pObject); } @@ -37,129 +36,115 @@ namespace rtl::detail return static_cast(&valueRef); } default: return nullptr; - } } - catch (const std::bad_any_cast&) { /*TODO: log the failure. */ } return nullptr; } template - const T* getPointer() const + FORCE_INLINE const T* getPointer() const { - try { - switch (m_rObj.m_objectId.m_containsAs) - { - case EntityKind::Ref: { - return std::any_cast(m_rObj.m_object); - } - case EntityKind::Wrapper: { - return getFromWrapper(); - } - case EntityKind::Value: { - const T& valueRef = std::any_cast(m_rObj.m_object); - return static_cast(&valueRef); - } - default: return nullptr; + switch (m_rObj.m_objectId.m_containsAs) + { + case EntityKind::Ref: { + return std::any_cast(m_rObj.m_object); } + case EntityKind::Wrapper: { + return getFromWrapper(); + } + case EntityKind::Value: { + const T& valueRef = std::any_cast(m_rObj.m_object); + return static_cast(&valueRef); + } + default: return nullptr; } - catch (const std::bad_any_cast&) { /*TODO: log the failure. */ } return nullptr; } template = 0> - auto getWrapper() const -> const RObjectUPtr::value_type>* + FORCE_INLINE auto getWrapper() const -> const RObjectUPtr::value_type>* { - try { - if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Unique) + if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Unique) + { + using _T = traits::std_wrapper::value_type; + if constexpr (traits::is_const_v<_T>) { - using _T = traits::std_wrapper::value_type; - if constexpr (traits::is_const_v<_T>) + if (m_rObj.m_objectId.m_isWrappingConst) { - if (m_rObj.m_objectId.m_isWrappingConst) - { - using U = detail::RObjectUPtr; - const U& uptrRef = std::any_cast(m_rObj.m_object); - return static_cast(&uptrRef); - } - } - else - { - using U = detail::RObjectUPtr<_T>; + using U = detail::RObjectUPtr; const U& uptrRef = std::any_cast(m_rObj.m_object); return static_cast(&uptrRef); } } + else + { + using U = detail::RObjectUPtr<_T>; + const U& uptrRef = std::any_cast(m_rObj.m_object); + return static_cast(&uptrRef); + } } - catch (const std::bad_any_cast&) { /*TODO: log the failure. */ } return nullptr; } template = 0> - const T* getWrapper() const + FORCE_INLINE const T* getWrapper() const { - try { - if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Shared) + if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Shared) + { + using _T = traits::std_wrapper::value_type; + if constexpr (traits::is_const_v<_T>) { - using _T = traits::std_wrapper::value_type; - if constexpr (traits::is_const_v<_T>) - { - if (m_rObj.m_objectId.m_isWrappingConst) { - using U = std::shared_ptr; - const U& sptrRef = std::any_cast(m_rObj.m_object); - return static_cast(&sptrRef); - } - } - else - { - using U = std::shared_ptr<_T>; + if (m_rObj.m_objectId.m_isWrappingConst) { + using U = std::shared_ptr; const U& sptrRef = std::any_cast(m_rObj.m_object); return static_cast(&sptrRef); } } + else + { + using U = std::shared_ptr<_T>; + const U& sptrRef = std::any_cast(m_rObj.m_object); + return static_cast(&sptrRef); + } } - catch (const std::bad_any_cast&) { /*TODO: log the failure. */ } return nullptr; } template - const T* getFromWrapper() const + FORCE_INLINE const T* getFromWrapper() const { - try { - if constexpr (std::is_destructible_v) + if constexpr (std::is_destructible_v) + { + if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Unique) { - if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Unique) - { - if (m_rObj.m_objectId.m_isWrappingConst) { - using U = detail::RObjectUPtr; - const U& uptrRef = std::any_cast(m_rObj.m_object); - return static_cast(uptrRef.get()); - } - else { - using U = detail::RObjectUPtr; - const U& uptrRef = std::any_cast(m_rObj.m_object); - return static_cast(uptrRef.get()); - } + if (m_rObj.m_objectId.m_isWrappingConst) { + using U = detail::RObjectUPtr; + const U& uptrRef = std::any_cast(m_rObj.m_object); + return static_cast(uptrRef.get()); } - if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Shared) - { - if (m_rObj.m_objectId.m_isWrappingConst) { - using U = std::shared_ptr; - const auto& sptrRef = std::any_cast(m_rObj.m_object); - return static_cast(sptrRef.get()); - } - else { - using U = std::shared_ptr; - const auto& sptrRef = std::any_cast(m_rObj.m_object); - return static_cast(sptrRef.get()); - } + else { + using U = detail::RObjectUPtr; + const U& uptrRef = std::any_cast(m_rObj.m_object); + return static_cast(uptrRef.get()); + } + } + if (m_rObj.m_objectId.m_wrapperType == detail::Wrapper::Shared) + { + if (m_rObj.m_objectId.m_isWrappingConst) { + using U = std::shared_ptr; + const auto& sptrRef = std::any_cast(m_rObj.m_object); + return static_cast(sptrRef.get()); + } + else { + using U = std::shared_ptr; + const auto& sptrRef = std::any_cast(m_rObj.m_object); + return static_cast(sptrRef.get()); } } } - catch (const std::bad_any_cast&) { /*TODO: log the failure. */ } return nullptr; } }; diff --git a/ReflectionTemplateLib/detail/inc/RObjectBuilder.h b/ReflectionTemplateLib/detail/inc/RObjectBuilder.h index 17e0f08c..5ea9619e 100644 --- a/ReflectionTemplateLib/detail/inc/RObjectBuilder.h +++ b/ReflectionTemplateLib/detail/inc/RObjectBuilder.h @@ -20,25 +20,17 @@ namespace rtl { namespace rtl::detail { - class RObjectBuilder + template + struct RObjectBuilder { - using Cloner = std::function< Return(const RObject&, rtl::alloc) >; - - template - static const Cloner& buildCloner(); - - template - static const std::vector& getConverters(); - - public: - RObjectBuilder() = delete; RObjectBuilder(const RObjectBuilder&) = delete; - static const std::size_t rtlManagedInstanceCount(); + template requires (_allocOn == alloc::Heap) + static RObject build(T&& pVal, bool pIsConstCastSafe) noexcept; - template - static RObject build(T&& pVal, const bool pIsConstCastSafe); + template requires (_allocOn == alloc::Stack) + static RObject build(T&& pVal, bool pIsConstCastSafe) noexcept; }; } @@ -47,34 +39,38 @@ namespace rtl { inline const std::size_t getRtlManagedHeapInstanceCount() { - return detail::RObjectBuilder::rtlManagedInstanceCount(); + return RObject::getInstanceCounter(); } template - inline RObject reflect(T(&pArr)[N]) + inline RObject reflect(T(&pArr)[N]) noexcept { if constexpr (std::is_same_v, char>) { - return detail::RObjectBuilder::build(std::string_view(pArr, N - 1), !traits::is_const_v); + return detail::RObjectBuilder::template + build(std::string_view(pArr, N - 1), !traits::is_const_v); } else { - return detail::RObjectBuilder::build, alloc::Stack>(std::vector(pArr, pArr + N), !traits::is_const_v); + return detail::RObjectBuilder>::template + build(std::vector(pArr, pArr + N), !traits::is_const_v); } } template - inline RObject reflect(T&& pVal) + inline RObject reflect(T&& pVal) noexcept { using _T = traits::raw_t; if constexpr (traits::std_wrapper<_T>::type == detail::Wrapper::None) { - return detail::RObjectBuilder::build(std::forward(pVal), !traits::is_const_v); + return detail::RObjectBuilder::template + build(std::forward(pVal), !traits::is_const_v); } else { constexpr bool isConstCastSafe = !traits::is_const_v::value_type>; - return detail::RObjectBuilder::build(std::forward(pVal), isConstCastSafe); + return detail::RObjectBuilder::template + build(std::forward(pVal), isConstCastSafe); } } } \ No newline at end of file diff --git a/ReflectionTemplateLib/detail/inc/RObjectBuilder.hpp b/ReflectionTemplateLib/detail/inc/RObjectBuilder.hpp index 68fa56e0..6c0f3e48 100644 --- a/ReflectionTemplateLib/detail/inc/RObjectBuilder.hpp +++ b/ReflectionTemplateLib/detail/inc/RObjectBuilder.hpp @@ -18,14 +18,11 @@ #include "RObjectBuilder.h" namespace rtl::detail { - - inline const std::size_t RObjectBuilder::rtlManagedInstanceCount() - { - return RObject::getInstanceCounter(); - } + using Cloner = std::function< Return(const RObject&, rtl::alloc) >; + template - inline const std::vector& RObjectBuilder::getConverters() + FORCE_INLINE const std::vector& getConverters() noexcept { // extract wrapper info. using _W = traits::std_wrapper>; @@ -35,7 +32,7 @@ namespace rtl::detail { } template - inline const RObjectBuilder::Cloner& RObjectBuilder::buildCloner() + FORCE_INLINE const Cloner& buildCloner() noexcept { using W = traits::std_wrapper; using _T = std::conditional_t; @@ -48,15 +45,20 @@ namespace rtl::detail { switch (pAllocOn) { case alloc::Stack: - return { error::None, - RObjectBuilder::template build<_T, alloc::Stack>(_T(srcObj), true) }; - + return { + error::None, + RObjectBuilder<_T>::template build(_T(srcObj), true) + }; case alloc::Heap: - return { error::None, - RObjectBuilder::template build<_T*, alloc::Heap>(new _T(srcObj), true) }; - + return { + error::None, + RObjectBuilder<_T*>::template build(new _T(srcObj), true) + }; default: - return { error::EmptyRObject, RObject{} }; + return { + error::EmptyRObject, + RObject{} + }; } }; return cloner; @@ -64,55 +66,68 @@ namespace rtl::detail { else { static const Cloner cloner = [](const RObject&, alloc) -> Return { - return { error::TypeNotCopyConstructible, RObject{} }; + return { + error::TypeNotCopyConstructible, + RObject{} + }; }; return cloner; } } + template + template requires (_allocOn == alloc::Heap) + FORCE_INLINE RObject RObjectBuilder::build(T&& pVal, bool pIsConstCastSafe) noexcept + { + using _T = traits::raw_t; + return RObject( std::any{ + std::in_place_type>, + RObjectUPtr<_T>(std::unique_ptr<_T>(static_cast<_T*>(pVal))) + }, + RObjectId::create, alloc::Heap>(pIsConstCastSafe), + &buildCloner<_T>(), + &getConverters>()); + } - template - inline RObject RObjectBuilder::build(T&& pVal, const bool pIsConstCastSafe) + + template + template requires (_allocOn == alloc::Stack) + FORCE_INLINE RObject RObjectBuilder::build(T&& pVal, bool pIsConstCastSafe) noexcept { using _T = traits::raw_t; constexpr bool isRawPointer = std::is_pointer_v>; - if constexpr (_allocOn == alloc::Heap) + if constexpr (isRawPointer) { - static_assert(isRawPointer, "Invalid 'alloc' specified for non-pointer-type 'T'"); - return RObject(RObjectId::create, _allocOn>(pIsConstCastSafe), - std::any(RObjectUPtr<_T>(std::unique_ptr<_T>(static_cast<_T*>(pVal)))), - buildCloner<_T>(), - getConverters>()); + return RObject( std::any { static_cast(pVal) }, + RObjectId::create(pIsConstCastSafe), + &buildCloner<_T>(), + &getConverters() ); } - else if constexpr (_allocOn == alloc::Stack) + else { - if constexpr (isRawPointer) + if constexpr (traits::std_wrapper<_T>::type == Wrapper::Unique) { - return RObject(RObjectId::create(pIsConstCastSafe), - std::any(static_cast(pVal)), - buildCloner<_T>(), - getConverters()); + using U = traits::std_wrapper<_T>::value_type; + return RObject( std::any { + std::in_place_type>, + RObjectUPtr(std::move(pVal)) + }, + RObjectId::create(pIsConstCastSafe), + &buildCloner<_T>(), + &getConverters() ); } else { - if constexpr (traits::std_wrapper<_T>::type == Wrapper::Unique) - { - using U = traits::std_wrapper<_T>::value_type; - return RObject(RObjectId::create(pIsConstCastSafe), - std::any(RObjectUPtr(std::move(pVal))), - buildCloner<_T>(), - getConverters()); - } - else - { - static_assert(std::is_copy_constructible_v<_T>, "T must be copy-constructible (std::any requires this)."); - return RObject(RObjectId::create(pIsConstCastSafe), - std::any(std::forward(pVal)), - buildCloner<_T>(), - getConverters()); - } + static_assert(std::is_copy_constructible_v<_T>, "T must be copy-constructible (std::any requires this)."); + return RObject( std::any { + std::in_place_type, + std::forward(pVal) + }, + RObjectId::create(pIsConstCastSafe), + &buildCloner<_T>(), + &getConverters() ); } } } diff --git a/ReflectionTemplateLib/detail/inc/RObjectId.h b/ReflectionTemplateLib/detail/inc/RObjectId.h index 4dd628c8..a5d5decc 100644 --- a/ReflectionTemplateLib/detail/inc/RObjectId.h +++ b/ReflectionTemplateLib/detail/inc/RObjectId.h @@ -37,7 +37,7 @@ namespace rtl::detail GETTER(EntityKind, ContainedAs, m_containsAs) template - static constexpr EntityKind getEntityKind() + FORCE_INLINE static constexpr EntityKind getEntityKind() noexcept { using W = traits::std_wrapper>; using _T = traits::raw_t>; @@ -57,7 +57,7 @@ namespace rtl::detail template - static RObjectId create(bool pIsConstCastSafe) + FORCE_INLINE static RObjectId create(bool pIsConstCastSafe) noexcept { // extract wrapper info. using _W = traits::std_wrapper>; @@ -67,7 +67,7 @@ namespace rtl::detail const std::size_t wrapperId = _W::id(); const std::size_t typeId = rtl::detail::TypeId<_T>::get(); - const bool isWrappingConst = (_W::type != Wrapper::None && traits::is_const_v); + constexpr bool isWrappingConst = (_W::type != Wrapper::None && traits::is_const_v); return RObjectId{ isWrappingConst, pIsConstCastSafe, typeId, wrapperId, _allocOn, _W::type, entityKind }; } }; diff --git a/ReflectionTemplateLib/detail/inc/SetupConstructor.hpp b/ReflectionTemplateLib/detail/inc/SetupConstructor.hpp index a5d78f4b..a84ff3ea 100644 --- a/ReflectionTemplateLib/detail/inc/SetupConstructor.hpp +++ b/ReflectionTemplateLib/detail/inc/SetupConstructor.hpp @@ -37,14 +37,16 @@ namespace rtl::detail } else { return { error::None, - RObjectBuilder::build<_recordType, alloc::Stack>(_recordType(std::forward<_signature>(params)...), - true) }; + RObjectBuilder<_recordType>::template + build(_recordType(std::forward<_signature>(params)...), true) + }; } } else if (pAllocType == alloc::Heap) { - return {error::None, - RObjectBuilder::build<_recordType*, alloc::Heap>(new _recordType(std::forward<_signature>(params)...), - true) }; + return { error::None, + RObjectBuilder<_recordType*>::template + build(new _recordType(std::forward<_signature>(params)...), true) + }; } } return { error::EmptyRObject, RObject{} }; //dead code. compiler warning omitted. diff --git a/ReflectionTemplateLib/detail/inc/SetupFunction.h b/ReflectionTemplateLib/detail/inc/SetupFunction.h index 131a1f62..493aa4a9 100644 --- a/ReflectionTemplateLib/detail/inc/SetupFunction.h +++ b/ReflectionTemplateLib/detail/inc/SetupFunction.h @@ -35,6 +35,9 @@ namespace rtl { template using FunctionLambda = std::function < Return(_signature...) >; + template + static FunctionLambda<_signature...> getCaller(void(*pFunctor)(_signature...)); + template static FunctionLambda<_signature...> getCaller(_returnType(*pFunctor)(_signature...)); diff --git a/ReflectionTemplateLib/detail/inc/SetupFunction.hpp b/ReflectionTemplateLib/detail/inc/SetupFunction.hpp index ade7baec..f76bbbf4 100644 --- a/ReflectionTemplateLib/detail/inc/SetupFunction.hpp +++ b/ReflectionTemplateLib/detail/inc/SetupFunction.hpp @@ -18,6 +18,19 @@ namespace rtl { namespace detail { + template + template + inline SetupFunction<_derivedType>::FunctionLambda<_signature...> + SetupFunction<_derivedType>::getCaller(void(*pFunctor)(_signature...)) + { + return [=](_signature&&... params) -> Return + { + pFunctor(std::forward<_signature>(params)...); + return { error::None, RObject{} }; + }; + } + + template template inline SetupFunction<_derivedType>::FunctionLambda<_signature...> @@ -29,25 +42,25 @@ namespace rtl { constexpr bool isConstCastSafe = (!traits::is_const_v<_returnType>); - if constexpr (std::is_same_v<_returnType, void>) { - //if the function do not returns anything, this block will be retained by compiler. - (*pFunctor)(std::forward<_signature>(params)...); - return { error::None, RObject{} }; - } - else if constexpr (std::is_reference_v<_returnType>) { + if constexpr (std::is_reference_v<_returnType>) { /* if the function returns reference, this block will be retained by compiler. Note: reference to temporary or dangling is not checked here. */ using _rawRetType = traits::raw_t<_returnType>; - const _rawRetType& retObj = (*pFunctor)(std::forward<_signature>(params)...); + const _rawRetType& retObj = pFunctor(std::forward<_signature>(params)...); return { error::None, - RObjectBuilder::build(&retObj, - isConstCastSafe) }; + RObjectBuilder::template + build(&retObj, isConstCastSafe) + }; } else { //if the function returns anything (not refrence), this block will be retained by compiler. - return { error::None, - RObjectBuilder::build<_returnType, rtl::alloc::Stack>((*pFunctor)(std::forward<_signature>(params)...), - isConstCastSafe) }; + auto&& retObj = pFunctor(std::forward<_signature>(params)...); + using T = std::remove_cvref_t; + + return { error::None, + RObjectBuilder::template + build(std::forward(retObj), isConstCastSafe) + }; } }; } diff --git a/ReflectionTemplateLib/detail/inc/SetupMethod.h b/ReflectionTemplateLib/detail/inc/SetupMethod.h index 8456aa6e..2aea8dfe 100644 --- a/ReflectionTemplateLib/detail/inc/SetupMethod.h +++ b/ReflectionTemplateLib/detail/inc/SetupMethod.h @@ -41,6 +41,12 @@ namespace rtl { template static MethodLambda<_signature...> getMethodCaller(_returnType(_recordType::* pFunctor)(_signature...) const); + + template + static MethodLambda<_signature...> getVoidMethodCaller(_returnType(_recordType::* pFunctor)(_signature...)); + + template + static MethodLambda<_signature...> getVoidMethodCaller(_returnType(_recordType::* pFunctor)(_signature...) const); protected: diff --git a/ReflectionTemplateLib/detail/inc/SetupMethod.hpp b/ReflectionTemplateLib/detail/inc/SetupMethod.hpp index b0ca559e..4f64ee7a 100644 --- a/ReflectionTemplateLib/detail/inc/SetupMethod.hpp +++ b/ReflectionTemplateLib/detail/inc/SetupMethod.hpp @@ -20,6 +20,27 @@ namespace rtl { namespace detail { + + template + template + inline SetupMethod<_derivedType>::MethodLambda<_signature...> + SetupMethod<_derivedType>::getVoidMethodCaller(_returnType(_recordType::* pFunctor)(_signature...)) + { + /* a variable arguments lambda, which finally calls the 'pFunctor' with 'params...'. + this is stored in _derivedType's (MethodContainer) vector holding lambda's. + */ return [=](const RObject& pTargetObj, _signature&&...params)-> Return + { + if (!pTargetObj.isConstCastSafe()) { + return { error::IllegalConstCast, RObject{} }; + } + + _recordType& target = const_cast<_recordType&>(pTargetObj.view<_recordType>()->get()); + (target.*pFunctor)(std::forward<_signature>(params)...); + return { error::None, RObject{} }; + }; + } + + template template inline SetupMethod<_derivedType>::MethodLambda<_signature...> @@ -29,37 +50,55 @@ namespace rtl this is stored in _derivedType's (MethodContainer) vector holding lambda's. */ return [=](const RObject& pTargetObj, _signature&&...params)-> Return { - if (!pTargetObj.isConstCastSafe()) - { + if (!pTargetObj.isConstCastSafe()) { return { error::IllegalConstCast, RObject{} }; } constexpr bool isConstCastSafe = (!traits::is_const_v<_returnType>); //'target' needs const_cast, since the functor is non-const-member-function. _recordType& target = const_cast<_recordType&>(pTargetObj.view<_recordType>()->get()); - if constexpr (std::is_same_v<_returnType, void>) { - //if the function do not returns anything, this block will be retained by compiler. - (target.*pFunctor)(std::forward<_signature>(params)...); - return { error::None, RObject{} }; - } - else if constexpr (std::is_reference_v<_returnType>) { + if constexpr (std::is_reference_v<_returnType>) + { /* if the function returns reference, this block will be retained by compiler. Note: reference to temporary or dangling is not checked here. */ using _rawRetType = traits::raw_t<_returnType>; const _rawRetType& retObj = (target.*pFunctor)(std::forward<_signature>(params)...); return { error::None, - RObjectBuilder::build(&retObj, - isConstCastSafe) }; + RObjectBuilder::template + build(&retObj, isConstCastSafe) + }; } else { + + auto&& retObj = (target.*pFunctor)(std::forward<_signature>(params)...); + using T = std::remove_cvref_t; + return { error::None, - RObjectBuilder::build<_returnType, alloc::Stack>((target.*pFunctor)(std::forward<_signature>(params)...), - isConstCastSafe) }; + RObjectBuilder::template + build(std::forward(retObj), isConstCastSafe) + }; } }; } + + template + template + inline SetupMethod<_derivedType>::MethodLambda<_signature...> + SetupMethod<_derivedType>::getVoidMethodCaller(_returnType(_recordType::* pFunctor)(_signature...) const) + { + /* a variable arguments lambda, which finally calls the 'pFunctor' with 'params...'. + this is stored in _derivedType's (MethodContainer) vector holding lambda's. + */ return [=](const RObject& pTargetObj, _signature&&...params)-> Return + { + const _recordType& target = pTargetObj.view<_recordType>()->get(); + (target.*pFunctor)(std::forward<_signature>(params)...); + return { error::None, RObject{} }; + }; + } + + template template inline SetupMethod<_derivedType>::MethodLambda<_signature...> @@ -72,25 +111,25 @@ namespace rtl constexpr bool isConstCastSafe = (!traits::is_const_v<_returnType>); //'target' is const and 'pFunctor' is const-member-function. const _recordType& target = pTargetObj.view<_recordType>()->get(); - - if constexpr (std::is_same_v<_returnType, void>) { - //if the function do not returns anything, this block will be retained by compiler. - (target.*pFunctor)(std::forward<_signature>(params)...); - return { error::None, RObject{} }; - } - else if constexpr (std::is_reference_v<_returnType>) { - /* if the function returns reference, this block will be retained by compiler. - Note: reference to temporary or dangling is not checked here. - */ using _rawRetType = traits::raw_t<_returnType>; + if constexpr (std::is_reference_v<_returnType>) { + /* if the function returns reference, this block will be retained by compiler. + Note: reference to temporary or dangling is not checked here. + */ using _rawRetType = traits::raw_t<_returnType>; const _rawRetType& retObj = (target.*pFunctor)(std::forward<_signature>(params)...); return { error::None, - RObjectBuilder::build(&retObj, - isConstCastSafe) }; + RObjectBuilder::template + build(&retObj, isConstCastSafe) + }; } else { + + auto&& retObj = (target.*pFunctor)(std::forward<_signature>(params)...); + using T = std::remove_cvref_t; + return { error::None, - RObjectBuilder::build<_returnType, alloc::Stack>((target.*pFunctor)(std::forward<_signature>(params)...), - isConstCastSafe) }; + RObjectBuilder::template + build(std::forward(retObj), isConstCastSafe) + }; } }; } @@ -137,10 +176,21 @@ namespace rtl //generate a type-id of '_returnType'. const std::size_t retTypeId = TypeId>::get(); //finally add the lambda 'functor' in 'MethodContainer' lambda vector and get the index. - const std::size_t index = _derivedType::pushBack(getMethodCaller(pFunctor), getIndex, updateIndex); - //construct the hash-key 'FunctorId' and return. - return detail::FunctorId(index, retTypeId, TypeId<_recordType>::get(), _derivedType::getContainerId(), - _derivedType::template getSignatureStr<_recordType, _returnType>()); + + if constexpr (std::is_same_v<_returnType, void>) + { + const std::size_t index = _derivedType::pushBack(getVoidMethodCaller(pFunctor), getIndex, updateIndex); + //construct the hash-key 'FunctorId' and return. + return detail::FunctorId(index, retTypeId, TypeId<_recordType>::get(), _derivedType::getContainerId(), + _derivedType::template getSignatureStr<_recordType, _returnType>()); + } + else + { + const std::size_t index = _derivedType::pushBack(getMethodCaller(pFunctor), getIndex, updateIndex); + //construct the hash-key 'FunctorId' and return. + return detail::FunctorId(index, retTypeId, TypeId<_recordType>::get(), _derivedType::getContainerId(), + _derivedType::template getSignatureStr<_recordType, _returnType>()); + } } @@ -182,10 +232,21 @@ namespace rtl //generate a type-id of '_returnType'. const std::size_t retTypeId = TypeId>::get(); //finally add the lambda 'functor' in 'MethodContainer' lambda vector and get the index. - const std::size_t index = _derivedType::pushBack(getMethodCaller(pFunctor), getIndex, updateIndex); - //construct the hash-key 'FunctorId' and return. - return detail::FunctorId(index, retTypeId, TypeId<_recordType>::get(), _derivedType::getContainerId(), - _derivedType::template getSignatureStr<_recordType, _returnType>()); + + if constexpr (std::is_same_v<_returnType, void>) + { + const std::size_t index = _derivedType::pushBack(getVoidMethodCaller(pFunctor), getIndex, updateIndex); + //construct the hash-key 'FunctorId' and return. + return detail::FunctorId(index, retTypeId, TypeId<_recordType>::get(), _derivedType::getContainerId(), + _derivedType::template getSignatureStr<_recordType, _returnType>()); + } + else + { + const std::size_t index = _derivedType::pushBack(getMethodCaller(pFunctor), getIndex, updateIndex); + //construct the hash-key 'FunctorId' and return. + return detail::FunctorId(index, retTypeId, TypeId<_recordType>::get(), _derivedType::getContainerId(), + _derivedType::template getSignatureStr<_recordType, _returnType>()); + } } } } \ No newline at end of file diff --git a/ReflectionTemplateLib/detail/inc/TypeId.h b/ReflectionTemplateLib/detail/inc/TypeId.h index 2cc571ca..09f4dce6 100644 --- a/ReflectionTemplateLib/detail/inc/TypeId.h +++ b/ReflectionTemplateLib/detail/inc/TypeId.h @@ -35,7 +35,7 @@ namespace rtl { //'0' represents no type. [Never change, critical.] static constexpr const std::size_t None = 0; - static std::size_t get() + static const std::size_t get() { //statically initialize a unique-id. static const std::size_t typeId = generate_unique_id();