Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ comment:
behavior: default

ignore:
- "demo/**"
- "CxxTestProps/**"
- "CxxTestUtils/**"
- "RTLBenchmarkApp/**"
- "ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp"
4 changes: 3 additions & 1 deletion CxxTestProps/inc/Animal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class Animal
{
std::string m_name;
mutable std::string m_name;
std::string m_familyName;

static std::string m_zooKeeper;
Expand Down Expand Up @@ -37,6 +37,8 @@ class Animal

void setAnimalName(const std::string& pName);

void setAnimalName(const std::string& pName) const;

static std::string updateZooKeeper(std::string& pZooKeeper);

static std::string updateZooKeeper(std::string&& pZooKeeper);
Expand Down
6 changes: 6 additions & 0 deletions CxxTestProps/src/Animal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ void Animal::setAnimalName(const std::string& pName)
}


void Animal::setAnimalName(const std::string& pName) const
{
m_name = pName + "__args_const_lvalue_ref_method_const...";
}


std::string Animal::updateZooKeeper(std::string& pZooKeeper)
{
m_zooKeeper = pZooKeeper + "__args_non_const_lvalue_ref...";
Expand Down
7 changes: 6 additions & 1 deletion CxxTestRegistration/src/AnimalRegistration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ namespace test_mirror
fns.push_back(rtl::type().member<Animal>()
.method<const std::string&>(animal::str_setAnimalName)
.build(&Animal::setAnimalName));


// Overloaded const-method, taking const-ref as argument.
fns.push_back(rtl::type().member<Animal>()
.methodConst<const std::string&>(animal::str_setAnimalName)
.build(&Animal::setAnimalName));

// Static method, taking const-ref as argument.
fns.push_back(rtl::type().member<Animal>()
.methodStatic<const std::string&>(animal::str_updateZooKeeper)
Expand Down
2 changes: 2 additions & 0 deletions CxxTestUtils/inc/TestUtilsAnimal.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ namespace test_utils

static const bool test_method_setAnimalName_const_lvalue_ref_args(const rtl::RObject& pInstance);

static const bool test_method_const_setAnimalName_const_lvalue_ref_args(const rtl::RObject& pInstance);

static const bool test_method_setAnimalName_non_const_lvalue_ref_args(const rtl::RObject& pInstance);

template<class ...signature_t>
Expand Down
15 changes: 15 additions & 0 deletions CxxTestUtils/src/TestUtilsAnimal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,18 @@ const bool test_utils::animal::test_method_setAnimalName_non_const_lvalue_ref_ar
}
return false;
}


const bool test_utils::animal::test_method_const_setAnimalName_const_lvalue_ref_args(const rtl::RObject& pInstance)
{
if (pInstance.canViewAs<Animal>())
{
const Animal animal;
auto nameStr = std::string(NAME);
animal.setAnimalName(nameStr);

const Animal& rAnimal = pInstance.view<Animal>()->get();
return (animal == rAnimal);
}
return false;
}
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ RTL’s reflective calls are comparable to `std::function` for fully type-erased

## A Quick Preview: Reflection That Looks and Feels Like C++

First, create an instance of `CxxMirror` –
First, create an instance of `rtl::CxxMirror` –
```c++
auto cxx_mirror = rtl::CxxMirror({ /* ...register all types here... */ });
```
The `cxx_mirror` object provides access to the runtime reflection system. It enables querying, introspection, and instantiation of registered types without requiring compile-time type knowledge at the call site.
It can reside in any translation unit. To make it globally accessible and ensure it is initialized only when needed, a singleton interface can be used –
It can reside in any translation unit. To make it globally accessible and ensure it is initialized only when needed, a singleton access interface can be used –
```c++
// MyReflection.h
namespace rtl { class CxxMirror; } // Forward declaration, no includes here!
Expand All @@ -82,9 +82,7 @@ rtl::CxxMirror& cxx::mirror() {
return cxx_mirror;
}
```
`cxx_mirror` is a immutable, stack-allocated, value-type object and is safe to copy.
However, when used as a singleton (as shown above), implicit copies (e.g., `auto mirror = cxx::mirror()`) can unintentionally violate the singleton semantics.
To prevent this, the `rtl::CxxMirror`'s copy constructor is restricted to avoid such unintended duplication.
`cxx_mirror` is an immutable, stack-allocated, value-type object and is safe to copy, but when used as a singleton (as shown above), implicit copies (e.g., `auto mirror = cxx::mirror();`) can unintentionally violate the singleton semantics, so `rtl::CxxMirror` restricts such copy construction.

### RTL in action:

Expand Down
6 changes: 2 additions & 4 deletions RTLBenchmarkApp/src/ReflectedCallUnknownReturn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,14 @@ namespace
return method;
}();

//TODO: make return-type 'void' work here, fix compile error.
// rtl::method<rtl::RObject, void(bm::argStr_t)>
static rtl::method<rtl::RObject, bm::retStr_t(bm::argStr_t)> ErasedTargetAwareReturn_SendMessage = []()
static rtl::method<rtl::RObject, void(bm::argStr_t)> ErasedTargetAwareReturn_SendMessage = []()
{
std::optional<rtl::Method> optMethod = class_Node.getMethod("sendMessage");
if (!optMethod) {
std::cerr << "[3] error: method 'Node::sendMessage' not found.\n";
std::abort();
}
auto method = optMethod->targetT<>().argsT<bm::argStr_t>().returnT<bm::retStr_t>();
auto method = optMethod->targetT<>().argsT<bm::argStr_t>().returnT<void>();
if (!method) {
std::cerr << "[3] error: invalid method caller.\n";
std::abort();
Expand Down
15 changes: 7 additions & 8 deletions RTLTestRunApp/src/CxxMirrorTests/CxxMirrorObjectTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,21 @@ namespace rtl_tests
// Lookup push_back method and call it multiple times with different values.
std::optional<rtl::Method> oPushBack = classVectorInt->getMethod("push_back");
ASSERT_TRUE(oPushBack);

// TODO: specialize caller for known 'void' return type.
// due to std::optional<void>, compiler error for now -
// rtl::method<rtl::RObject, void(int)> pushBack = oPushBack->targetT().argsT<int>().returnT<void>();

rtl::method<rtl::RObject, rtl::Return(int)> pushBack = oPushBack->targetT().argsT<int>().returnT();
rtl::method<rtl::RObject, void(int)> pushBack = oPushBack->targetT().argsT<int>().returnT<void>();
EXPECT_TRUE(pushBack);
{
auto [err, ret] = pushBack(robj)(intArr0[0]);
EXPECT_TRUE(err == rtl::error::None);
EXPECT_EQ(err, rtl::error::None);
EXPECT_EQ(ret, std::nullopt);
} {
auto [err, ret] = pushBack(robj)(intArr0[1]);
EXPECT_TRUE(err == rtl::error::None);
EXPECT_EQ(err, rtl::error::None);
EXPECT_EQ(ret, std::nullopt);
} {
auto [err, ret] = pushBack(robj)(intArr0[2]);
EXPECT_TRUE(err == rtl::error::None);
EXPECT_EQ(err, rtl::error::None);
EXPECT_EQ(ret, std::nullopt);
}
}

Expand Down
38 changes: 38 additions & 0 deletions RTLTestRunApp/src/FunctionalityTests/ConstMethodOverloadTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <gtest/gtest.h>

#include "TestMirrorProvider.h"
#include "TestUtilsAnimal.h"
#include "TestUtilsPerson.h"
#include "TestUtilsBook.h"

Expand Down Expand Up @@ -246,4 +247,41 @@ namespace rtl_tests
EXPECT_TRUE(person::assert_zero_instance_count());
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
}


TEST(ConstMethodOverload, erased_target_known_return_void)
{
{
// Retrieve the metadata for the "Animal" class.
optional<Record> classAnimal = cxx::mirror().getRecord(animal::class_);
ASSERT_TRUE(classAnimal);

// Create an instance of the "Animal" class.
auto [err0, animal] = classAnimal->ctorT()(alloc::Heap);
EXPECT_TRUE(err0 == error::None);
ASSERT_FALSE(animal.isEmpty());

// Retrieve the "setAnimalName" method.
optional<Method> oSetAnimalName = classAnimal->getMethod(animal::str_setAnimalName);
ASSERT_TRUE(oSetAnimalName);
// Verify that the method has the correct signature for a const L-value reference.
EXPECT_TRUE((oSetAnimalName->hasSignature<const std::string&>()));

auto setAnimalName = oSetAnimalName->targetT().argsT<std::string>().returnT<void>();
EXPECT_TRUE(setAnimalName);

// Invoke the method with a const L-value reference.
auto [err1, ret1] = setAnimalName(std::cref(animal))(animal::NAME);

EXPECT_EQ(err1, error::None);
EXPECT_EQ(ret1, std::nullopt);

// Validate the behavior of the method.
EXPECT_TRUE(animal::test_method_const_setAnimalName_const_lvalue_ref_args(animal));
}

// Ensure that all instances are cleaned up.
EXPECT_TRUE(animal::assert_zero_instance_count());
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
}
}
2 changes: 1 addition & 1 deletion ReflectionTemplateLib/rtl/dispatch/aware_return_n_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace rtl::dispatch
if constexpr (std::is_void_v<return_t>)
{
(const_cast<record_t&>(target).*mptr)(std::forward<signature_t>(params)...);
return std::make_pair(error::None, std::optional<void*>());
return std::make_pair(error::None, std::optional<std::nullptr_t>());
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace rtl::dispatch
if constexpr (std::is_void_v<return_t>)
{
(target.*mptr)(std::forward<signature_t>(params)...);
return std::make_pair(error::None, std::optional<void*>());
return std::make_pair(error::None, std::optional<std::nullptr_t>());
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion ReflectionTemplateLib/rtl/dispatch/method_lambda.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace rtl::dispatch
{
using if_ref_t = std::conditional_t<std::is_reference_v<known_t>, std::remove_reference_t<known_t>*, known_t>;

using if_void_t = std::conditional_t<std::is_void_v<known_t>, void*, if_ref_t>;
using if_void_t = std::conditional_t<std::is_void_v<known_t>, std::nullptr_t, if_ref_t>;

using return_t = std::pair<error, std::optional<if_void_t>>;

Expand Down
9 changes: 5 additions & 4 deletions ReflectionTemplateLib/rtl/rtl_method_erased_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@

namespace rtl
{
// TODO: Needs to be well tested, special case return-type 'void' (which does not returns std::nullopt for now)
template<class return_t, class ...signature_t> requires (!std::is_same_v<return_t, Return>)
struct method<RObject, return_t(signature_t...)>
{
using hopper_t = dispatch::forward_call<std::pair<error, std::optional<return_t>>, const RObject&, signature_t...>;
using cond_ret_t = std::conditional_t<std::is_void_v<return_t>, std::nullptr_t, return_t>;

using hopper_t = dispatch::forward_call<std::pair<error, std::optional<cond_ret_t>>, const RObject&, signature_t...>;

struct invoker
{
Expand All @@ -30,7 +31,7 @@ namespace rtl
template<class ...args_t>
requires (sizeof...(args_t) == sizeof...(signature_t))
[[nodiscard]] [[gnu::hot]] [[gnu::flatten]]
constexpr std::pair<error, std::optional<return_t>> operator()(args_t&&...params) const noexcept
constexpr std::pair<error, std::optional<cond_ret_t>> operator()(args_t&&...params) const noexcept
{
if (init_err != error::None) [[unlikely]] {
return { init_err, std::nullopt };
Expand All @@ -53,7 +54,7 @@ namespace rtl

template<class ...args_t>
[[nodiscard]] [[gnu::hot]] [[gnu::flatten]]
constexpr std::pair<error, std::optional<return_t>> operator()(args_t&&...params) const noexcept
constexpr std::pair<error, std::optional<cond_ret_t>> operator()(args_t&&...params) const noexcept
{
if (init_err != error::None) [[unlikely]] {
return { init_err, std::nullopt };
Expand Down