rice 4.11.2 → 4.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/CMakePresets.json +3 -3
- data/include/rice/rice.hpp +27 -20
- data/include/rice/stl.hpp +22 -10
- data/lib/mkmf-rice.rb +6 -4
- data/lib/rice/version.rb +1 -1
- data/rice/detail/Wrapper.ipp +3 -1
- data/rice/detail/from_ruby.ipp +3 -19
- data/rice/stl/map.ipp +2 -2
- data/rice/stl/ostream.ipp +5 -1
- data/rice/stl/pair.ipp +6 -2
- data/rice/stl/shared_ptr.ipp +6 -2
- data/rice/stl/unique_ptr.ipp +1 -1
- data/rice/stl/unordered_map.ipp +2 -2
- data/rice/traits/function_traits.hpp +21 -0
- data/test/test_Attribute.cpp +31 -0
- data/test/test_Callback.cpp +2 -4
- data/test/test_From_Ruby.cpp +14 -0
- data/test/test_Stl_Map.cpp +56 -0
- data/test/test_Stl_Pair.cpp +32 -0
- data/test/test_Stl_SharedPtr.cpp +93 -0
- data/test/test_Stl_UniquePtr.cpp +47 -0
- data/test/test_Stl_Unordered_Map.cpp +56 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2b52ff5ddc64209e1c252d529111695d833a57d1362e15b6d223777194dc6750
|
|
4
|
+
data.tar.gz: 214c2b675683e68d026440cc83499a7de0faa6282563c0bfa028c925e677f195
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fdee833532115137ec7e375e83e377c5e21a634b44e0b67bb5954ea3ad04a2983ef3c6ffc5d5deb804b1d94c7b76f04d2cc94b73b054aaa0722f01fd39f58c37
|
|
7
|
+
data.tar.gz: 23e9990bbf398a067608c29028018840506254f3a8a00a7f6cc56840ec88e70007d0309656b297b20ab31776805b3deccb14e16fface820b0ec7d3340aafc0f3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.11.3 (2026-03-11)
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
* Fix C++20 compilation error with `std::ostringstream::str` overload (#395)
|
|
8
|
+
* Add `function_traits` specializations for ref-qualified member functions (`&`, `const&`)
|
|
9
|
+
|
|
10
|
+
### Enhancements
|
|
11
|
+
|
|
12
|
+
* Support C++20 and C++23 compilation
|
|
13
|
+
* Make C++ standard version configurable via `--with-cxx-standard` in mkmf-rice
|
|
14
|
+
* Add `/Zc:__cplusplus` to MSVC build flags
|
|
15
|
+
* Add CMake-based CI job testing C++17, C++20, and C++23
|
|
16
|
+
* Add test case for function pointer struct attributes
|
|
17
|
+
* Add support for `std::shared_ptr<const T>`
|
|
18
|
+
* Add support for `std::unique_ptr<const T>`
|
|
19
|
+
* Add support for non-constructible objects in std::pair, std::map, std::multimap
|
|
20
|
+
* Add support for wrapping pointers to abstract types that cannot be created nor destroyed
|
|
21
|
+
* Fix converting std::nullptr_t to Ruby
|
|
22
|
+
|
|
3
23
|
## 4.11.2 (2026-02-21)
|
|
4
24
|
|
|
5
25
|
### Enhancements
|
data/CMakePresets.json
CHANGED
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"toolchainFile": "$env{VCPKG_ROOT}\\scripts\\buildsystems\\vcpkg.cmake",
|
|
117
117
|
"cacheVariables": {
|
|
118
118
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
|
119
|
-
"CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE"
|
|
119
|
+
"CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /Zc:__cplusplus /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE"
|
|
120
120
|
},
|
|
121
121
|
"condition": {
|
|
122
122
|
"type": "equals",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
"cacheVariables": {
|
|
156
156
|
"CMAKE_BUILD_TYPE": "Debug",
|
|
157
157
|
"CMAKE_CXX_COMPILER": "clang-cl.exe",
|
|
158
|
-
"CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /clang:-Wno-unused-private-field",
|
|
158
|
+
"CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /Zc:__cplusplus /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /clang:-Wno-unused-private-field",
|
|
159
159
|
"CMAKE_CXX_FLAGS_DEBUG": "/Od /Zi"
|
|
160
160
|
}
|
|
161
161
|
},
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
"cacheVariables": {
|
|
167
167
|
"CMAKE_BUILD_TYPE": "Release",
|
|
168
168
|
"CMAKE_CXX_COMPILER": "clang-cl.exe",
|
|
169
|
-
"CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /clang:-Wno-unused-private-field",
|
|
169
|
+
"CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /Zc:__cplusplus /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /clang:-Wno-unused-private-field",
|
|
170
170
|
"CMAKE_CXX_FLAGS_RELEASE": "/O2 /DNDEBUG",
|
|
171
171
|
"CMAKE_INTERPROCEDURAL_OPTIMIZATION": "ON"
|
|
172
172
|
}
|
data/include/rice/rice.hpp
CHANGED
|
@@ -556,6 +556,27 @@ namespace Rice::detail
|
|
|
556
556
|
{
|
|
557
557
|
};
|
|
558
558
|
|
|
559
|
+
// ref-qualified member Functions on C++ classes (C++20 uses these for std library types)
|
|
560
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
561
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) &> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
562
|
+
{
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
566
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) const&> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
567
|
+
{
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
571
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) & noexcept> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
572
|
+
{
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
576
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) const& noexcept> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
577
|
+
{
|
|
578
|
+
};
|
|
579
|
+
|
|
559
580
|
/*// Functors and lambdas
|
|
560
581
|
template<class Function_T>
|
|
561
582
|
struct function_traits<Function_T&> : public function_traits<Function_T>
|
|
@@ -8854,31 +8875,15 @@ namespace Rice::detail
|
|
|
8854
8875
|
}
|
|
8855
8876
|
}
|
|
8856
8877
|
|
|
8857
|
-
|
|
8878
|
+
std::nullptr_t convert(VALUE value)
|
|
8858
8879
|
{
|
|
8859
8880
|
if (value == Qnil)
|
|
8860
8881
|
{
|
|
8861
8882
|
return nullptr;
|
|
8862
8883
|
}
|
|
8863
8884
|
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
return (void*)value;
|
|
8867
|
-
}
|
|
8868
|
-
|
|
8869
|
-
switch (rb_type(value))
|
|
8870
|
-
{
|
|
8871
|
-
case RUBY_T_NIL:
|
|
8872
|
-
{
|
|
8873
|
-
return nullptr;
|
|
8874
|
-
break;
|
|
8875
|
-
}
|
|
8876
|
-
default:
|
|
8877
|
-
{
|
|
8878
|
-
throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
|
|
8879
|
-
detail::protect(rb_obj_classname, value), "nil");
|
|
8880
|
-
}
|
|
8881
|
-
}
|
|
8885
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
|
|
8886
|
+
detail::protect(rb_obj_classname, value), "nil");
|
|
8882
8887
|
}
|
|
8883
8888
|
private:
|
|
8884
8889
|
Arg* arg_ = nullptr;
|
|
@@ -10277,7 +10282,9 @@ namespace Rice::detail
|
|
|
10277
10282
|
|
|
10278
10283
|
if constexpr (is_complete_v<T>)
|
|
10279
10284
|
{
|
|
10280
|
-
|
|
10285
|
+
// is_abstract_v requires a complete type, so nest inside is_complete_v.
|
|
10286
|
+
// Deleting an abstract class through a non-virtual destructor is UB.
|
|
10287
|
+
if constexpr (std::is_destructible_v<T> && !std::is_abstract_v<T>)
|
|
10281
10288
|
{
|
|
10282
10289
|
if (this->isOwner_)
|
|
10283
10290
|
{
|
data/include/rice/stl.hpp
CHANGED
|
@@ -1133,8 +1133,12 @@ namespace Rice::stl
|
|
|
1133
1133
|
|
|
1134
1134
|
void define_methods()
|
|
1135
1135
|
{
|
|
1136
|
+
#if __cplusplus >= 202002L
|
|
1137
|
+
klass_.define_method<std::string(std::ostringstream::*)() const&>("str", &std::ostringstream::str)
|
|
1138
|
+
#else
|
|
1136
1139
|
klass_.define_method<std::string(std::ostringstream::*)() const>("str", &std::ostringstream::str)
|
|
1137
|
-
|
|
1140
|
+
#endif
|
|
1141
|
+
.define_method("str=", [](std::ostringstream& stream, const std::string& s) { stream.str(s); }, Arg("str"));
|
|
1138
1142
|
|
|
1139
1143
|
rb_define_alias(klass_, "to_s", "str");
|
|
1140
1144
|
}
|
|
@@ -1310,8 +1314,12 @@ namespace Rice
|
|
|
1310
1314
|
private:
|
|
1311
1315
|
void define_constructors()
|
|
1312
1316
|
{
|
|
1313
|
-
|
|
1314
|
-
|
|
1317
|
+
if constexpr (std::is_default_constructible_v<T>)
|
|
1318
|
+
{
|
|
1319
|
+
klass_.define_constructor(Constructor<T>());
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
klass_.define_constructor(Constructor<T, First_Parameter_T, Second_Parameter_T>(), Arg("x").keepAlive(), Arg("y").keepAlive());
|
|
1315
1323
|
|
|
1316
1324
|
if constexpr (std::is_copy_constructible_v<First_T> && std::is_copy_constructible_v<Second_T>)
|
|
1317
1325
|
{
|
|
@@ -1650,7 +1658,7 @@ namespace Rice
|
|
|
1650
1658
|
}, Arg("key"))
|
|
1651
1659
|
.define_method("[]=", [](T& map, Key_T key, Mapped_Parameter_T value) -> Mapped_T
|
|
1652
1660
|
{
|
|
1653
|
-
map
|
|
1661
|
+
map.insert_or_assign(key, value);
|
|
1654
1662
|
return value;
|
|
1655
1663
|
}, Arg("key").keepAlive(), Arg("value").keepAlive());
|
|
1656
1664
|
|
|
@@ -1772,7 +1780,7 @@ namespace Rice
|
|
|
1772
1780
|
// exceptions propogate back to Ruby
|
|
1773
1781
|
return cpp_protect([&]
|
|
1774
1782
|
{
|
|
1775
|
-
result->
|
|
1783
|
+
result->insert_or_assign(From_Ruby<T>().convert(key), From_Ruby<U>().convert(value));
|
|
1776
1784
|
return ST_CONTINUE;
|
|
1777
1785
|
});
|
|
1778
1786
|
}
|
|
@@ -3201,7 +3209,11 @@ namespace Rice
|
|
|
3201
3209
|
|
|
3202
3210
|
if constexpr (detail::is_complete_v<T> && !std::is_void_v<T>)
|
|
3203
3211
|
{
|
|
3204
|
-
|
|
3212
|
+
// is_abstract_v requires a complete type, so it must be nested inside the is_complete_v check
|
|
3213
|
+
if constexpr (!std::is_abstract_v<T>)
|
|
3214
|
+
{
|
|
3215
|
+
result.define_constructor(Constructor<SharedPtr_T, typename SharedPtr_T::element_type*>(), Arg("value").takeOwnership());
|
|
3216
|
+
}
|
|
3205
3217
|
}
|
|
3206
3218
|
|
|
3207
3219
|
// Forward methods to wrapped T
|
|
@@ -3256,7 +3268,7 @@ namespace Rice::detail
|
|
|
3256
3268
|
}
|
|
3257
3269
|
else if (rb_typeddata_inherited_p(this->inner_rb_data_type_, requestedType))
|
|
3258
3270
|
{
|
|
3259
|
-
return this->data_.get();
|
|
3271
|
+
return (void*)this->data_.get();
|
|
3260
3272
|
}
|
|
3261
3273
|
else
|
|
3262
3274
|
{
|
|
@@ -3931,7 +3943,7 @@ namespace Rice::detail
|
|
|
3931
3943
|
}
|
|
3932
3944
|
else if (rb_typeddata_inherited_p(this->inner_rb_data_type_, requestedType))
|
|
3933
3945
|
{
|
|
3934
|
-
return this->data_.get();
|
|
3946
|
+
return (void*)this->data_.get();
|
|
3935
3947
|
}
|
|
3936
3948
|
else
|
|
3937
3949
|
{
|
|
@@ -4171,7 +4183,7 @@ namespace Rice
|
|
|
4171
4183
|
}, Arg("key"))
|
|
4172
4184
|
.define_method("[]=", [](T& unordered_map, Key_T key, Mapped_Parameter_T value) -> Mapped_T
|
|
4173
4185
|
{
|
|
4174
|
-
unordered_map
|
|
4186
|
+
unordered_map.insert_or_assign(key, value);
|
|
4175
4187
|
return value;
|
|
4176
4188
|
}, Arg("key").keepAlive(), Arg("value").keepAlive());
|
|
4177
4189
|
|
|
@@ -4293,7 +4305,7 @@ namespace Rice
|
|
|
4293
4305
|
// exceptions propogate back to Ruby
|
|
4294
4306
|
return cpp_protect([&]
|
|
4295
4307
|
{
|
|
4296
|
-
result->
|
|
4308
|
+
result->insert_or_assign(From_Ruby<T>().convert(key), From_Ruby<U>().convert(value));
|
|
4297
4309
|
return ST_CONTINUE;
|
|
4298
4310
|
});
|
|
4299
4311
|
}
|
data/lib/mkmf-rice.rb
CHANGED
|
@@ -24,14 +24,16 @@ end
|
|
|
24
24
|
# Now pull in the C++ support
|
|
25
25
|
include MakeMakefile['C++']
|
|
26
26
|
|
|
27
|
-
# Rice needs c++17.
|
|
27
|
+
# Rice needs c++17 or higher. Use --with-cxx-standard=20 to override.
|
|
28
|
+
std = with_config('cxx-standard', '17')
|
|
29
|
+
|
|
28
30
|
if IS_MSWIN
|
|
29
|
-
$CXXFLAGS += " /std:c
|
|
31
|
+
$CXXFLAGS += " /std:c++#{std} /EHs /permissive- /bigobj /utf-8 /Zc:__cplusplus"
|
|
30
32
|
$CPPFLAGS += " -D_ALLOW_KEYWORD_MACROS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE"
|
|
31
33
|
elsif IS_MINGW
|
|
32
|
-
$CXXFLAGS += " -std=c
|
|
34
|
+
$CXXFLAGS += " -std=c++#{std} -Wa,-mbig-obj"
|
|
33
35
|
else
|
|
34
|
-
$CXXFLAGS += " -std=c
|
|
36
|
+
$CXXFLAGS += " -std=c++#{std}"
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# Rice needs to include its header. Let's setup the include path
|
data/lib/rice/version.rb
CHANGED
data/rice/detail/Wrapper.ipp
CHANGED
|
@@ -121,7 +121,9 @@ namespace Rice::detail
|
|
|
121
121
|
|
|
122
122
|
if constexpr (is_complete_v<T>)
|
|
123
123
|
{
|
|
124
|
-
|
|
124
|
+
// is_abstract_v requires a complete type, so nest inside is_complete_v.
|
|
125
|
+
// Deleting an abstract class through a non-virtual destructor is UB.
|
|
126
|
+
if constexpr (std::is_destructible_v<T> && !std::is_abstract_v<T>)
|
|
125
127
|
{
|
|
126
128
|
if (this->isOwner_)
|
|
127
129
|
{
|
data/rice/detail/from_ruby.ipp
CHANGED
|
@@ -1608,31 +1608,15 @@ namespace Rice::detail
|
|
|
1608
1608
|
}
|
|
1609
1609
|
}
|
|
1610
1610
|
|
|
1611
|
-
|
|
1611
|
+
std::nullptr_t convert(VALUE value)
|
|
1612
1612
|
{
|
|
1613
1613
|
if (value == Qnil)
|
|
1614
1614
|
{
|
|
1615
1615
|
return nullptr;
|
|
1616
1616
|
}
|
|
1617
1617
|
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
return (void*)value;
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
switch (rb_type(value))
|
|
1624
|
-
{
|
|
1625
|
-
case RUBY_T_NIL:
|
|
1626
|
-
{
|
|
1627
|
-
return nullptr;
|
|
1628
|
-
break;
|
|
1629
|
-
}
|
|
1630
|
-
default:
|
|
1631
|
-
{
|
|
1632
|
-
throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
|
|
1633
|
-
detail::protect(rb_obj_classname, value), "nil");
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1618
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
|
|
1619
|
+
detail::protect(rb_obj_classname, value), "nil");
|
|
1636
1620
|
}
|
|
1637
1621
|
private:
|
|
1638
1622
|
Arg* arg_ = nullptr;
|
data/rice/stl/map.ipp
CHANGED
|
@@ -156,7 +156,7 @@ namespace Rice
|
|
|
156
156
|
}, Arg("key"))
|
|
157
157
|
.define_method("[]=", [](T& map, Key_T key, Mapped_Parameter_T value) -> Mapped_T
|
|
158
158
|
{
|
|
159
|
-
map
|
|
159
|
+
map.insert_or_assign(key, value);
|
|
160
160
|
return value;
|
|
161
161
|
}, Arg("key").keepAlive(), Arg("value").keepAlive());
|
|
162
162
|
|
|
@@ -278,7 +278,7 @@ namespace Rice
|
|
|
278
278
|
// exceptions propogate back to Ruby
|
|
279
279
|
return cpp_protect([&]
|
|
280
280
|
{
|
|
281
|
-
result->
|
|
281
|
+
result->insert_or_assign(From_Ruby<T>().convert(key), From_Ruby<U>().convert(value));
|
|
282
282
|
return ST_CONTINUE;
|
|
283
283
|
});
|
|
284
284
|
}
|
data/rice/stl/ostream.ipp
CHANGED
|
@@ -59,8 +59,12 @@ namespace Rice::stl
|
|
|
59
59
|
|
|
60
60
|
void define_methods()
|
|
61
61
|
{
|
|
62
|
+
#if __cplusplus >= 202002L
|
|
63
|
+
klass_.define_method<std::string(std::ostringstream::*)() const&>("str", &std::ostringstream::str)
|
|
64
|
+
#else
|
|
62
65
|
klass_.define_method<std::string(std::ostringstream::*)() const>("str", &std::ostringstream::str)
|
|
63
|
-
|
|
66
|
+
#endif
|
|
67
|
+
.define_method("str=", [](std::ostringstream& stream, const std::string& s) { stream.str(s); }, Arg("str"));
|
|
64
68
|
|
|
65
69
|
rb_define_alias(klass_, "to_s", "str");
|
|
66
70
|
}
|
data/rice/stl/pair.ipp
CHANGED
|
@@ -24,8 +24,12 @@ namespace Rice
|
|
|
24
24
|
private:
|
|
25
25
|
void define_constructors()
|
|
26
26
|
{
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if constexpr (std::is_default_constructible_v<T>)
|
|
28
|
+
{
|
|
29
|
+
klass_.define_constructor(Constructor<T>());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
klass_.define_constructor(Constructor<T, First_Parameter_T, Second_Parameter_T>(), Arg("x").keepAlive(), Arg("y").keepAlive());
|
|
29
33
|
|
|
30
34
|
if constexpr (std::is_copy_constructible_v<First_T> && std::is_copy_constructible_v<Second_T>)
|
|
31
35
|
{
|
data/rice/stl/shared_ptr.ipp
CHANGED
|
@@ -32,7 +32,11 @@ namespace Rice
|
|
|
32
32
|
|
|
33
33
|
if constexpr (detail::is_complete_v<T> && !std::is_void_v<T>)
|
|
34
34
|
{
|
|
35
|
-
|
|
35
|
+
// is_abstract_v requires a complete type, so it must be nested inside the is_complete_v check
|
|
36
|
+
if constexpr (!std::is_abstract_v<T>)
|
|
37
|
+
{
|
|
38
|
+
result.define_constructor(Constructor<SharedPtr_T, typename SharedPtr_T::element_type*>(), Arg("value").takeOwnership());
|
|
39
|
+
}
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
// Forward methods to wrapped T
|
|
@@ -87,7 +91,7 @@ namespace Rice::detail
|
|
|
87
91
|
}
|
|
88
92
|
else if (rb_typeddata_inherited_p(this->inner_rb_data_type_, requestedType))
|
|
89
93
|
{
|
|
90
|
-
return this->data_.get();
|
|
94
|
+
return (void*)this->data_.get();
|
|
91
95
|
}
|
|
92
96
|
else
|
|
93
97
|
{
|
data/rice/stl/unique_ptr.ipp
CHANGED
data/rice/stl/unordered_map.ipp
CHANGED
|
@@ -156,7 +156,7 @@ namespace Rice
|
|
|
156
156
|
}, Arg("key"))
|
|
157
157
|
.define_method("[]=", [](T& unordered_map, Key_T key, Mapped_Parameter_T value) -> Mapped_T
|
|
158
158
|
{
|
|
159
|
-
unordered_map
|
|
159
|
+
unordered_map.insert_or_assign(key, value);
|
|
160
160
|
return value;
|
|
161
161
|
}, Arg("key").keepAlive(), Arg("value").keepAlive());
|
|
162
162
|
|
|
@@ -278,7 +278,7 @@ namespace Rice
|
|
|
278
278
|
// exceptions propogate back to Ruby
|
|
279
279
|
return cpp_protect([&]
|
|
280
280
|
{
|
|
281
|
-
result->
|
|
281
|
+
result->insert_or_assign(From_Ruby<T>().convert(key), From_Ruby<U>().convert(value));
|
|
282
282
|
return ST_CONTINUE;
|
|
283
283
|
});
|
|
284
284
|
}
|
|
@@ -102,6 +102,27 @@ namespace Rice::detail
|
|
|
102
102
|
{
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
// ref-qualified member Functions on C++ classes (C++20 uses these for std library types)
|
|
106
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
107
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) &> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
108
|
+
{
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
112
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) const&> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
113
|
+
{
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
117
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) & noexcept> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
118
|
+
{
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
template<typename Return_T, typename Class_T, typename...Parameter_Ts>
|
|
122
|
+
struct function_traits<Return_T(Class_T::*)(Parameter_Ts...) const& noexcept> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
|
|
123
|
+
{
|
|
124
|
+
};
|
|
125
|
+
|
|
105
126
|
/*// Functors and lambdas
|
|
106
127
|
template<class Function_T>
|
|
107
128
|
struct function_traits<Function_T&> : public function_traits<Function_T>
|
data/test/test_Attribute.cpp
CHANGED
|
@@ -590,4 +590,35 @@ TESTCASE(KeepAlive)
|
|
|
590
590
|
// This should work because keepAlive prevents MyClass2 from being GC'd
|
|
591
591
|
ASSERT_NOT_EQUAL(nullptr, dataStruct->myClass2);
|
|
592
592
|
ASSERT_EQUAL(43, dataStruct->myClass2->value);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
namespace
|
|
596
|
+
{
|
|
597
|
+
struct FuncPtrStruct
|
|
598
|
+
{
|
|
599
|
+
int (*callback)(int) = nullptr;
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
TESTCASE(function_pointer_attribute)
|
|
604
|
+
{
|
|
605
|
+
Module m = define_module("Testing");
|
|
606
|
+
|
|
607
|
+
Class c = define_class<FuncPtrStruct>("FuncPtrStruct")
|
|
608
|
+
.define_constructor(Constructor<FuncPtrStruct>())
|
|
609
|
+
.define_attr("callback", &FuncPtrStruct::callback);
|
|
610
|
+
|
|
611
|
+
Object o = c.call("new");
|
|
612
|
+
|
|
613
|
+
// Set the callback via Ruby using a lambda
|
|
614
|
+
std::string code = R"(struct = FuncPtrStruct.new
|
|
615
|
+
struct.callback = lambda { |x| x * 2 }
|
|
616
|
+
struct)";
|
|
617
|
+
|
|
618
|
+
Data_Object<FuncPtrStruct> funcPtrStruct = m.module_eval(code);
|
|
619
|
+
|
|
620
|
+
// Invoke the callback from C++ to verify it works
|
|
621
|
+
ASSERT_NOT_EQUAL(nullptr, funcPtrStruct->callback);
|
|
622
|
+
int result = funcPtrStruct->callback(5);
|
|
623
|
+
ASSERT_EQUAL(10, result);
|
|
593
624
|
}
|
data/test/test_Callback.cpp
CHANGED
|
@@ -55,10 +55,8 @@ TESTCASE(LambdaCallBack)
|
|
|
55
55
|
ASSERT((globalCallback != nullptr));
|
|
56
56
|
|
|
57
57
|
int ref = 4;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
char* result = triggerCallback(1, 2, true, "hello", ref);
|
|
61
|
-
#pragma GCC diagnostic pop
|
|
58
|
+
char hello[] = "hello";
|
|
59
|
+
char* result = triggerCallback(1, 2, true, hello, ref);
|
|
62
60
|
ASSERT_EQUAL("1 - 2.0 - true - hello - 4", result);
|
|
63
61
|
}
|
|
64
62
|
|
data/test/test_From_Ruby.cpp
CHANGED
|
@@ -615,3 +615,17 @@ TESTCASE(void_pointer_array)
|
|
|
615
615
|
Object result = m.module_eval(code);
|
|
616
616
|
ASSERT_EQUAL("[4, 3, 2, 1]", detail::From_Ruby<std::string>().convert(result.value()));
|
|
617
617
|
}
|
|
618
|
+
|
|
619
|
+
TESTCASE(nullptr_t)
|
|
620
|
+
{
|
|
621
|
+
detail::From_Ruby<std::nullptr_t> fromRuby;
|
|
622
|
+
|
|
623
|
+
std::nullptr_t result = fromRuby.convert(Qnil);
|
|
624
|
+
ASSERT_EQUAL(nullptr, result);
|
|
625
|
+
|
|
626
|
+
ASSERT_EXCEPTION_CHECK(
|
|
627
|
+
Exception,
|
|
628
|
+
fromRuby.convert(rb_str_new2("not nil")),
|
|
629
|
+
ASSERT_EQUAL("wrong argument type String (expected nil)", ex.what())
|
|
630
|
+
);
|
|
631
|
+
}
|
data/test/test_Stl_Map.cpp
CHANGED
|
@@ -365,6 +365,62 @@ TESTCASE(NotPrintable)
|
|
|
365
365
|
ASSERT_EQUAL("[Not printable]", detail::From_Ruby<std::string>().convert(result));
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
+
namespace
|
|
369
|
+
{
|
|
370
|
+
// Type with no default constructor - verifies insert_or_assign works in []=
|
|
371
|
+
class NoDefaultMap
|
|
372
|
+
{
|
|
373
|
+
public:
|
|
374
|
+
NoDefaultMap(int value) : value_(value) {}
|
|
375
|
+
int value() const { return value_; }
|
|
376
|
+
|
|
377
|
+
bool operator==(const NoDefaultMap& other) const
|
|
378
|
+
{
|
|
379
|
+
return this->value_ == other.value_;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private:
|
|
383
|
+
int value_;
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
TESTCASE(NoDefaultConstructor)
|
|
388
|
+
{
|
|
389
|
+
define_class<NoDefaultMap>("NoDefaultMap").
|
|
390
|
+
define_constructor(Constructor<NoDefaultMap, int>(), Arg("value")).
|
|
391
|
+
define_method("value", &NoDefaultMap::value);
|
|
392
|
+
|
|
393
|
+
Class c = define_map<std::string, NoDefaultMap>("NoDefaultMap_Map");
|
|
394
|
+
|
|
395
|
+
Object map = c.call("new");
|
|
396
|
+
|
|
397
|
+
// Test []= (insert_or_assign) with non-default-constructible value
|
|
398
|
+
map.call("[]=", "one", NoDefaultMap(1));
|
|
399
|
+
map.call("[]=", "two", NoDefaultMap(2));
|
|
400
|
+
|
|
401
|
+
Object result = map.call("size");
|
|
402
|
+
ASSERT_EQUAL(2, detail::From_Ruby<int32_t>().convert(result));
|
|
403
|
+
|
|
404
|
+
// Test [] access
|
|
405
|
+
result = map.call("[]", "one");
|
|
406
|
+
ASSERT_EQUAL(1, detail::From_Ruby<int>().convert(result.call("value")));
|
|
407
|
+
|
|
408
|
+
// Test update existing key
|
|
409
|
+
map.call("[]=", "one", NoDefaultMap(10));
|
|
410
|
+
result = map.call("[]", "one");
|
|
411
|
+
ASSERT_EQUAL(10, detail::From_Ruby<int>().convert(result.call("value")));
|
|
412
|
+
|
|
413
|
+
result = map.call("size");
|
|
414
|
+
ASSERT_EQUAL(2, detail::From_Ruby<int32_t>().convert(result));
|
|
415
|
+
|
|
416
|
+
// Test delete
|
|
417
|
+
result = map.call("delete", "two");
|
|
418
|
+
ASSERT_EQUAL(2, detail::From_Ruby<int>().convert(result.call("value")));
|
|
419
|
+
|
|
420
|
+
result = map.call("size");
|
|
421
|
+
ASSERT_EQUAL(1, detail::From_Ruby<int32_t>().convert(result));
|
|
422
|
+
}
|
|
423
|
+
|
|
368
424
|
namespace
|
|
369
425
|
{
|
|
370
426
|
class Comparable
|
data/test/test_Stl_Pair.cpp
CHANGED
|
@@ -144,6 +144,38 @@ TESTCASE(AutoRegister)
|
|
|
144
144
|
ASSERT(result.is_instance_of(pair.class_of()));
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
namespace
|
|
148
|
+
{
|
|
149
|
+
// Type with no default constructor - like cv::detail::tracking::tbm::Track
|
|
150
|
+
class NoDefault
|
|
151
|
+
{
|
|
152
|
+
public:
|
|
153
|
+
NoDefault(int value) : value_(value) {}
|
|
154
|
+
int value() const { return value_; }
|
|
155
|
+
private:
|
|
156
|
+
int value_;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
TESTCASE(PairNoDefaultConstructor)
|
|
161
|
+
{
|
|
162
|
+
define_class<NoDefault>("NoDefault").
|
|
163
|
+
define_constructor(Constructor<NoDefault, int>(), Arg("value")).
|
|
164
|
+
define_method("value", &NoDefault::value);
|
|
165
|
+
|
|
166
|
+
// This should compile and work even though NoDefault has no default constructor.
|
|
167
|
+
// The pair's default constructor should be skipped, but the two-argument constructor should work.
|
|
168
|
+
Class c = define_pair<const int, NoDefault>("IntNoDefaultPair");
|
|
169
|
+
|
|
170
|
+
Object pair = c.call("new", 42, NoDefault(7));
|
|
171
|
+
|
|
172
|
+
Object result = pair.call("first");
|
|
173
|
+
ASSERT_EQUAL(42, detail::From_Ruby<int>().convert(result));
|
|
174
|
+
|
|
175
|
+
result = pair.call("second");
|
|
176
|
+
ASSERT_EQUAL(7, detail::From_Ruby<int>().convert(result.call("value")));
|
|
177
|
+
}
|
|
178
|
+
|
|
147
179
|
namespace
|
|
148
180
|
{
|
|
149
181
|
struct SomeStruct
|
data/test/test_Stl_SharedPtr.cpp
CHANGED
|
@@ -609,6 +609,99 @@ TESTCASE(InheritedForwarding)
|
|
|
609
609
|
ASSERT_EQUAL(456, detail::From_Ruby<int>().convert(result));
|
|
610
610
|
}
|
|
611
611
|
|
|
612
|
+
// --- shared_ptr<const T> tests ---
|
|
613
|
+
// When shared_ptr wraps a const T, the Wrapper::get() method must handle
|
|
614
|
+
// the const-to-void* conversion. Without a cast, data_.get() returns
|
|
615
|
+
// const T* which cannot implicitly convert to void*.
|
|
616
|
+
namespace
|
|
617
|
+
{
|
|
618
|
+
class ConstTarget
|
|
619
|
+
{
|
|
620
|
+
public:
|
|
621
|
+
int value;
|
|
622
|
+
|
|
623
|
+
ConstTarget() : value(0) {}
|
|
624
|
+
ConstTarget(int v) : value(v) {}
|
|
625
|
+
|
|
626
|
+
int getValue() const { return value; }
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
std::shared_ptr<const ConstTarget> createConstTarget(int v)
|
|
630
|
+
{
|
|
631
|
+
return std::make_shared<const ConstTarget>(v);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
int extractConstTargetValue(std::shared_ptr<const ConstTarget> ptr)
|
|
635
|
+
{
|
|
636
|
+
return ptr->getValue();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
TESTCASE(SharedPtrConstT)
|
|
641
|
+
{
|
|
642
|
+
define_class<ConstTarget>("ConstTarget").
|
|
643
|
+
define_constructor(Constructor<ConstTarget, int>(),
|
|
644
|
+
Arg("v")).
|
|
645
|
+
define_method("get_value", &ConstTarget::getValue);
|
|
646
|
+
|
|
647
|
+
Module m = define_module("SharedPtrConstTest").
|
|
648
|
+
define_module_function("create_const_target", &createConstTarget).
|
|
649
|
+
define_module_function("extract_const_target_value", &extractConstTargetValue);
|
|
650
|
+
|
|
651
|
+
// Test that shared_ptr<const T> can be unwrapped to access the inner T
|
|
652
|
+
std::string code = R"(ptr = create_const_target(42)
|
|
653
|
+
extract_const_target_value(ptr))";
|
|
654
|
+
|
|
655
|
+
Object result = m.instance_eval(code);
|
|
656
|
+
ASSERT_EQUAL(42, detail::From_Ruby<int>().convert(result.value()));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Abstract class with non-virtual destructor - like cv::cudacodec::NVSurfaceToColorConverter
|
|
660
|
+
namespace
|
|
661
|
+
{
|
|
662
|
+
class AbstractClass
|
|
663
|
+
{
|
|
664
|
+
public:
|
|
665
|
+
virtual bool compute(int value) const = 0;
|
|
666
|
+
~AbstractClass() {} // non-virtual destructor
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
class ConcreteImpl : public AbstractClass
|
|
670
|
+
{
|
|
671
|
+
public:
|
|
672
|
+
bool compute(int value) const override { return value > 0; }
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
std::shared_ptr<AbstractClass> createAbstract()
|
|
676
|
+
{
|
|
677
|
+
return std::make_shared<ConcreteImpl>();
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
bool useAbstract(std::shared_ptr<AbstractClass> ptr, int value)
|
|
681
|
+
{
|
|
682
|
+
return ptr->compute(value);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
TESTCASE(SharedPtrAbstractT)
|
|
687
|
+
{
|
|
688
|
+
// shared_ptr<T> where T is abstract should NOT define a constructor taking T*,
|
|
689
|
+
// because deleting through a non-virtual destructor on an abstract class is UB.
|
|
690
|
+
define_class<AbstractClass>("AbstractClass").
|
|
691
|
+
define_method("compute", &AbstractClass::compute,
|
|
692
|
+
Arg("value"));
|
|
693
|
+
|
|
694
|
+
Module m = define_module("SharedPtrAbstractTest").
|
|
695
|
+
define_module_function("create_abstract", &createAbstract).
|
|
696
|
+
define_module_function("use_abstract", &useAbstract);
|
|
697
|
+
|
|
698
|
+
std::string code = R"(ptr = create_abstract
|
|
699
|
+
use_abstract(ptr, 5))";
|
|
700
|
+
|
|
701
|
+
Object result = m.instance_eval(code);
|
|
702
|
+
ASSERT_EQUAL(Qtrue, result.value());
|
|
703
|
+
}
|
|
704
|
+
|
|
612
705
|
// Forward declaration only - IncompleteClass is never defined
|
|
613
706
|
class IncompleteClass;
|
|
614
707
|
|
data/test/test_Stl_UniquePtr.cpp
CHANGED
|
@@ -252,4 +252,51 @@ TESTCASE(Release)
|
|
|
252
252
|
Exception,
|
|
253
253
|
m.module_eval(code),
|
|
254
254
|
ASSERT(std::string(ex.what()).find("undefined method") == 0));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// --- unique_ptr<const T> tests ---
|
|
258
|
+
// When unique_ptr wraps a const T, the Wrapper::get() method must handle
|
|
259
|
+
// the const-to-void* conversion. Without a cast, data_.get() returns
|
|
260
|
+
// const T* which cannot implicitly convert to void*.
|
|
261
|
+
namespace
|
|
262
|
+
{
|
|
263
|
+
class ConstTarget
|
|
264
|
+
{
|
|
265
|
+
public:
|
|
266
|
+
int value;
|
|
267
|
+
|
|
268
|
+
ConstTarget() : value(0) {}
|
|
269
|
+
ConstTarget(int v) : value(v) {}
|
|
270
|
+
|
|
271
|
+
int getValue() const { return value; }
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
std::unique_ptr<const ConstTarget> createConstTarget(int v)
|
|
275
|
+
{
|
|
276
|
+
return std::make_unique<const ConstTarget>(v);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
int extractConstTargetValue(const std::unique_ptr<const ConstTarget>& ptr)
|
|
280
|
+
{
|
|
281
|
+
return ptr->getValue();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
TESTCASE(UniquePtrConstT)
|
|
286
|
+
{
|
|
287
|
+
define_class<ConstTarget>("ConstTarget").
|
|
288
|
+
define_constructor(Constructor<ConstTarget, int>(),
|
|
289
|
+
Arg("v")).
|
|
290
|
+
define_method("get_value", &ConstTarget::getValue);
|
|
291
|
+
|
|
292
|
+
Module m = define_module("UniquePtrConstTest").
|
|
293
|
+
define_module_function("create_const_target", &createConstTarget).
|
|
294
|
+
define_module_function("extract_const_target_value", &extractConstTargetValue);
|
|
295
|
+
|
|
296
|
+
// Test that unique_ptr<const T> can be unwrapped to access the inner T
|
|
297
|
+
std::string code = R"(ptr = create_const_target(42)
|
|
298
|
+
extract_const_target_value(ptr))";
|
|
299
|
+
|
|
300
|
+
Object result = m.instance_eval(code);
|
|
301
|
+
ASSERT_EQUAL(42, detail::From_Ruby<int>().convert(result.value()));
|
|
255
302
|
}
|
|
@@ -357,6 +357,62 @@ TESTCASE(NotPrintable)
|
|
|
357
357
|
ASSERT_EQUAL("[Not printable]", detail::From_Ruby<std::string>().convert(result));
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
namespace
|
|
361
|
+
{
|
|
362
|
+
// Type with no default constructor - verifies insert_or_assign works in []=
|
|
363
|
+
class NoDefaultUnorderedMap
|
|
364
|
+
{
|
|
365
|
+
public:
|
|
366
|
+
NoDefaultUnorderedMap(int value) : value_(value) {}
|
|
367
|
+
int value() const { return value_; }
|
|
368
|
+
|
|
369
|
+
bool operator==(const NoDefaultUnorderedMap& other) const
|
|
370
|
+
{
|
|
371
|
+
return this->value_ == other.value_;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private:
|
|
375
|
+
int value_;
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
TESTCASE(NoDefaultConstructor)
|
|
380
|
+
{
|
|
381
|
+
define_class<NoDefaultUnorderedMap>("NoDefaultUnorderedMap").
|
|
382
|
+
define_constructor(Constructor<NoDefaultUnorderedMap, int>(), Arg("value")).
|
|
383
|
+
define_method("value", &NoDefaultUnorderedMap::value);
|
|
384
|
+
|
|
385
|
+
Class c = define_unordered_map<std::string, NoDefaultUnorderedMap>("NoDefaultUnorderedMap_Map");
|
|
386
|
+
|
|
387
|
+
Object map = c.call("new");
|
|
388
|
+
|
|
389
|
+
// Test []= (insert_or_assign) with non-default-constructible value
|
|
390
|
+
map.call("[]=", "one", NoDefaultUnorderedMap(1));
|
|
391
|
+
map.call("[]=", "two", NoDefaultUnorderedMap(2));
|
|
392
|
+
|
|
393
|
+
Object result = map.call("size");
|
|
394
|
+
ASSERT_EQUAL(2, detail::From_Ruby<int32_t>().convert(result));
|
|
395
|
+
|
|
396
|
+
// Test [] access
|
|
397
|
+
result = map.call("[]", "one");
|
|
398
|
+
ASSERT_EQUAL(1, detail::From_Ruby<int>().convert(result.call("value")));
|
|
399
|
+
|
|
400
|
+
// Test update existing key
|
|
401
|
+
map.call("[]=", "one", NoDefaultUnorderedMap(10));
|
|
402
|
+
result = map.call("[]", "one");
|
|
403
|
+
ASSERT_EQUAL(10, detail::From_Ruby<int>().convert(result.call("value")));
|
|
404
|
+
|
|
405
|
+
result = map.call("size");
|
|
406
|
+
ASSERT_EQUAL(2, detail::From_Ruby<int32_t>().convert(result));
|
|
407
|
+
|
|
408
|
+
// Test delete
|
|
409
|
+
result = map.call("delete", "two");
|
|
410
|
+
ASSERT_EQUAL(2, detail::From_Ruby<int>().convert(result.call("value")));
|
|
411
|
+
|
|
412
|
+
result = map.call("size");
|
|
413
|
+
ASSERT_EQUAL(1, detail::From_Ruby<int32_t>().convert(result));
|
|
414
|
+
}
|
|
415
|
+
|
|
360
416
|
namespace
|
|
361
417
|
{
|
|
362
418
|
class Comparable
|