diff --git a/CHANGELOG.md b/CHANGELOG.md index fba9929f..3ac989e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 4.9.2 (unreleased) +## 4.10.0 (2026-02-05) Enhancements: * Add support for incomplete types (PIMPL/opaque handle patterns). Rice now uses `typeid(T*)` for forward-declared types that are never fully defined. @@ -8,9 +8,12 @@ Enhancements: * Add support for `Buffer` and `Pointer` * Add support for `std::function`. Ruby procs, lambdas, and blocks can be wrapped in `std::function` objects and passed to C++ methods. C++ functions returning `std::function` are automatically wrapped. * Add support for `std::ostream`, `std::ostringstream`, and `std::ofstream`. Ruby can write to C++ streams and pass them to C++ functions. Standard streams are exposed as `Std::COUT` and `Std::CERR`. +* Add support for verifying arrays of non-fundamental types (e.g., `MyClass[2]`) +* Delegate method calls for smart pointers to their wrapped objects via method_missing? Internal: * Refactor type handling by merging `TypeMapper` into `TypeDetail` and simplifying class hierarchy +* Greatly simplify define_attr Incompatible Changes: * `Address_Registration_Guard` has been replaced by `Pin`. If you are using `Address_Registration_Guard` diff --git a/docs/architecture/registries.md b/docs/architecture/registries.md index 5dd71774..46762931 100644 --- a/docs/architecture/registries.md +++ b/docs/architecture/registries.md @@ -27,10 +27,19 @@ Maps C++ types to Ruby classes. The TypeRegistry serves two important purposes: When a C++ method returns a base class pointer that actually points to a derived class, Rice uses RTTI (`typeid`) to look up the derived type in the registry and wrap it as the correct Ruby class. For example: ```cpp -class Base { virtual ~Base() = default; }; -class Derived : public Base {}; +class Base +{ + virtual ~Base() = default; +}; + +class Derived : public Base +{ +}; -Base* create() { return new Derived(); } // Returns Derived* as Base* +Base* create() +{ + return new Derived(); +} // Returns Derived* as Base* ``` When `create()` is called from Ruby, Rice uses `typeid(*result)` to discover the object is actually a `Derived`, looks it up in the TypeRegistry, and wraps it as `Rb::Derived` instead of `Rb::Base`. diff --git a/lib/rice/version.rb b/lib/rice/version.rb index fe6c0edb..0e008019 100644 --- a/lib/rice/version.rb +++ b/lib/rice/version.rb @@ -1,3 +1,3 @@ module Rice - VERSION = "4.9.2" + VERSION = "4.10.0" end diff --git a/rice/Data_Type.ipp b/rice/Data_Type.ipp index 7753780a..f97f06b0 100644 --- a/rice/Data_Type.ipp +++ b/rice/Data_Type.ipp @@ -81,14 +81,28 @@ namespace Rice { dataType.define_singleton_method("cpp_class", [](VALUE) -> VALUE { + Return returnInfo; + returnInfo.takeOwnership(); + detail::TypeDetail typeDetail; std::string cppClassName = typeDetail.simplifiedName(); + + return detail::To_Ruby(&returnInfo).convert(cppClassName.c_str()); + }, Arg("klass").setValue(), Return().setValue()); + } + else + { + VALUE klass_value = klass.value(); + dataType.define_singleton_method("cpp_class", [klass_value](VALUE) -> VALUE + { Return returnInfo; returnInfo.takeOwnership(); + + Rice::String cppClassName = detail::protect(rb_class_path, klass_value); + return detail::To_Ruby(&returnInfo).convert(cppClassName.c_str()); }, Arg("klass").setValue(), Return().setValue()); } - return dataType; } diff --git a/rice/detail/Type.hpp b/rice/detail/Type.hpp index f94ccc95..07c8862b 100644 --- a/rice/detail/Type.hpp +++ b/rice/detail/Type.hpp @@ -37,6 +37,7 @@ namespace Rice::detail struct Type { static bool verify(); + static VALUE rubyKlass(); }; template diff --git a/rice/detail/Type.ipp b/rice/detail/Type.ipp index a36bcc47..74c6b36c 100644 --- a/rice/detail/Type.ipp +++ b/rice/detail/Type.ipp @@ -40,6 +40,13 @@ namespace Rice::detail return Type::verify(); } + template + inline VALUE Type::rubyKlass() + { + detail::TypeDetail> typeDetail; + return typeDetail.rubyKlass(); + } + template void verifyType() { @@ -58,16 +65,4 @@ namespace Rice::detail std::make_index_sequence> indexes; verifyTypesImpl(indexes); } - - // ---------- RubyKlass ------------ - // Helper template to see if the method rubyKlass is defined on a Type specialization - template> - struct has_ruby_klass : std::false_type - { - }; - - template - struct has_ruby_klass> : std::true_type - { - }; } diff --git a/rice/stl/shared_ptr.ipp b/rice/stl/shared_ptr.ipp index e5f1fa7e..c762dd49 100644 --- a/rice/stl/shared_ptr.ipp +++ b/rice/stl/shared_ptr.ipp @@ -115,10 +115,8 @@ namespace Rice::detail result = result && Type::verify(); } - if (result) - { - define_shared_ptr(); - } + // We ALWAYS need to define the std::shared_ptr, even if T is not bound, because it could be bound after this call + define_shared_ptr(); return result; } diff --git a/rice/stl/unique_ptr.ipp b/rice/stl/unique_ptr.ipp index 2d4c5cc6..6310d5c5 100644 --- a/rice/stl/unique_ptr.ipp +++ b/rice/stl/unique_ptr.ipp @@ -112,10 +112,8 @@ namespace Rice::detail result = result && Type::verify(); } - if (result) - { - define_unique_ptr(); - } + // We ALWAYS need to define the std::unique_ptr, even if T is not bound, because it could be bound after this call + define_unique_ptr(); return result; } diff --git a/rice/traits/rice_traits.hpp b/rice/traits/rice_traits.hpp index 56e0f6bb..0bc2a288 100644 --- a/rice/traits/rice_traits.hpp +++ b/rice/traits/rice_traits.hpp @@ -162,6 +162,17 @@ namespace Rice template constexpr bool is_wrapped_v = is_wrapped::value; + // ---------- RubyKlass ------------ + template> + struct has_ruby_klass : std::false_type + { + }; + + template + struct has_ruby_klass> : std::true_type + { + }; + // -- Tuple Helpers --- template struct tuple_shift; diff --git a/test/test_Type.cpp b/test/test_Type.cpp index 4711e72a..38b84ef3 100644 --- a/test/test_Type.cpp +++ b/test/test_Type.cpp @@ -418,6 +418,16 @@ TESTCASE(RubyKlass) expected = stdModule.const_get("OFStream"); actual = typeDetail34.rubyKlass(); ASSERT_EQUAL(expected.value(), actual); + + detail::TypeDetail typeDetail35; + actual = typeDetail35.rubyKlass(); + ASSERT_EQUAL(rb_cString, actual); + + define_pointer>(); + detail::TypeDetail[2]> typeDetail36; + expected = riceModule.const_get("Pointer≺vector≺int≻≻"); + actual = typeDetail36.rubyKlass(); + ASSERT_EQUAL(expected.value(), actual); } TESTCASE(MakeRubyClass)