Skip to content

Commit ab3b9cc

Browse files
committed
static_method test cases, error-codes, doc.
1 parent 5286a9f commit ab3b9cc

File tree

14 files changed

+207
-42
lines changed

14 files changed

+207
-42
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Reflection Template Library - Modern C++ Reflection Framework
1+
# Reflection Template Library (RTL) — A Modern C++ Run-Time Reflection Framework
22

3-
**Reflection Template Library (RTL)** brings rich run-time reflection to modern C++ — combining compile-time safety with run-time flexibility.
3+
**RTL** brings rich, type-safe run-time reflection to modern C++ — combining compile-time guarantees with run-time flexibility.
44

55
🪞 What's “Reflection”?
66

@@ -13,11 +13,12 @@ std::string complexToStr(float real, float img);
1313
rtl::function<std::string(float, float)> cToStr = cxx_mirror.getFunction("complexToStr") // cxx_mirror?? see quick preview!
1414
->argsT<float, float>()
1515
.returnT<std::string>();
16-
if(cToStr) { // All's well?
16+
if(cToStr) { // Function found?
1717
std::string result = cToStr(61, 35); // Works! (int → float? No problem.)
1818
}
1919
```
20-
*No includes. No compile-time linking. No argument type-casting. No guess work. Just run-time lookup & type-safe invocation*.
20+
*No includes. No compile-time linking. No argument type-casting. No guess work.*
21+
*Just run-time lookup and type-safe invocation*.
2122

2223
⚡ Performance!
2324

RTLTestRunApp/src/FunctionalityTests/StaticTypeReflectiveCalls/StrictStaticTypeDispatch_StaticMethod.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,57 @@ using namespace test_mirror;
1010

1111
namespace rtl_tests
1212
{
13+
TEST(StrictStaticTypeRtl_static_method, using_wrong_class_n_callable_apis_for_static_method)
14+
{
15+
{
16+
std::optional<rtl::Record> optStringUtil = cxx::mirror().getRecord(StringS::struct_); // has only static-methods.
17+
ASSERT_TRUE(optStringUtil);
18+
19+
std::optional<rtl::Method> reverseString = optStringUtil->getMethod(str_reverseString);
20+
ASSERT_TRUE(reverseString);
21+
{
22+
rtl::method<StringS, std::string(std::string&)> reverse_string = reverseString.value()
23+
.targetT<StringS>()
24+
.argsT<std::string&>()
25+
.returnT<std::string>();
26+
EXPECT_FALSE(reverse_string);
27+
EXPECT_EQ(reverse_string.get_init_error(), rtl::error::InvalidStaticMethodCaller);
28+
} {
29+
rtl::function<std::string(std::string&)> reverse_string = static_cast<rtl::Function>(reverseString.value())
30+
.argsT<std::string&>()
31+
.returnT<std::string>();
32+
EXPECT_FALSE(reverse_string);
33+
EXPECT_EQ(reverse_string.get_init_error(), rtl::error::InvalidStaticMethodCaller);
34+
}
35+
} {
36+
std::optional<rtl::Record> optStringUtil = cxx::mirror().getRecord(StringC::struct_); // doesn't have any static-methods.
37+
ASSERT_TRUE(optStringUtil);
38+
39+
std::optional<rtl::Method> reverseString = optStringUtil->getMethod(str_reverseString);
40+
ASSERT_TRUE(reverseString);
41+
42+
rtl::static_method<std::string(std::string&)> reverse_string = reverseString.value()
43+
.argsT<std::string&>()
44+
.returnT<std::string>();
45+
EXPECT_FALSE(reverse_string);
46+
EXPECT_EQ(reverse_string.get_init_error(), rtl::error::InvalidNonStaticMethodCaller);
47+
} {
48+
std::optional<rtl::Record> optStringUtil = cxx::mirror().getRecord(StringM::struct_); // doesn't have any static-methods.
49+
ASSERT_TRUE(optStringUtil);
50+
51+
std::optional<rtl::Method> reverseString = optStringUtil->getMethod(str_reverseString);
52+
ASSERT_TRUE(reverseString);
53+
{
54+
rtl::static_method<std::string(std::string&)> reverse_string = reverseString.value()
55+
.argsT<std::string&>()
56+
.returnT<std::string>();
57+
EXPECT_FALSE(reverse_string);
58+
EXPECT_EQ(reverse_string.get_init_error(), rtl::error::InvalidNonStaticMethodCaller);
59+
}
60+
}
61+
}
62+
63+
1364
TEST(StrictStaticTypeRtl_static_method, overload_resolution_with_known_signatures)
1465
{
1566
std::optional<rtl::Record> optStringUtil = cxx::mirror().getRecord(StringS::struct_);
@@ -70,4 +121,64 @@ namespace rtl_tests
70121
EXPECT_EQ(ret_str, exp_str);
71122
}
72123
}
124+
125+
126+
TEST(StrictStaticTypeRtl_static_method, lvalue_ref_overload_resolution_with_known_signatures)
127+
{
128+
std::optional<rtl::Record> optStringUtil = cxx::mirror().getRecord(StringS::struct_);
129+
ASSERT_TRUE(optStringUtil);
130+
131+
//non-const target.
132+
StringM target;
133+
std::optional<rtl::Method> reverseString = optStringUtil->getMethod(str_reverseString);
134+
ASSERT_TRUE(reverseString);
135+
{
136+
//argument lvalue-ref
137+
rtl::static_method<std::string(std::string&)> reverse_string = reverseString.value()
138+
.argsT<std::string&>()
139+
.returnT<std::string>();
140+
ASSERT_TRUE(reverse_string);
141+
std::string lv_str = STRA;
142+
std::string ret_str = reverse_string(lv_str);
143+
144+
auto exp_str = std::string(STRA_REVERSE) + SUFFIX_std_string_lvref + SUFFIX_static;
145+
EXPECT_EQ(ret_str, exp_str);
146+
} {
147+
//argument const-lvalue-ref
148+
rtl::static_method<std::string(const std::string&)> reverse_string = reverseString.value()
149+
.argsT<const std::string&>()
150+
.returnT<std::string>();
151+
ASSERT_TRUE(reverse_string);
152+
153+
const std::string lv_str = STRA;
154+
std::string ret_str = reverse_string(lv_str);
155+
auto exp_str = std::string(STRA_REVERSE) + SUFFIX_std_string_clvref + SUFFIX_static;
156+
EXPECT_EQ(ret_str, exp_str);
157+
} {
158+
//argument lvalue-ref
159+
rtl::static_method<std::string(std::string&)> reverse_string = reverseString.value()
160+
.argsT<std::string&>()
161+
.returnT<std::string>();
162+
ASSERT_TRUE(reverse_string);
163+
std::string lv_str = STRA;
164+
std::string ret_str = reverse_string(lv_str);
165+
166+
auto exp_str = std::string(STRA_REVERSE) + SUFFIX_std_string_lvref + SUFFIX_static;
167+
EXPECT_EQ(ret_str, exp_str);
168+
} {
169+
//argument const-lvalue-ref
170+
rtl::static_method<std::string(const std::string&)> reverse_string = reverseString.value()
171+
.argsT<const std::string&>()
172+
.returnT<std::string>();
173+
ASSERT_TRUE(reverse_string);
174+
//const-target.
175+
const StringM& c_target = target;
176+
std::string lv_str = STRA;
177+
178+
std::string ret_str = reverse_string(lv_str);
179+
180+
auto exp_str = std::string(STRA_REVERSE) + SUFFIX_std_string_clvref + SUFFIX_static;
181+
EXPECT_EQ(ret_str, exp_str);
182+
}
183+
}
73184
}

ReflectionTemplateLib/rtl/detail/inc/FunctionCaller.hpp

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace rtl::detail
5454
template<class return_t> requires (member_kind == member::Static && std::is_same_v<return_t, rtl::Return>)
5555
inline constexpr const static_method<Return(args_t...)> HopFunction<member_kind, args_t...>::returnT() const
5656
{
57-
static_method<Return(args_t...)> erasedFn;
57+
static_method<Return(traits::normal_sign_t<args_t>...)> erasedFn;
5858
initHopper(erasedFn);
5959
return erasedFn;
6060
}
@@ -63,30 +63,44 @@ namespace rtl::detail
6363
template<class return_t> requires (member_kind == member::Static && !std::is_same_v<return_t, rtl::Return>)
6464
inline constexpr const static_method<return_t(args_t...)> HopFunction<member_kind, args_t...>::returnT() const
6565
{
66-
const auto retId = traits::uid<return_t>::value;
66+
static_method<return_t(args_t...)> fn;
6767
if (!m_argsTfnMeta.is_empty())
6868
{
69-
return m_argsTfnMeta.get_lambda()
70-
.template to_function<args_t...>()
71-
.template get_hopper<return_t>(retId)
72-
.f_ptr();
69+
if (m_argsTfnMeta.get_member_kind() != member::Static) {
70+
fn.set_init_error(error::InvalidNonStaticMethodCaller);
71+
}
72+
else if (m_argsTfnMeta.get_member_kind() == member::Static) {
73+
74+
const auto retId = traits::uid<return_t>::value;
75+
return m_argsTfnMeta.get_lambda()
76+
.template to_function<args_t...>()
77+
.template get_hopper<return_t>(retId)
78+
.f_ptr();
79+
}
7380
}
74-
return static_method<return_t(args_t...)>();
81+
return fn;
7582
}
7683

7784

7885
template<member member_kind, class ...args_t>
7986
template<class return_t> requires (member_kind == member::None && !std::is_same_v<return_t, rtl::Return>)
8087
inline constexpr const function<return_t(args_t...)> HopFunction<member_kind, args_t...>::returnT() const
8188
{
82-
const auto retId = traits::uid<return_t>::value;
89+
function<return_t(args_t...)> fn;
8390
if (!m_argsTfnMeta.is_empty())
8491
{
85-
return m_argsTfnMeta.get_lambda()
86-
.template to_function<args_t...>()
87-
.template get_hopper<return_t>(retId);
92+
if (m_argsTfnMeta.get_member_kind() == member::Static) {
93+
fn.set_init_error(error::InvalidStaticMethodCaller);
94+
}
95+
else if (m_argsTfnMeta.get_member_kind() == member::None) {
96+
97+
const auto retId = traits::uid<return_t>::value;
98+
return m_argsTfnMeta.get_lambda()
99+
.template to_function<args_t...>()
100+
.template get_hopper<return_t>(retId);
101+
}
88102
}
89-
return function<return_t(args_t...)>();
103+
return fn;
90104
}
91105

92106

@@ -104,6 +118,11 @@ namespace rtl::detail
104118
continue;
105119
}
106120

121+
if (fnMeta.get_member_kind() != member::None && fnMeta.get_member_kind() != member::Static) {
122+
pHopFn.set_init_error(error::InvalidNonStaticMethodCaller);
123+
return;
124+
}
125+
107126
auto& erasedRetFn = fnMeta.get_erasure_base()
108127
.template to_erased_return<traits::normal_sign_t<args_t>...>();
109128
if (fnMeta.is_void()) {
@@ -114,16 +133,14 @@ namespace rtl::detail
114133
pHopFn.get_rhop().push_back(erasedRetFn.get_return_hopper());
115134
}
116135
pHopFn.get_overloads().push_back(&fnMeta.get_lambda());
136+
pHopFn.set_init_error(error::None);
117137
}
118138
if (isReturnTvoid) {
119139
pHopFn.get_rhop().clear();
120140
}
121141
else {
122142
pHopFn.get_vhop().clear();
123143
}
124-
if (pHopFn) {
125-
pHopFn.set_init_error(error::None);
126-
}
127144
}
128145

129146

ReflectionTemplateLib/rtl/detail/inc/MethodInvoker.hpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,21 @@ namespace rtl::detail
172172
template<class return_t> requires (traits::type_aware_v<record_t, return_t>)
173173
inline constexpr const method<record_t, return_t(args_t...)> HopMethod<record_t, args_t...>::returnT() const
174174
{
175-
if (!m_argsTfnMeta.is_empty() && m_argsTfnMeta.get_member_kind() != member::Static)
175+
method<record_t, return_t(args_t...)> mth;
176+
if (!m_argsTfnMeta.is_empty())
176177
{
177-
const auto retId = traits::uid<return_t>::value;
178-
return m_argsTfnMeta.get_lambda()
179-
.template to_method<record_t, args_t...>()
180-
.template get_hopper<return_t>(retId);
178+
if (m_argsTfnMeta.get_member_kind() == member::Static) {
179+
mth.set_init_error(error::InvalidStaticMethodCaller);
180+
}
181+
else {
182+
183+
const auto retId = traits::uid<return_t>::value;
184+
return m_argsTfnMeta.get_lambda()
185+
.template to_method<record_t, args_t...>()
186+
.template get_hopper<return_t>(retId);
187+
}
181188
}
182-
return method<record_t, return_t(args_t...)>();
189+
return mth;
183190
}
184191

185192

@@ -275,16 +282,13 @@ namespace rtl::detail
275282
pMth.get_rhop().push_back(erasedFn.get_return_hopper());
276283
}
277284
pMth.get_overloads().push_back(&fnMeta.get_lambda());
278-
285+
pMth.set_init_error(error::None);
279286
}
280287
if (isReturnTvoid) {
281288
pMth.get_rhop().clear();
282289
}
283290
else {
284291
pMth.get_vhop().clear();
285292
}
286-
if (pMth) {
287-
pMth.set_init_error(error::None);
288-
}
289293
}
290294
}

ReflectionTemplateLib/rtl/dispatch/rtl_function.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ namespace rtl
5858
void set_init_error(error p_err) {
5959
m_init_err = p_err;
6060
}
61+
62+
template<detail::member, class ...>
63+
friend struct detail::HopFunction;
6164
};
6265
}
6366

@@ -78,5 +81,8 @@ namespace rtl
7881

7982
static_method& operator=(static_method&&) = default;
8083
static_method& operator=(const static_method&) = default;
84+
85+
template<detail::member, class ...>
86+
friend struct detail::HopFunction;
8187
};
8288
}

ReflectionTemplateLib/rtl/dispatch/rtl_function_erased_return.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ namespace rtl
9595
}
9696

9797
constexpr operator bool() const noexcept {
98-
return !(m_lambdas.empty() || (m_lambdas.size() == 1 && m_lambdas[0] == nullptr));
98+
return !(m_init_err != error::None || m_lambdas.empty() ||
99+
(m_lambdas.size() == 1 && m_lambdas[0] == nullptr));
100+
99101
}
100102

101103
constexpr bool must_bind_refs() const noexcept {
@@ -147,5 +149,8 @@ namespace rtl
147149
{
148150
template<class ...signature_t>
149151
struct static_method<Return(signature_t...)> : function<Return(signature_t...)>
150-
{ };
152+
{
153+
template<detail::member, class ...>
154+
friend struct detail::HopFunction;
155+
};
151156
}

ReflectionTemplateLib/rtl/dispatch/rtl_method.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,8 @@ namespace rtl
7777
void set_init_error(error p_err) {
7878
m_init_err = p_err;
7979
}
80+
81+
template<class, class ...>
82+
friend struct detail::HopMethod;
8083
};
8184
}

ReflectionTemplateLib/rtl/dispatch/rtl_method_const.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,8 @@ namespace rtl
7272
void set_init_error(error p_err) {
7373
m_init_err = p_err;
7474
}
75+
76+
template<class, class ...>
77+
friend struct detail::HopMethod;
7578
};
7679
}

ReflectionTemplateLib/rtl/dispatch/rtl_method_erased.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ namespace rtl
116116
}
117117

118118
constexpr operator bool() const noexcept {
119-
return !(m_lambdas.empty() || (m_lambdas.size() == 1 && m_lambdas[0] == nullptr));
119+
return !(m_init_err != error::None || m_lambdas.empty() ||
120+
(m_lambdas.size() == 1 && m_lambdas[0] == nullptr));
120121
}
121122

122123
constexpr bool must_bind_refs() const noexcept {

ReflectionTemplateLib/rtl/dispatch/rtl_method_erased_return.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ namespace rtl
116116
}
117117

118118
constexpr operator bool() const noexcept {
119-
return !(m_lambdas.empty() || (m_lambdas.size() == 1 && m_lambdas[0] == nullptr));
119+
return !(m_init_err != error::None || m_lambdas.empty() ||
120+
(m_lambdas.size() == 1 && m_lambdas[0] == nullptr));
121+
120122
}
121123

122124
constexpr bool must_bind_refs() const noexcept {

0 commit comments

Comments
 (0)