rice 4.9.0 → 4.9.1

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/CMakePresets.json +77 -50
  4. data/FindRuby.cmake +1 -1
  5. data/include/rice/rice.hpp +114 -81
  6. data/include/rice/stl.hpp +157 -95
  7. data/lib/rice/doc/config.rb +57 -57
  8. data/lib/rice/doc/cpp_reference.rb +158 -158
  9. data/lib/rice/doc/doxygen.rb +289 -289
  10. data/lib/rice/doc/mkdocs.rb +332 -332
  11. data/lib/rice/doc/rice.rb +48 -48
  12. data/lib/rice/doc/ruby.rb +26 -26
  13. data/lib/rice/native.rb +15 -15
  14. data/lib/rice/native_registry.rb +12 -12
  15. data/lib/rice/parameter.rb +5 -5
  16. data/lib/rice/rbs.rb +71 -71
  17. data/lib/rice/version.rb +1 -1
  18. data/lib/rubygems/builder.rb +9 -9
  19. data/lib/rubygems_plugin.rb +8 -8
  20. data/rice/Data_Type.ipp +1 -1
  21. data/rice/detail/Native.ipp +2 -4
  22. data/rice/detail/NativeAttributeGet.ipp +1 -1
  23. data/rice/detail/NativeAttributeSet.hpp +5 -3
  24. data/rice/detail/NativeAttributeSet.ipp +41 -33
  25. data/rice/detail/NativeMethod.ipp +25 -22
  26. data/rice/detail/NativeRegistry.hpp +3 -2
  27. data/rice/detail/NativeRegistry.ipp +13 -9
  28. data/rice/detail/Parameter.ipp +3 -4
  29. data/rice/detail/Wrapper.hpp +5 -1
  30. data/rice/detail/Wrapper.ipp +15 -1
  31. data/rice/stl/exception.ipp +1 -1
  32. data/rice/stl/filesystem.ipp +1 -1
  33. data/rice/stl/map.ipp +13 -11
  34. data/rice/stl/multimap.ipp +13 -11
  35. data/rice/stl/pair.ipp +14 -8
  36. data/rice/stl/set.ipp +16 -16
  37. data/rice/stl/shared_ptr.ipp +15 -1
  38. data/rice/stl/type_index.ipp +1 -1
  39. data/rice/stl/unique_ptr.ipp +2 -2
  40. data/rice/stl/unordered_map.ipp +14 -12
  41. data/rice/stl/vector.ipp +67 -31
  42. data/test/test_Attribute.cpp +72 -0
  43. data/test/test_Callback.cpp +3 -0
  44. data/test/test_Stl_Map.cpp +46 -0
  45. data/test/test_Stl_Multimap.cpp +46 -0
  46. data/test/test_Stl_Set.cpp +34 -0
  47. data/test/test_Stl_Unordered_Map.cpp +46 -0
  48. data/test/test_Stl_Variant.cpp +10 -14
  49. data/test/test_Stl_Vector.cpp +140 -13
  50. data/test/test_Tracking.cpp +3 -0
  51. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60ee7533ef1a2e8fae4a992ef2d1dc2f3dd1cd4ab260217058e9de77ead8f80b
4
- data.tar.gz: 83393a7a4238d817da51eccc5392d318bb2dab00e57d9ce688867a748a50f7a5
3
+ metadata.gz: 6482695048d122782acef1d7ee310c47c85cff57eb39c8740db46013a4af0e87
4
+ data.tar.gz: a4b3c772ac9d5baff75ad80dbc404dde8a3fc67c821836203dfdcef948877091
5
5
  SHA512:
6
- metadata.gz: 26c796a5fcac6d816f9cddda0944be07a16cc93a0264221a351f4612061ed3b0e61dd4ab99eae0470ebc62e483f762820116024f6814f9f0023adccc466d66cf
7
- data.tar.gz: aacb86cb9d086100650839ac2cf2cf49d60dcf4604f3539be24e0922c3f3ca3a8f06dfb0511e1e1b3281e9f475cf621218ac165b4c155e5d6e4e2083b3444823
6
+ metadata.gz: 553618d580f7734d23a0c0278636fe725f48cacd68572fa9d6cee828fed68b2af84cda1fecdad4bb65633ada077bf4b34a703e2bd25173bd4f3ae0de91c54c26
7
+ data.tar.gz: '0368ac5a1d1d957ab8a94dcec8a25c6e5cc539930848db9f78077c6006b86a6d81299af839ab1daed52fab9ce743496cebcff6e3698483961c0fc6cc2bef82b4'
data/CHANGELOG.md CHANGED
@@ -1,4 +1,20 @@
1
- # Changelog
1
+ # Changelog
2
+
3
+ ## 4.9.1 (2026-01-04)
4
+ This release focuses on improving memory management for STL containers and attribute setters.
5
+
6
+ Enhancements:
7
+ * Support `takeOwnership` and `keepAlive` when setting attributes via `Arg("value").takeOwnership()` and `Arg("value").keepAlive()`
8
+ * Add `Arg` parameter names to all STL container methods for keyword argument support
9
+ * Add `keepAlive` support for STL container operations (vector push/insert, set insert, map/multimap store)
10
+ * Add `keepAlive` for map/unordered_map/multimap keys to prevent GC of pointer-type keys
11
+
12
+ Bug Fixes:
13
+ * Fix error when multiple overloaded methods take different types of vectors
14
+ * Fix type unknown errors when using `std::shared_ptr` with g++
15
+ * Fix CMake `_Ruby_DLEXT` variable type (string, not path)
16
+ * Fix crash caused by static variable in keepAlive implementation
17
+ * Fix incorrect attribute overloading behavior
2
18
 
3
19
  ## 4.9.0 (2026-01-01)
4
20
  This release revamps smart pointer support for `std::shared_ptr` and `std::unique_ptr`.
data/CMakePresets.json CHANGED
@@ -6,7 +6,11 @@
6
6
  "hidden": true,
7
7
  "generator": "Ninja",
8
8
  "binaryDir": "${sourceDir}/build/${presetName}",
9
- "installDir": "${sourceDir}/install/${presetName}"
9
+ "installDir": "${sourceDir}/install/${presetName}",
10
+ "cacheVariables": {
11
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
12
+ "CMAKE_CXX_FLAGS": "-Wall -ftemplate-backtrace-limit=0 -fvisibility=hidden -fvisibility-inlines-hidden"
13
+ }
10
14
  },
11
15
  {
12
16
  "name": "linux-debug",
@@ -14,7 +18,7 @@
14
18
  "displayName": "Linux Debug",
15
19
  "cacheVariables": {
16
20
  "CMAKE_BUILD_TYPE": "Debug",
17
- "CMAKE_CXX_FLAGS": "-Wall -ftemplate-backtrace-limit=0 -Og"
21
+ "CMAKE_CXX_FLAGS_DEBUG": "-ggdb3 -O0 -fno-omit-frame-pointer -fno-inline -fno-optimize-sibling-calls"
18
22
  },
19
23
  "condition": {
20
24
  "type": "equals",
@@ -28,7 +32,9 @@
28
32
  "displayName": "Linux Release",
29
33
  "cacheVariables": {
30
34
  "CMAKE_BUILD_TYPE": "Release",
31
- "CMAKE_CXX_FLAGS": "-Wall -ftemplate-backtrace-limit=0 -O3 -DNDEBUG"
35
+ "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG",
36
+ "CMAKE_INTERPROCEDURAL_OPTIMIZATION": "ON",
37
+ "CMAKE_SHARED_LINKER_FLAGS_RELEASE": "-Wl,--exclude-libs,ALL -Wl,--strip-all"
32
38
  },
33
39
  "condition": {
34
40
  "type": "equals",
@@ -42,7 +48,8 @@
42
48
  "displayName": "macOS Debug",
43
49
  "cacheVariables": {
44
50
  "CMAKE_BUILD_TYPE": "Debug",
45
- "CMAKE_CXX_FLAGS": "-Wall -Wno-unused-private-field -ftemplate-backtrace-limit=0 -Og"
51
+ "CMAKE_CXX_FLAGS": "-Wall -Wno-unused-private-field -ftemplate-backtrace-limit=0 -fvisibility=hidden -fvisibility-inlines-hidden",
52
+ "CMAKE_CXX_FLAGS_DEBUG": "-g3 -Og -fno-omit-frame-pointer -fno-inline -gsplit-dwarf"
46
53
  },
47
54
  "condition": {
48
55
  "type": "equals",
@@ -56,7 +63,10 @@
56
63
  "displayName": "macOS Release",
57
64
  "cacheVariables": {
58
65
  "CMAKE_BUILD_TYPE": "Release",
59
- "CMAKE_CXX_FLAGS": "-Wall -Wno-unused-private-field -ftemplate-backtrace-limit=0 -O3 -DNDEBUG"
66
+ "CMAKE_CXX_FLAGS": "-Wall -Wno-unused-private-field -ftemplate-backtrace-limit=0 -fvisibility=hidden -fvisibility-inlines-hidden",
67
+ "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG",
68
+ "CMAKE_INTERPROCEDURAL_OPTIMIZATION": "ON",
69
+ "CMAKE_SHARED_LINKER_FLAGS_RELEASE": "-Wl,-dead_strip -Wl,-x"
60
70
  },
61
71
  "condition": {
62
72
  "type": "equals",
@@ -65,99 +75,116 @@
65
75
  }
66
76
  },
67
77
  {
68
- "name": "windows",
69
- "hidden": true,
78
+ "name": "mingw-debug",
79
+ "inherits": "base",
80
+ "displayName": "Mingw x64 Debug",
81
+ "cacheVariables": {
82
+ "CMAKE_BUILD_TYPE": "Debug",
83
+ "CMAKE_CXX_COMPILER": "g++.exe",
84
+ "CMAKE_CXX_FLAGS": "-Wall -ftemplate-backtrace-limit=0 -Wa,-mbig-obj -fvisibility=hidden -fvisibility-inlines-hidden",
85
+ "CMAKE_CXX_FLAGS_DEBUG": "-g3 -Og -fno-omit-frame-pointer -fno-inline -gsplit-dwarf"
86
+ },
87
+ "condition": {
88
+ "type": "equals",
89
+ "lhs": "${hostSystemName}",
90
+ "rhs": "Windows"
91
+ }
92
+ },
93
+ {
94
+ "name": "mingw-release",
70
95
  "inherits": "base",
96
+ "displayName": "Mingw x64 Release",
97
+ "cacheVariables": {
98
+ "CMAKE_BUILD_TYPE": "Release",
99
+ "CMAKE_CXX_COMPILER": "g++.exe",
100
+ "CMAKE_CXX_FLAGS": "-Wall -ftemplate-backtrace-limit=0 -Wa,-mbig-obj -fvisibility=hidden -fvisibility-inlines-hidden",
101
+ "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG",
102
+ "CMAKE_SHARED_LINKER_FLAGS_RELEASE": "-Wl,--exclude-all-symbols"
103
+ },
71
104
  "condition": {
72
105
  "type": "equals",
73
106
  "lhs": "${hostSystemName}",
74
107
  "rhs": "Windows"
108
+ }
109
+ },
110
+ {
111
+ "name": "windows-base",
112
+ "hidden": true,
113
+ "generator": "Ninja",
114
+ "binaryDir": "${sourceDir}/build/${presetName}",
115
+ "installDir": "${sourceDir}/install/${presetName}",
116
+ "toolchainFile": "$env{VCPKG_ROOT}\\scripts\\buildsystems\\vcpkg.cmake",
117
+ "cacheVariables": {
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"
75
120
  },
76
- "toolchainFile": "$env{VCPKG_ROOT}\\scripts\\buildsystems\\vcpkg.cmake"
121
+ "condition": {
122
+ "type": "equals",
123
+ "lhs": "${hostSystemName}",
124
+ "rhs": "Windows"
125
+ }
77
126
  },
78
127
  {
79
128
  "name": "msvc-debug",
80
- "inherits": "windows",
129
+ "inherits": "windows-base",
81
130
  "displayName": "MSVC x64 Debug",
82
131
  "cacheVariables": {
83
132
  "CMAKE_BUILD_TYPE": "Debug",
84
133
  "CMAKE_CXX_COMPILER": "cl.exe",
85
- "CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE",
86
- "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "EditAndContinue"
134
+ "CMAKE_CXX_FLAGS_DEBUG": "/Od",
135
+ "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "ProgramDatabase"
87
136
  }
88
137
  },
89
138
  {
90
139
  "name": "msvc-release",
91
- "inherits": "windows",
140
+ "inherits": "windows-base",
92
141
  "displayName": "MSVC x64 Release",
93
142
  "cacheVariables": {
94
143
  "CMAKE_BUILD_TYPE": "Release",
95
144
  "CMAKE_CXX_COMPILER": "cl.exe",
96
- "CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /O2 /DNDEBUG /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE",
145
+ "CMAKE_CXX_FLAGS_RELEASE": "/O2 /DNDEBUG",
146
+ "CMAKE_INTERPROCEDURAL_OPTIMIZATION": "ON",
97
147
  "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "ProgramDatabase"
98
148
  }
99
149
  },
150
+
100
151
  {
101
152
  "name": "clang-windows-debug",
102
- "inherits": "windows",
153
+ "inherits": "windows-base",
103
154
  "displayName": "Clang Windows Debug",
104
155
  "cacheVariables": {
105
156
  "CMAKE_BUILD_TYPE": "Debug",
106
157
  "CMAKE_CXX_COMPILER": "clang-cl.exe",
107
- "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 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /clang:-Wno-unused-private-field",
159
+ "CMAKE_CXX_FLAGS_DEBUG": "/Od /Zi"
108
160
  }
109
161
  },
110
162
  {
111
163
  "name": "clang-windows-release",
112
- "inherits": "windows",
164
+ "inherits": "windows-base",
113
165
  "displayName": "Clang Windows Release",
114
166
  "cacheVariables": {
115
167
  "CMAKE_BUILD_TYPE": "Release",
116
168
  "CMAKE_CXX_COMPILER": "clang-cl.exe",
117
- "CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /O2 /DNDEBUG /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /clang:-Wno-unused-private-field"
118
- }
119
- },
120
- {
121
- "name": "mingw-debug",
122
- "inherits": "base",
123
- "displayName": "Mingw x64 Debug",
124
- "cacheVariables": {
125
- "CMAKE_BUILD_TYPE": "Debug",
126
- "CMAKE_CXX_COMPILER": "g++.exe",
127
- "CMAKE_CXX_FLAGS": "-Wall -ftemplate-backtrace-limit=0 -Wa,-mbig-obj -Og"
128
- },
129
- "condition": {
130
- "type": "equals",
131
- "lhs": "${hostSystemName}",
132
- "rhs": "Windows"
133
- }
134
- },
135
- {
136
- "name": "mingw-release",
137
- "inherits": "base",
138
- "displayName": "Mingw x64 Release",
139
- "cacheVariables": {
140
- "CMAKE_BUILD_TYPE": "Release",
141
- "CMAKE_CXX_COMPILER": "g++.exe",
142
- "CMAKE_CXX_FLAGS": "-Wall -ftemplate-backtrace-limit=0 -Wa,-mbig-obj -O3 -DNDEBUG"
143
- },
144
- "condition": {
145
- "type": "equals",
146
- "lhs": "${hostSystemName}",
147
- "rhs": "Windows"
169
+ "CMAKE_CXX_FLAGS": "/EHs /W4 /bigobj /utf-8 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /clang:-Wno-unused-private-field",
170
+ "CMAKE_CXX_FLAGS_RELEASE": "/O2 /DNDEBUG",
171
+ "CMAKE_INTERPROCEDURAL_OPTIMIZATION": "ON"
148
172
  }
149
173
  }
150
174
  ],
175
+
151
176
  "buildPresets": [
152
177
  {
153
178
  "name": "linux-debug",
154
179
  "displayName": "Build Linux Debug",
155
- "configurePreset": "linux-debug"
180
+ "configurePreset": "linux-debug",
181
+ "jobs": 6
156
182
  },
157
183
  {
158
184
  "name": "linux-release",
159
185
  "displayName": "Build Linux Release",
160
- "configurePreset": "linux-release"
186
+ "configurePreset": "linux-release",
187
+ "jobs": 6
161
188
  },
162
189
  {
163
190
  "name": "macos-debug",
@@ -200,4 +227,4 @@
200
227
  "configurePreset": "mingw-release"
201
228
  }
202
229
  ]
203
- }
230
+ }
data/FindRuby.cmake CHANGED
@@ -428,7 +428,7 @@ if (Ruby_EXECUTABLE AND NOT Ruby_EXECUTABLE STREQUAL "${_Ruby_EXECUTABLE_LAST_QU
428
428
  set(_Ruby_POSSIBLE_LIB_DIR ${_Ruby_POSSIBLE_LIB_DIR} CACHE INTERNAL "The Ruby lib dir" FORCE)
429
429
  set(Ruby_RUBY_LIB_DIR ${Ruby_RUBY_LIB_DIR} CACHE INTERNAL "The Ruby ruby-lib dir" FORCE)
430
430
  set(_Ruby_SO_NAME ${_Ruby_SO_NAME} CACHE PATH "The Ruby shared library name" FORCE)
431
- set(_Ruby_DLEXT ${_Ruby_DLEXT} CACHE PATH "Ruby extensions extension" FORCE)
431
+ set(_Ruby_DLEXT ${_Ruby_DLEXT} CACHE STRING "Ruby extensions extension" FORCE)
432
432
  set(Ruby_SITEARCH_DIR ${Ruby_SITEARCH_DIR} CACHE INTERNAL "The Ruby site arch dir" FORCE)
433
433
  set(Ruby_SITELIB_DIR ${Ruby_SITELIB_DIR} CACHE INTERNAL "The Ruby site lib dir" FORCE)
434
434
  set(Ruby_HAS_VENDOR_RUBY ${Ruby_HAS_VENDOR_RUBY} CACHE BOOL "Vendor Ruby is available" FORCE)
@@ -599,6 +599,10 @@ namespace Rice::detail
599
599
  {
600
600
  class WrapperBase
601
601
  {
602
+ public:
603
+ static void addKeepAlive(VALUE object, VALUE keepAlive);
604
+ static bool isConst(VALUE object);
605
+
602
606
  public:
603
607
  WrapperBase(rb_data_type_t* rb_data_type);
604
608
  virtual ~WrapperBase() = default;
@@ -672,7 +676,7 @@ namespace Rice::detail
672
676
 
673
677
  // ---- Helper Functions ---------
674
678
  template <typename T>
675
- void wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data);
679
+ Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data);
676
680
 
677
681
  template <typename T>
678
682
  VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner);
@@ -3020,8 +3024,9 @@ namespace Rice
3020
3024
  using Receiver_T = typename attribute_traits<Attribute_T>::class_type;
3021
3025
 
3022
3026
  public:
3023
- // Register attribute getter/setter with Ruby
3024
- static void define(VALUE klass, std::string name, Attribute_T attribute);
3027
+ // Register attribute setter with Ruby
3028
+ template<typename...Arg_Ts>
3029
+ static void define(VALUE klass, std::string name, Attribute_T attribute, Arg_Ts&...args);
3025
3030
 
3026
3031
  public:
3027
3032
  // Disallow creating/copying/moving
@@ -3039,11 +3044,12 @@ namespace Rice
3039
3044
  VALUE returnKlass() override;
3040
3045
 
3041
3046
  protected:
3042
- NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr);
3047
+ NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr, std::unique_ptr<Parameter<T_Unqualified>> parameter);
3043
3048
 
3044
3049
  private:
3045
3050
  VALUE klass_;
3046
3051
  Attribute_T attribute_;
3052
+ std::unique_ptr<Parameter<T_Unqualified>> parameter_;
3047
3053
  };
3048
3054
  } // detail
3049
3055
  } // Rice
@@ -3804,10 +3810,11 @@ namespace Rice::detail
3804
3810
  NativeRegistry& operator=(const NativeRegistry& other) = delete;
3805
3811
 
3806
3812
  void add(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
3813
+ void replace(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
3807
3814
  void reset(VALUE klass);
3808
3815
 
3809
- const std::vector<Native*> lookup(VALUE klass);
3810
- const std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
3816
+ std::vector<Native*> lookup(VALUE klass);
3817
+ std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
3811
3818
  std::vector<Native*> lookup(VALUE klass, NativeKind kind);
3812
3819
 
3813
3820
  private:
@@ -3976,17 +3983,16 @@ namespace Rice::detail
3976
3983
  // One caveat - procs are also RUBY_T_DATA so don't check if this is a function type
3977
3984
  if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA && !std::is_function_v<std::remove_pointer_t<T>>)
3978
3985
  {
3979
- // Check the constness of the Ruby wrapped value and the parameter
3980
- WrapperBase* wrapper = getWrapper(value);
3986
+ bool isConst = WrapperBase::isConst(value);
3981
3987
 
3982
3988
  // Do not send a const value to a non-const parameter
3983
- if (wrapper->isConst() && !is_const_any_v<T>)
3989
+ if (isConst && !is_const_any_v<T>)
3984
3990
  {
3985
3991
  result = Convertible::None;
3986
3992
  }
3987
3993
  // It is ok to send a non-const value to a const parameter but
3988
3994
  // prefer non-const to non-const by slightly decreasing the score
3989
- else if (!wrapper->isConst() && is_const_any_v<T>)
3995
+ else if (!isConst && is_const_any_v<T>)
3990
3996
  {
3991
3997
  result = Convertible::ConstMismatch;
3992
3998
  }
@@ -8929,17 +8935,21 @@ namespace Rice::detail
8929
8935
  {
8930
8936
  inline void NativeRegistry::add(VALUE klass, ID methodId, std::unique_ptr<Native>& native)
8931
8937
  {
8932
- if (rb_type(klass) == T_ICLASS)
8933
- {
8934
- klass = detail::protect(rb_class_of, klass);
8935
- }
8938
+ // Lookup items for method
8939
+ std::vector<std::unique_ptr<Native>>& natives = NativeRegistry::lookup(klass, methodId);
8936
8940
 
8937
- // Create the key
8938
- std::pair<VALUE, ID> key = std::make_pair(klass, methodId);
8941
+ // Add new native
8942
+ natives.push_back(std::move(native));
8943
+ }
8939
8944
 
8945
+ inline void NativeRegistry::replace(VALUE klass, ID methodId, std::unique_ptr<Native>& native)
8946
+ {
8940
8947
  // Lookup items for method
8941
- std::vector<std::unique_ptr<Native>>& natives = this->natives_[key];
8948
+ std::vector<std::unique_ptr<Native>>& natives = NativeRegistry::lookup(klass, methodId);
8942
8949
 
8950
+ // Clear existing natives
8951
+ natives.clear();
8952
+ // Add new native
8943
8953
  natives.push_back(std::move(native));
8944
8954
  }
8945
8955
 
@@ -8959,7 +8969,7 @@ namespace Rice::detail
8959
8969
  }
8960
8970
  }
8961
8971
 
8962
- inline const std::vector<Native*> NativeRegistry::lookup(VALUE klass)
8972
+ inline std::vector<Native*> NativeRegistry::lookup(VALUE klass)
8963
8973
  {
8964
8974
  std::vector<Native*> result;
8965
8975
 
@@ -8985,7 +8995,7 @@ namespace Rice::detail
8985
8995
  return result;
8986
8996
  }
8987
8997
 
8988
- inline const std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
8998
+ inline std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
8989
8999
  {
8990
9000
  if (rb_type(klass) == T_ICLASS)
8991
9001
  {
@@ -9577,6 +9587,18 @@ namespace Rice::detail
9577
9587
 
9578
9588
  namespace Rice::detail
9579
9589
  {
9590
+ inline void WrapperBase::addKeepAlive(VALUE object, VALUE keepAlive)
9591
+ {
9592
+ WrapperBase* wrapper = getWrapper(object);
9593
+ wrapper->addKeepAlive(keepAlive);
9594
+ }
9595
+
9596
+ inline bool WrapperBase::isConst(VALUE object)
9597
+ {
9598
+ WrapperBase* wrapper = getWrapper(object);
9599
+ return wrapper->isConst();
9600
+ }
9601
+
9580
9602
  inline WrapperBase::WrapperBase(rb_data_type_t* rb_data_type) : rb_data_type_(rb_data_type)
9581
9603
  {
9582
9604
  }
@@ -9874,7 +9896,7 @@ namespace Rice::detail
9874
9896
  }
9875
9897
 
9876
9898
  template <typename T>
9877
- inline void wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data)
9899
+ inline Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data)
9878
9900
  {
9879
9901
  using Wrapper_T = Wrapper<T*>;
9880
9902
 
@@ -9890,6 +9912,8 @@ namespace Rice::detail
9890
9912
  RTYPEDDATA_DATA(value) = wrapper;
9891
9913
 
9892
9914
  Registries::instance.instances.add(data, value);
9915
+
9916
+ return wrapper;
9893
9917
  }
9894
9918
  }
9895
9919
 
@@ -10072,16 +10096,14 @@ namespace Rice::detail
10072
10096
  Arg* arg = parameters_[i]->arg();
10073
10097
  if (arg->isKeepAlive())
10074
10098
  {
10075
- static WrapperBase* selfWrapper = getWrapper(self);
10076
- selfWrapper->addKeepAlive(rubyValues[i].value());
10099
+ WrapperBase::addKeepAlive(self, rubyValues[i].value());
10077
10100
  }
10078
10101
  }
10079
10102
 
10080
10103
  // Check return value
10081
10104
  if (this->returnInfo_->isKeepAlive())
10082
10105
  {
10083
- WrapperBase* returnWrapper = getWrapper(returnValue);
10084
- returnWrapper->addKeepAlive(self);
10106
+ WrapperBase::addKeepAlive(returnValue, self);
10085
10107
  }
10086
10108
  }
10087
10109
 
@@ -10420,7 +10442,7 @@ namespace Rice::detail
10420
10442
  // matches or calls function pointer. Instead Ruby can call the static call method defined on
10421
10443
  // this class (&NativeAttribute_T::get).
10422
10444
  Identifier identifier(name);
10423
- detail::Registries::instance.natives.add(klass, identifier.id(), native);
10445
+ detail::Registries::instance.natives.replace(klass, identifier.id(), native);
10424
10446
  }
10425
10447
 
10426
10448
  template<typename Attribute_T>
@@ -10525,28 +10547,46 @@ namespace Rice::detail
10525
10547
  namespace Rice::detail
10526
10548
  {
10527
10549
  template<typename Attribute_T>
10528
- void NativeAttributeSet<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute)
10550
+ template<typename...Arg_Ts>
10551
+ void NativeAttributeSet<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute, Arg_Ts&...args)
10529
10552
  {
10530
- // Create a NativeAttributeSet that Ruby will call to read/write C++ variables
10531
- NativeAttribute_T* nativeAttribute = new NativeAttribute_T(klass, name, std::forward<Attribute_T>(attribute));
10553
+ // Extract Arg from Arg_Ts if present, otherwise create default
10554
+ using Arg_Tuple = std::tuple<Arg_Ts...>;
10555
+ constexpr std::size_t index = tuple_element_index_v<Arg_Tuple, Arg, ArgBuffer>;
10556
+
10557
+ std::unique_ptr<Arg> arg;
10558
+ if constexpr (index < std::tuple_size_v<Arg_Tuple>)
10559
+ {
10560
+ using Arg_T_Local = std::decay_t<std::tuple_element_t<index, Arg_Tuple>>;
10561
+ const Arg_T_Local& argInfo = std::get<index>(std::forward_as_tuple(std::forward<Arg_Ts>(args)...));
10562
+ arg = std::make_unique<Arg_T_Local>(argInfo);
10563
+ }
10564
+ else
10565
+ {
10566
+ arg = std::make_unique<Arg>("value");
10567
+ }
10568
+
10569
+ // Create the parameter
10570
+ auto parameter = std::make_unique<Parameter<T_Unqualified>>(std::move(arg));
10571
+
10572
+ // Create a NativeAttributeSet that Ruby will call to write C++ variables
10573
+ NativeAttribute_T* nativeAttribute = new NativeAttribute_T(klass, name, std::forward<Attribute_T>(attribute), std::move(parameter));
10532
10574
  std::unique_ptr<Native> native(nativeAttribute);
10533
10575
 
10534
10576
  // Define the write method name
10535
10577
  std::string setter = name + "=";
10536
10578
 
10537
- // Tell Ruby to invoke the static method write to get the attribute value
10579
+ // Tell Ruby to invoke the static method resolve to set the attribute value
10538
10580
  detail::protect(rb_define_method, klass, setter.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
10539
10581
 
10540
- // Add to native registry. Since attributes cannot be overridden, there is no need to set the
10541
- // matches or calls function pointer. Instead Ruby can call the static call method defined on
10542
- // this class (&NativeAttribute_T::set).
10582
+ // Add to native registry
10543
10583
  Identifier identifier(setter);
10544
- detail::Registries::instance.natives.add(klass, identifier.id(), native);
10584
+ detail::Registries::instance.natives.replace(klass, identifier.id(), native);
10545
10585
  }
10546
10586
 
10547
10587
  template<typename Attribute_T>
10548
- NativeAttributeSet<Attribute_T>::NativeAttributeSet(VALUE klass, std::string name, Attribute_T attribute)
10549
- : Native(name), klass_(klass), attribute_(attribute)
10588
+ NativeAttributeSet<Attribute_T>::NativeAttributeSet(VALUE klass, std::string name, Attribute_T attribute, std::unique_ptr<Parameter<T_Unqualified>> parameter)
10589
+ : Native(name), klass_(klass), attribute_(attribute), parameter_(std::move(parameter))
10550
10590
  {
10551
10591
  }
10552
10592
 
@@ -10567,25 +10607,25 @@ namespace Rice::detail
10567
10607
  throw std::runtime_error("Incorrect number of parameters for setting attribute. Attribute: " + this->name_);
10568
10608
  }
10569
10609
 
10610
+ // Get the Ruby value and convert to native
10570
10611
  VALUE value = values.begin()->second;
10612
+ std::optional<VALUE> valueOpt(value);
10613
+ T_Unqualified nativeValue = this->parameter_->convertToNative(valueOpt);
10571
10614
 
10572
10615
  if constexpr (!std::is_null_pointer_v<Receiver_T>)
10573
10616
  {
10574
10617
  Receiver_T* nativeSelf = From_Ruby<Receiver_T*>().convert(self);
10575
-
10576
- // Deal with pointers to pointes, see Parameter::convertToNative commment
10577
- if constexpr (is_pointer_pointer_v<Attr_T> && !std::is_convertible_v<remove_cv_recursive_t<Attr_T>, Attr_T>)
10578
- {
10579
- nativeSelf->*attribute_ = (Attr_T)From_Ruby<T_Unqualified>().convert(value);
10580
- }
10581
- else
10582
- {
10583
- nativeSelf->*attribute_ = From_Ruby<T_Unqualified>().convert(value);
10584
- }
10618
+ nativeSelf->*attribute_ = (Attr_T)nativeValue;
10585
10619
  }
10586
10620
  else
10587
10621
  {
10588
- *attribute_ = From_Ruby<T_Unqualified>().convert(value);
10622
+ *attribute_ = nativeValue;
10623
+ }
10624
+
10625
+ // Check if we need to prevent the value from being garbage collected
10626
+ if (this->parameter_->arg()->isKeepAlive())
10627
+ {
10628
+ WrapperBase::addKeepAlive(self, value);
10589
10629
  }
10590
10630
 
10591
10631
  return value;
@@ -10606,18 +10646,8 @@ namespace Rice::detail
10606
10646
  template<typename Attribute_T>
10607
10647
  inline VALUE NativeAttributeSet<Attribute_T>::returnKlass()
10608
10648
  {
10609
- // Check if an array is being returned
10610
- bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
10611
- if (isBuffer)
10612
- {
10613
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Attr_T>>>> typeMapper;
10614
- return typeMapper.rubyKlass();
10615
- }
10616
- else
10617
- {
10618
- TypeMapper<Attr_T> typeMapper;
10619
- return typeMapper.rubyKlass();
10620
- }
10649
+ TypeMapper<Attr_T> typeMapper;
10650
+ return typeMapper.rubyKlass();
10621
10651
  }
10622
10652
  }
10623
10653
 
@@ -11257,30 +11287,33 @@ namespace Rice::detail
11257
11287
  {
11258
11288
  return self;
11259
11289
  }
11260
- /* This case happens when a class wrapped by Rice is calling a method
11261
- defined on an ancestor class. For example, the std::map size method
11262
- is defined on _Tree not map. Rice needs to know the actual type
11263
- that was wrapped so it can correctly extract the C++ object from
11264
- the Ruby object. */
11265
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
11266
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
11267
- std::is_pointer_v<Receiver_T>)
11268
- {
11269
- Class_T* instance = From_Ruby<Class_T*>().convert(self);
11270
- return dynamic_cast<Receiver_T>(instance);
11271
- }
11272
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
11273
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
11274
- std::is_reference_v<Receiver_T>)
11275
- {
11276
- Class_T& instance = From_Ruby<Class_T&>().convert(self);
11277
- return dynamic_cast<Receiver_T>(instance);
11278
- }
11279
- // Self parameter could be derived from Object or it is an C++ instance and
11280
- // needs to be unwrapped from Ruby
11281
11290
  else
11282
11291
  {
11283
- return From_Ruby<Receiver_T>().convert(self);
11292
+ /* When a class wrapped by Rice calls a method defined on an ancestor class
11293
+ (e.g., std::map calling a method from _Tree), we need to unwrap as Class_T
11294
+ and dynamic_cast to the base class. Otherwise unwrap directly as Receiver_T. */
11295
+ constexpr bool isDerived = !std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
11296
+ std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T>;
11297
+
11298
+ if constexpr (isDerived)
11299
+ {
11300
+ if constexpr (std::is_pointer_v<Receiver_T>)
11301
+ {
11302
+ Class_T* instance = From_Ruby<Class_T*>().convert(self);
11303
+ return dynamic_cast<Receiver_T>(instance);
11304
+ }
11305
+ else if constexpr (std::is_reference_v<Receiver_T>)
11306
+ {
11307
+ Class_T& instance = From_Ruby<Class_T&>().convert(self);
11308
+ return dynamic_cast<Receiver_T>(instance);
11309
+ }
11310
+ }
11311
+ else
11312
+ {
11313
+ // Note GCC has a false warning: function may return address of local variable [-Wreturn-local-addr].
11314
+ // From_Ruby returns a reference to data in the Ruby object, not the temporary.
11315
+ return From_Ruby<Receiver_T>().convert(self);
11316
+ }
11284
11317
  }
11285
11318
  }
11286
11319
 
@@ -14548,7 +14581,7 @@ namespace Rice
14548
14581
  }
14549
14582
  else
14550
14583
  {
14551
- detail::NativeAttributeSet<Attribute_T>::define(klass, name, std::forward<Attribute_T>(attribute));
14584
+ detail::NativeAttributeSet<Attribute_T>::define(klass, name, std::forward<Attribute_T>(attribute), args...);
14552
14585
  }
14553
14586
  }
14554
14587