diff --git a/.codecov.yml b/.codecov.yml index f8836a89..2b6e77a9 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -13,4 +13,5 @@ comment: ignore: - "demo/**" - - "RTLBenchmarkApp/**" \ No newline at end of file + - "RTLBenchmarkApp/**" + - "ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp" \ No newline at end of file diff --git a/.github/workflows/FUNDING.yml b/.github/FUNDING.yml similarity index 100% rename from .github/workflows/FUNDING.yml rename to .github/FUNDING.yml diff --git a/README.md b/README.md index b047adf2..4c0f0c34 100644 --- a/README.md +++ b/README.md @@ -170,12 +170,12 @@ RTL provides the following callable entities, designed to be as lightweight and These callable types are regular value types: they can be copied, moved, stored in standard containers, and passed around like any other lightweight object. -When invoked, only type-erased callables return an `rtl::error`, with results provided as `rtl::RObject` when both the return and target types are erased or as `std::optional` when only the target type is erased, while fully type-aware callables return `T` directly with no error (by design). +When invoked, only type-erased callables return an `rtl::error`, with results provided as `rtl::RObject` *(when both the return and target types are erased)* or as `std::optional` *(when only the target type is erased)*, while fully type-aware callables return `T` directly with no error (by design). ### How to Build (Windows / Linux) ```sh mkdir build && cd build -cmake -G "" # Use a C++20-compatible compiler +cmake ../ -G "" # Use a C++20-compatible compiler cmake --build . ``` Run the generated binaries from `bin/`: diff --git a/RTLTestRunApp/src/RObjectTests/RObjectReflecting_strings.cpp b/RTLTestRunApp/src/RObjectTests/RObjectReflecting_strings.cpp index 292b58c3..24387071 100644 --- a/RTLTestRunApp/src/RObjectTests/RObjectReflecting_strings.cpp +++ b/RTLTestRunApp/src/RObjectTests/RObjectReflecting_strings.cpp @@ -337,43 +337,43 @@ namespace unit_test // Create an RObject that reflects a string value (init with 'std::string'). RObject robj = rtl::reflect(STR_STD_STRING); - // Check if the value can be accessed as 'std::string'. - ASSERT_TRUE(robj.canViewAs()); - - // Try to obtain a view as 'std::string' and verify it is present. - auto view0 = robj.view(); - ASSERT_TRUE(view0.has_value()); - - // Validate the string content matches the original input. - const std::string& str_cref0 = view0->get(); - ASSERT_EQ(str_cref0, STR_STD_STRING); - - auto [err, robjcp] = robj.clone(); - EXPECT_EQ(err, rtl::error::None); - EXPECT_EQ(robj.getTypeId(), robjcp.getTypeId()); - - // Check if the value can be accessed as 'std::string'. - ASSERT_TRUE(robj.canViewAs()); - - // Try to obtain a view as 'std::string' and verify it is present. - auto view1 = robj.view(); - ASSERT_TRUE(view1.has_value()); - - // Validate the string content matches the original input. - const std::string& str_cref1 = view1->get(); - ASSERT_EQ(str_cref1, STR_STD_STRING); - ASSERT_TRUE(robjcp.canViewAs()); - ASSERT_TRUE(robjcp.canViewAs()); - - //TODO: Fix the crash here. - - // Try to obtain a view as 'const char*' and verify it is present. - //auto view2 = robjcp.view(); - //ASSERT_TRUE(view2.has_value()); - - //// Validate the base address are different, since RObject is reflecting a copy. - //const char& str_addr = view2->get(); - //ASSERT_NE(&str_addr, STR_STD_STRING.c_str()); + EXPECT_TRUE(robj.canViewAs()); + EXPECT_TRUE(robj.canViewAs()); + + auto testWithAlloc = [&]() + { + auto [err, robjcp] = robj.clone(); + + EXPECT_EQ(err, rtl::error::None); + EXPECT_EQ(robj.getTypeId(), robjcp.getTypeId()); + { + // Check if the value can be accessed as 'std::string'. + ASSERT_TRUE(robjcp.template canViewAs()); + + // Try to obtain a view as 'std::string' and verify it is present. + auto view = robjcp.template view(); + ASSERT_TRUE(view.has_value()); + + // Validate the string content matches the original input. + const std::string& str_cref = view->get(); + ASSERT_EQ(str_cref, STR_STD_STRING); + } { + ASSERT_TRUE(robjcp.template canViewAs()); + + // Try to obtain a view as 'const char*' and verify it is present. + auto view = robjcp.template view(); + ASSERT_TRUE(view.has_value()); + + // Validate the base address are different, since RObject is reflecting a copy. + const char& str_addr = view->get(); + ASSERT_NE(&str_addr, STR_STD_STRING.c_str()); + } + }; + + // Should even std::string to 'char' conversion be allowed implicitly? + // Need to re-think it. The implicit conversions are not used in core dispatch yet. + testWithAlloc.operator()(); + testWithAlloc.operator()(); } diff --git a/ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp b/ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp index f426169f..d3e2738f 100644 --- a/ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp +++ b/ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace rtl::detail { @@ -20,10 +21,27 @@ namespace rtl::detail { const auto& conversion = [](const std::any& pSrc, const EntityKind& pSrcEntityKind, EntityKind& pNewEntityKind)-> std::any { - pNewEntityKind = EntityKind::Ptr; - const auto& isPtr = (pSrcEntityKind == EntityKind::Ptr); - const auto& srcObj = (isPtr ? *std::any_cast(pSrc) : std::any_cast(pSrc)); - return std::any(srcObj.c_str()); + try { + pNewEntityKind = EntityKind::Ptr; + const auto& isPtr = (pSrcEntityKind == EntityKind::Ptr); + // GCOVR_EXCL_START + if (pSrcEntityKind == EntityKind::Wrapper) { + //TODO: Will fail for any other wrapper other than 'RObjectUPtr<>'. + const auto& srcRUptr = std::any_cast&>(pSrc); + return std::any(srcRUptr.get()->c_str()); + } + // GCOVR_EXCL_STOP + else { + const auto& srcObj = (isPtr ? *std::any_cast(pSrc) : std::any_cast(pSrc)); + return std::any(srcObj.c_str()); + } + } + // GCOVR_EXCL_START + catch (const std::exception&) { + pNewEntityKind = EntityKind::None; + return std::any(); + } + // GCOVR_EXCL_STOP }; conversions().emplace_back(std::pair(traits::uid::value, conversion)); } @@ -35,10 +53,27 @@ namespace rtl::detail { const auto& conversion = [](const std::any& pSrc, const EntityKind& pSrcEntityKind, EntityKind& pNewEntityKind)-> std::any { - pNewEntityKind = EntityKind::Ptr; - const auto& isPtr = (pSrcEntityKind == EntityKind::Ptr); - const auto& srcObj = (isPtr ? *std::any_cast(pSrc) : std::any_cast(pSrc)); - return std::any(srcObj.data()); + try { + pNewEntityKind = EntityKind::Ptr; + const auto& isPtr = (pSrcEntityKind == EntityKind::Ptr); + // GCOVR_EXCL_START + if (pSrcEntityKind == EntityKind::Wrapper) { + //TODO: Will fail for any other wrapper other than 'RObjectUPtr<>'. + const auto& srcRUptr = std::any_cast&>(pSrc); + return std::any(srcRUptr.get()->data()); + } + // GCOVR_EXCL_STOP + else { + const auto& srcObj = (isPtr ? *std::any_cast(pSrc) : std::any_cast(pSrc)); + return std::any(srcObj.data()); + } + } + // GCOVR_EXCL_START + catch (const std::exception&) { + pNewEntityKind = EntityKind::None; + return std::any(); + } + // GCOVR_EXCL_STOP }; conversions().emplace_back(std::pair(traits::uid::value, conversion)); } @@ -51,10 +86,27 @@ namespace rtl::detail using _toType = std::string; const auto& conversion = [](const std::any& pSrc, const EntityKind& pSrcEntityKind, EntityKind& pNewEntityKind)-> std::any { - pNewEntityKind = EntityKind::Value; - const auto& isPtr = (pSrcEntityKind == EntityKind::Ptr); - const auto& srcObj = (isPtr ? *std::any_cast(pSrc) : std::any_cast(pSrc)); - return std::any(_toType(srcObj)); + try { + pNewEntityKind = EntityKind::Value; + const auto& isPtr = (pSrcEntityKind == EntityKind::Ptr); + // GCOVR_EXCL_START + if (pSrcEntityKind == EntityKind::Wrapper) { + //TODO: Will fail for any other wrapper other than 'RObjectUPtr<>'. + const auto& srcRUptr = std::any_cast&>(pSrc); + return std::any(_toType(*srcRUptr.get())); + } + // GCOVR_EXCL_STOP + else { + const auto& srcObj = (isPtr ? *std::any_cast(pSrc) : std::any_cast(pSrc)); + return std::any(_toType(srcObj)); + } + } + // GCOVR_EXCL_START + catch (const std::exception&) { + pNewEntityKind = EntityKind::None; + return std::any(); + } + // GCOVR_EXCL_STOP }; conversions().emplace_back(std::pair(traits::uid<_toType>::value, conversion)); }