Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rubygem-rice for openSUSE:Factory checked in at 2024-02-26 19:49:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-rice (Old) and /work/SRC/openSUSE:Factory/.rubygem-rice.new.1770 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-rice" Mon Feb 26 19:49:06 2024 rev:11 rq:1151748 version:4.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-rice/rubygem-rice.changes 2023-11-15 21:08:07.931244809 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-rice.new.1770/rubygem-rice.changes 2024-02-26 19:50:20.700654678 +0100 @@ -1,0 +2,15 @@ +Mon Jan 29 14:29:16 UTC 2024 - Dan Äermák <dan.cer...@posteo.net> + +- ## 4.2.1 + +* Support systems who use `#include <experimental/filesystem>` over `#include<filesystem>`. See [#197](https://github.com/jasonroelofs/rice/issues/197) and [#201](https://github.com/jasonroelofs/rice/pull/201) + +## 4.2 + +* Support Ruby 3.3.0. +* Split Object.call to an explicit Object.call_kw for calling methods expecting keyword arguments. +* Previously, if a wrapper used `keepAlive` on an argument or return value that was itself a Rice type, calling said method would segfault. We've now added an explicit exception to be thrown in this case, prevending the segfault and providing guidance on what was wrong and how to fix it. See [#193](https://github.com/jasonroelofs/rice/pull/193) and [#194](https://github.com/jasonroelofs/rice/pull/194) +* Fix wrapping of std::shared_ptr to properly take default arguments into account. + + +------------------------------------------------------------------- Old: ---- rice-4.1.0.gem New: ---- rice-4.2.1.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-rice.spec ++++++ --- /var/tmp/diff_new_pack.ItIwdV/_old 2024-02-26 19:50:21.236674056 +0100 +++ /var/tmp/diff_new_pack.ItIwdV/_new 2024-02-26 19:50:21.236674056 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-rice # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,7 +24,7 @@ # Name: rubygem-rice -Version: 4.1.0 +Version: 4.2.1 Release: 0 %define mod_name rice %define mod_full_name %{mod_name}-%{version} ++++++ rice-4.1.0.gem -> rice-4.2.1.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md --- old/CHANGELOG.md 2023-04-23 02:19:48.000000000 +0200 +++ new/CHANGELOG.md 2024-01-20 14:53:39.000000000 +0100 @@ -1,3 +1,14 @@ +## 4.2.1 + +* Support systems who use `#include <experimental/filesystem>` over `#include<filesystem>`. See [#197](https://github.com/jasonroelofs/rice/issues/197) and [#201](https://github.com/jasonroelofs/rice/pull/201) + +## 4.2 + +* Support Ruby 3.3.0. +* Split Object.call to an explicit Object.call_kw for calling methods expecting keyword arguments. +* Previously, if a wrapper used `keepAlive` on an argument or return value that was itself a Rice type, calling said method would segfault. We've now added an explicit exception to be thrown in this case, prevending the segfault and providing guidance on what was wrong and how to fix it. See [#193](https://github.com/jasonroelofs/rice/pull/193) and [#194](https://github.com/jasonroelofs/rice/pull/194) +* Fix wrapping of std::shared_ptr to properly take default arguments into account. + ## 4.1 Rice 4.1 builds on the 4.0 release and has a number of improvements that both polish Rice and extend its functionality. However, there are three incompatibilities to know about: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CONTRIBUTORS.md new/CONTRIBUTORS.md --- old/CONTRIBUTORS.md 2023-04-23 02:19:48.000000000 +0200 +++ new/CONTRIBUTORS.md 2024-01-20 14:53:39.000000000 +0100 @@ -17,3 +17,6 @@ * [Charlie Savage (cfis)](https://github.com/cfis) for multiple improvements and modernizations: [#130](https://github.com/jasonroelofs/rice/pull/130), [#131](https://github.com/jasonroelofs/rice/pull/131), [#133](https://github.com/jasonroelofs/rice/pull/133), [#134](https://github.com/jasonroelofs/rice/pull/134), [#136](https://github.com/jasonroelofs/rice/pull/136), [#137](https://github.com/jasonroelofs/rice/pull/137), [#140](https://github.com/jasonroelofs/rice/pull/140), [#141](https://github.com/jasonroelofs/rice/pull/141) and many others, including the work to make Rice header-only. * [Atsushi Tatsuma (yoshoku)](https://github.com/yoshoku) for [#135](https://github.com/jasonroelofs/rice/pull/135) * [Andrew Kane (ankane)](https://github.com/ankane) for helping [test Rice 4](https://github.com/jasonroelofs/rice/issues/149). +* [Maxim Samsonov (maxirmx)](https://github.com/maxirmx) for [#193](https://github.com/jasonroelofs/rice/issues/193) and [#194](https://github.com/jasonroelofs/rice/pull/194) +* [kvtb](https://github.com/kvtb) for [#191](https://github.com/jasonroelofs/rice/issues/191) +* [thekendalmiller](https://github.com/thekendalmiller) for [#201](https://github.com/jasonroelofs/rice/pull/201) Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/include/rice/rice.hpp new/include/rice/rice.hpp --- old/include/rice/rice.hpp 2023-04-23 02:19:48.000000000 +0200 +++ new/include/rice/rice.hpp 2024-01-20 14:53:39.000000000 +0100 @@ -1205,9 +1205,18 @@ // ========= cpp_protect.hpp ========= #include <regex> -#include <filesystem> #include <stdexcept> +#if __has_include(<filesystem>) + #include <filesystem> + namespace fs = std::filesystem; +#elif __has_include(<experimental/filesystem>) + #include <experimental/filesystem> + namespace fs = std::experimental::filesystem; +#else + #error "no filesystem include found :'(" +#endif + namespace Rice::detail { @@ -1247,7 +1256,7 @@ { rb_exc_raise(rb_exc_new2(rb_eArgError, ex.what())); } - catch (std::filesystem::filesystem_error const& ex) + catch (fs::filesystem_error const& ex) { rb_exc_raise(rb_exc_new2(rb_eIOError, ex.what())); } @@ -1531,7 +1540,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" #endif - return static_cast<Wrapper*>(RTYPEDDATA_DATA(value)); + return RTYPEDDATA_P(value) ? static_cast<Wrapper*>(RTYPEDDATA_DATA(value)) : nullptr; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif @@ -4052,6 +4061,9 @@ // Figure out what self is Class_T getReceiver(VALUE self); + // Throw an exception when wrapper cannot be extracted + [[noreturn]] void noWrapper(const VALUE klass, const std::string& wrapper); + // Do we need to keep alive any arguments? void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues); @@ -4073,6 +4085,7 @@ #include <array> #include <algorithm> #include <stdexcept> +#include <sstream> namespace Rice::detail @@ -4107,7 +4120,7 @@ NativeFunction<From_Ruby_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo) : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo) { - // Create a tuple of NativeArgs that will convert the Ruby values to native values. For + // Create a tuple of NativeArgs that will convert the Ruby values to native values. For // builtin types NativeArgs will keep a copy of the native value so that it // can be passed by reference or pointer to the native function. For non-builtin types // it will just pass the value through. @@ -4233,7 +4246,7 @@ { // Call the native method and get the result Return_T nativeResult = std::apply(this->function_, nativeArgs); - + // Return the result return this->toRuby_.convert(nativeResult); } @@ -4285,14 +4298,37 @@ } template<typename From_Ruby_T, typename Function_T, bool IsMethod> + void NativeFunction<From_Ruby_T, Function_T, IsMethod>::noWrapper(const VALUE klass, const std::string& wrapper) + { + std::stringstream message; + + message << "When calling the method `"; + message << this->method_name_; + message << "' we could not find the wrapper for the '"; + message << rb_obj_classname(klass); + message << "' "; + message << wrapper; + message << " type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type."; + + throw std::runtime_error(message.str()); + } + + template<typename From_Ruby_T, typename Function_T, bool IsMethod> void NativeFunction<From_Ruby_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues) { - // Check function arguments + // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type + // it is highly unlikely that keepAlive is used in this case but we check anyway Wrapper* selfWrapper = getWrapper(self); + + // Check function arguments for (const Arg& arg : (*this->methodInfo_)) { if (arg.isKeepAlive()) { + if (selfWrapper == nullptr) + { + noWrapper(self, "self"); + } selfWrapper->addKeepAlive(rubyValues[arg.position]); } } @@ -4300,7 +4336,17 @@ // Check return value if (this->methodInfo_->returnInfo.isKeepAlive()) { + if (selfWrapper == nullptr) + { + noWrapper(self, "self"); + } + + // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type Wrapper* returnWrapper = getWrapper(returnValue); + if (returnWrapper == nullptr) + { + noWrapper(returnValue, "return"); + } returnWrapper->addKeepAlive(self); } } @@ -4672,7 +4718,8 @@ //! Call the Ruby method specified by 'id' on object 'obj'. /*! Pass in arguments (arg1, arg2, ...). The arguments will be converted to - * Ruby objects with to_ruby<>. + * Ruby objects with to_ruby<>. To call methods expecting keyword arguments, + * use call_kw. * * E.g.: * \code @@ -4690,6 +4737,29 @@ template<typename ...Arg_Ts> Object call(Identifier id, Arg_Ts... args) const; + //! Call the Ruby method specified by 'id' on object 'obj'. + /*! Pass in arguments (arg1, arg2, ...). The arguments will be converted to + * Ruby objects with to_ruby<>. The final argument must be a Hash and will be treated + * as keyword arguments to the function. + * + * E.g.: + * \code + * Rice::Hash kw; + * kw[":argument"] = String("one") + * Rice::Object obj = x.call_kw("foo", kw); + * \endcode + * + * If a return type is specified, the return value will automatically be + * converted to that type as long as 'from_ruby' exists for that type. + * + * E.g.: + * \code + * float ret = x.call_kw<float>("foo", kw); + * \endcode + */ + template<typename ...Arg_Ts> + Object call_kw(Identifier id, Arg_Ts... args) const; + //! Vectorized call. /*! Calls the method identified by id with the list of arguments * identified by args. @@ -4753,6 +4823,7 @@ } // namespace Rice #endif // Rice__Object_defn__hpp_ + // --------- Object.ipp --------- #ifndef Rice__Object__ipp_ #define Rice__Object__ipp_ @@ -4797,7 +4868,15 @@ easy to duplicate by setting GC.stress to true and calling a constructor that takes multiple values like a std::pair wrapper. */ std::array<VALUE, sizeof...(Arg_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Arg_Ts>>().convert(args)... }; - return detail::protect(rb_funcallv_kw, value(), id.id(), (int)values.size(), (const VALUE*)values.data(), RB_PASS_CALLED_KEYWORDS); + return detail::protect(rb_funcallv, value(), id.id(), (int)values.size(), (const VALUE*)values.data()); + } + + template<typename ...Arg_Ts> + inline Object Object::call_kw(Identifier id, Arg_Ts... args) const + { + /* IMPORTANT - See call() above */ + std::array<VALUE, sizeof...(Arg_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Arg_Ts>>().convert(args)... }; + return detail::protect(rb_funcallv_kw, value(), id.id(), (int)values.size(), (const VALUE*)values.data(), RB_PASS_KEYWORDS); } template<typename T> @@ -4975,6 +5054,7 @@ #endif // Rice__Object__ipp_ + // ========= Builtin_Object.hpp ========= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/include/rice/stl.hpp new/include/rice/stl.hpp --- old/include/rice/stl.hpp 2023-04-23 02:19:48.000000000 +0200 +++ new/include/rice/stl.hpp 2024-01-20 14:53:39.000000000 +0100 @@ -168,6 +168,11 @@ return std::complex<T>(From_Ruby<T>().convert(real), From_Ruby<T>().convert(imaginary)); } + + bool is_convertible(VALUE value) + { + return rb_type(value) == RUBY_T_COMPLEX; + } }; template<typename T> @@ -183,11 +188,17 @@ return this->converted_; } + bool is_convertible(VALUE value) + { + return rb_type(value) == RUBY_T_COMPLEX; + } + private: std::complex<T> converted_; }; } + // ========= optional.hpp ========= @@ -219,7 +230,7 @@ class To_Ruby<std::optional<T>> { public: - static VALUE convert(std::optional<T>& data, bool takeOwnership = false) + static VALUE convert(const std::optional<T>& data, bool takeOwnership = false) { if (data.has_value()) { @@ -236,7 +247,7 @@ class To_Ruby<std::optional<T>&> { public: - static VALUE convert(std::optional<T>& data, bool takeOwnership = false) + static VALUE convert(const std::optional<T>& data, bool takeOwnership = false) { if (data.has_value()) { @@ -288,6 +299,7 @@ }; } + // ========= reference_wrapper.hpp ========= @@ -309,7 +321,7 @@ class To_Ruby<std::reference_wrapper<T>> { public: - VALUE convert(std::reference_wrapper<T>& data, bool takeOwnership = false) + VALUE convert(const std::reference_wrapper<T>& data, bool takeOwnership = false) { return To_Ruby<T&>().convert(data.get()); } @@ -334,6 +346,7 @@ }; } + // ========= smart_ptr.hpp ========= @@ -448,8 +461,18 @@ class From_Ruby<std::shared_ptr<T>> { public: + From_Ruby() = default; + + explicit From_Ruby(Arg * arg) : arg_(arg) + { + } + std::shared_ptr<T> convert(VALUE value) { + if(value == Qnil && this->arg_ && this->arg_->hasDefaultValue()) { + return this->arg_->template defaultValue<std::shared_ptr<T>>(); + } + Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type()); using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>; @@ -461,14 +484,27 @@ } return smartWrapper->data(); } + + private: + Arg* arg_ = nullptr; }; template <typename T> class From_Ruby<std::shared_ptr<T>&> { public: + From_Ruby() = default; + + explicit From_Ruby(Arg * arg) : arg_(arg) + { + } + std::shared_ptr<T>& convert(VALUE value) { + if(value == Qnil && this->arg_ && this->arg_->hasDefaultValue()) { + return this->arg_->template defaultValue<std::shared_ptr<T>>(); + } + Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type()); using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>; @@ -480,6 +516,9 @@ } return smartWrapper->data(); } + + private: + Arg* arg_ = nullptr; }; template<typename T> @@ -492,6 +531,7 @@ }; } + // ========= monostate.hpp ========= @@ -513,7 +553,7 @@ class To_Ruby<std::monostate> { public: - VALUE convert(std::monostate& _) + VALUE convert(const std::monostate& _) { return Qnil; } @@ -523,7 +563,7 @@ class To_Ruby<std::monostate&> { public: - static VALUE convert(std::monostate& data, bool takeOwnership = false) + static VALUE convert(const std::monostate& data, bool takeOwnership = false) { return Qnil; } @@ -563,6 +603,7 @@ }; } + // ========= variant.hpp ========= @@ -596,13 +637,13 @@ public: template<typename T> - static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership) + static VALUE convertElement(const std::variant<Types...>& data, bool takeOwnership) { return To_Ruby<T>().convert(std::get<T>(data)); } template<std::size_t... I> - static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices) + static VALUE convertIterator(const std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices) { // Create a tuple of the variant types so we can look over the tuple's types using Tuple_T = std::tuple<Types...>; @@ -634,7 +675,7 @@ return result; } - static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false) + static VALUE convert(const std::variant<Types...>& data, bool takeOwnership = false) { auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{}; return convertIterator(data, takeOwnership, indices); @@ -646,13 +687,13 @@ { public: template<typename T> - static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership) + static VALUE convertElement(const std::variant<Types...>& data, bool takeOwnership) { return To_Ruby<T>().convert(std::get<T>(data)); } template<std::size_t... I> - static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices) + static VALUE convertIterator(const std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices) { // Create a tuple of the variant types so we can look over the tuple's types using Tuple_T = std::tuple<Types...>; @@ -665,7 +706,7 @@ return result; } - static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false) + static VALUE convert(const std::variant<Types...>& data, bool takeOwnership = false) { auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{}; return convertIterator(data, takeOwnership, indices); @@ -755,6 +796,7 @@ }; } + // ========= pair.hpp ========= @@ -2331,6 +2373,11 @@ } } + bool is_convertible(VALUE value) + { + return rb_type(value) == RUBY_T_ARRAY; + } + private: Arg* arg_ = nullptr; }; @@ -2378,6 +2425,11 @@ } } + bool is_convertible(VALUE value) + { + return rb_type(value) == RUBY_T_ARRAY; + } + private: Arg* arg_ = nullptr; std::vector<T> converted_; @@ -2413,10 +2465,16 @@ } } + bool is_convertible(VALUE value) + { + return rb_type(value) == RUBY_T_ARRAY; + } + private: std::vector<T> converted_; }; } } + #endif // Rice__stl__hpp_ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/mkmf-rice.rb new/lib/mkmf-rice.rb --- old/lib/mkmf-rice.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/lib/mkmf-rice.rb 2024-01-20 14:53:39.000000000 +0100 @@ -126,5 +126,7 @@ if IS_DARWIN have_library('c++') elsif !IS_MSWIN - have_library('stdc++') + if !have_library('stdc++fs') + have_library('stdc++') + end end \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/version.rb new/lib/version.rb --- old/lib/version.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/lib/version.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,3 +1,3 @@ module Rice - VERSION = "4.1.0" + VERSION = "4.2.1" end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2023-04-23 02:19:48.000000000 +0200 +++ new/metadata 2024-01-20 14:53:39.000000000 +0100 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: rice version: !ruby/object:Gem::Version - version: 4.1.0 + version: 4.2.1 platform: ruby authors: - Paul Brannan @@ -10,7 +10,7 @@ autorequire: bindir: bin cert_chain: [] -date: 2023-04-23 00:00:00.000000000 Z +date: 2024-01-20 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: bundler @@ -185,6 +185,7 @@ - test/test_Iterator.cpp - test/test_Jump_Tag.cpp - test/test_Keep_Alive.cpp +- test/test_Keep_Alive_No_Wrapper.cpp - test/test_Memory_Management.cpp - test/test_Module.cpp - test/test_Object.cpp @@ -230,7 +231,7 @@ - !ruby/object:Gem::Version version: '0' requirements: [] -rubygems_version: 3.4.12 +rubygems_version: 3.5.4 signing_key: specification_version: 4 summary: Ruby Interface for C++ Extensions diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rice/detail/NativeFunction.hpp new/rice/detail/NativeFunction.hpp --- old/rice/detail/NativeFunction.hpp 2023-04-23 02:19:48.000000000 +0200 +++ new/rice/detail/NativeFunction.hpp 2024-01-20 14:53:39.000000000 +0100 @@ -95,6 +95,9 @@ // Figure out what self is Class_T getReceiver(VALUE self); + // Throw an exception when wrapper cannot be extracted + [[noreturn]] void noWrapper(const VALUE klass, const std::string& wrapper); + // Do we need to keep alive any arguments? void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues); @@ -113,4 +116,4 @@ } #include "NativeFunction.ipp" -#endif // Rice__detail__Native_Function__hpp_ \ No newline at end of file +#endif // Rice__detail__Native_Function__hpp_ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rice/detail/NativeFunction.ipp new/rice/detail/NativeFunction.ipp --- old/rice/detail/NativeFunction.ipp 2023-04-23 02:19:48.000000000 +0200 +++ new/rice/detail/NativeFunction.ipp 2024-01-20 14:53:39.000000000 +0100 @@ -1,6 +1,7 @@ #include <array> #include <algorithm> #include <stdexcept> +#include <sstream> #include "cpp_protect.hpp" #include "to_ruby_defn.hpp" @@ -38,7 +39,7 @@ NativeFunction<From_Ruby_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo) : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo) { - // Create a tuple of NativeArgs that will convert the Ruby values to native values. For + // Create a tuple of NativeArgs that will convert the Ruby values to native values. For // builtin types NativeArgs will keep a copy of the native value so that it // can be passed by reference or pointer to the native function. For non-builtin types // it will just pass the value through. @@ -164,7 +165,7 @@ { // Call the native method and get the result Return_T nativeResult = std::apply(this->function_, nativeArgs); - + // Return the result return this->toRuby_.convert(nativeResult); } @@ -216,14 +217,37 @@ } template<typename From_Ruby_T, typename Function_T, bool IsMethod> + void NativeFunction<From_Ruby_T, Function_T, IsMethod>::noWrapper(const VALUE klass, const std::string& wrapper) + { + std::stringstream message; + + message << "When calling the method `"; + message << this->method_name_; + message << "' we could not find the wrapper for the '"; + message << rb_obj_classname(klass); + message << "' "; + message << wrapper; + message << " type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type."; + + throw std::runtime_error(message.str()); + } + + template<typename From_Ruby_T, typename Function_T, bool IsMethod> void NativeFunction<From_Ruby_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues) { - // Check function arguments + // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type + // it is highly unlikely that keepAlive is used in this case but we check anyway Wrapper* selfWrapper = getWrapper(self); + + // Check function arguments for (const Arg& arg : (*this->methodInfo_)) { if (arg.isKeepAlive()) { + if (selfWrapper == nullptr) + { + noWrapper(self, "self"); + } selfWrapper->addKeepAlive(rubyValues[arg.position]); } } @@ -231,7 +255,17 @@ // Check return value if (this->methodInfo_->returnInfo.isKeepAlive()) { + if (selfWrapper == nullptr) + { + noWrapper(self, "self"); + } + + // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type Wrapper* returnWrapper = getWrapper(returnValue); + if (returnWrapper == nullptr) + { + noWrapper(returnValue, "return"); + } returnWrapper->addKeepAlive(self); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rice/detail/Wrapper.ipp new/rice/detail/Wrapper.ipp --- old/rice/detail/Wrapper.ipp 2023-04-23 02:19:48.000000000 +0200 +++ new/rice/detail/Wrapper.ipp 2024-01-20 14:53:39.000000000 +0100 @@ -193,7 +193,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" #endif - return static_cast<Wrapper*>(RTYPEDDATA_DATA(value)); + return RTYPEDDATA_P(value) ? static_cast<Wrapper*>(RTYPEDDATA_DATA(value)) : nullptr; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rice/detail/cpp_protect.hpp new/rice/detail/cpp_protect.hpp --- old/rice/detail/cpp_protect.hpp 2023-04-23 02:19:48.000000000 +0200 +++ new/rice/detail/cpp_protect.hpp 2024-01-20 14:53:39.000000000 +0100 @@ -2,9 +2,18 @@ #define Rice__detail__cpp_protect__hpp_ #include <regex> -#include <filesystem> #include <stdexcept> +#if __has_include(<filesystem>) + #include <filesystem> + namespace fs = std::filesystem; +#elif __has_include(<experimental/filesystem>) + #include <experimental/filesystem> + namespace fs = std::experimental::filesystem; +#else + #error "no filesystem include found :'(" +#endif + #include "Jump_Tag.hpp" #include "../Exception_defn.hpp" @@ -46,7 +55,7 @@ { rb_exc_raise(rb_exc_new2(rb_eArgError, ex.what())); } - catch (std::filesystem::filesystem_error const& ex) + catch (fs::filesystem_error const& ex) { rb_exc_raise(rb_exc_new2(rb_eIOError, ex.what())); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sample/callbacks/extconf.rb new/sample/callbacks/extconf.rb --- old/sample/callbacks/extconf.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/sample/callbacks/extconf.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,5 +1,4 @@ require 'bundler/setup' -require 'rice' require 'mkmf-rice' create_makefile('sample_callbacks') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sample/enum/extconf.rb new/sample/enum/extconf.rb --- old/sample/enum/extconf.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/sample/enum/extconf.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,5 +1,4 @@ require 'bundler/setup' -require 'rice' require 'mkmf-rice' create_makefile('sample_enum') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sample/inheritance/extconf.rb new/sample/inheritance/extconf.rb --- old/sample/inheritance/extconf.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/sample/inheritance/extconf.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,5 +1,4 @@ require 'bundler/setup' -require 'rice' require 'mkmf-rice' create_makefile('animals') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sample/map/extconf.rb new/sample/map/extconf.rb --- old/sample/map/extconf.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/sample/map/extconf.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,5 +1,4 @@ require 'bundler/setup' -require 'rice' require 'mkmf-rice' create_makefile('map') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/embed_ruby.cpp new/test/embed_ruby.cpp --- old/test/embed_ruby.cpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/embed_ruby.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -15,6 +15,12 @@ ruby_init(); ruby_init_loadpath(); +#if RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 3 + // Force the prelude / builtins + char *opts[] = { "ruby", "-e;" }; + ruby_options(2, opts); +#endif + initialized__ = true; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/ext/t1/extconf.rb new/test/ext/t1/extconf.rb --- old/test/ext/t1/extconf.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/test/ext/t1/extconf.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,5 +1,4 @@ require 'bundler/setup' -require 'rice' require 'mkmf-rice' create_makefile('t1') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/ext/t2/extconf.rb new/test/ext/t2/extconf.rb --- old/test/ext/t2/extconf.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/test/ext/t2/extconf.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,5 +1,4 @@ require 'bundler/setup' -require 'rice' require 'mkmf-rice' create_makefile('t2') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/extconf.rb new/test/extconf.rb --- old/test/extconf.rb 2023-04-23 02:19:48.000000000 +0200 +++ new/test/extconf.rb 2024-01-20 14:53:39.000000000 +0100 @@ -1,5 +1,4 @@ require 'bundler/setup' -require 'rice' require 'mkmf-rice' require 'rbconfig' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_Attribute.cpp new/test/test_Attribute.cpp --- old/test/test_Attribute.cpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/test_Attribute.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -1,4 +1,4 @@ -#include <assert.h> +#include <assert.h> #include "unittest.hpp" #include "embed_ruby.hpp" @@ -60,9 +60,9 @@ ASSERT_EXCEPTION_CHECK( Exception, o.call("read_char=", "some text"), - ASSERT_EQUAL("undefined method `read_char=' for :DataStruct", ex.what()) + ASSERT(std::string(ex.what()).find("undefined method `read_char='") == 0) ); - + // Test writeonly attribute result = o.call("write_int=", 5); ASSERT_EQUAL(5, detail::From_Ruby<int>().convert(result.value())); @@ -70,7 +70,7 @@ ASSERT_EXCEPTION_CHECK( Exception, o.call("write_int", 3), - ASSERT_EQUAL("undefined method `write_int' for :DataStruct", ex.what()) + ASSERT(std::string(ex.what()).find("undefined method `write_int'") == 0) ); // Test readwrite attribute @@ -101,7 +101,7 @@ ASSERT_EXCEPTION_CHECK( Exception, c.call("static_string=", true), - ASSERT_EQUAL("undefined method `static_string=' for DataStruct:Class", ex.what()) + ASSERT(std::string(ex.what()).find("undefined method `static_string='") == 0) ); } @@ -127,7 +127,7 @@ { Data_Type<DataStruct> c = define_class<DataStruct>("DataStruct"); -#ifdef _MSC_VER +#ifdef _MSC_VER const char* message = "Type is not defined with Rice: class `anonymous namespace'::SomeClass"; #else const char* message = "Type is not defined with Rice: (anonymous namespace)::SomeClass"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_Keep_Alive_No_Wrapper.cpp new/test/test_Keep_Alive_No_Wrapper.cpp --- old/test/test_Keep_Alive_No_Wrapper.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/test/test_Keep_Alive_No_Wrapper.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -0,0 +1,80 @@ +#include "unittest.hpp" +#include "embed_ruby.hpp" +#include <rice/rice.hpp> +#include <rice/stl.hpp> + +using namespace Rice; + +TESTSUITE(Keep_Alive_No_Wrapper); + +namespace +{ + class Animal + { + public: + Animal(char const * name) : name_(name) {} + char const * getName() { return name_; } + virtual ~Animal() = default; + private: + char const * name_; + }; + + class Zoo + { + public: + Zoo(void) + { + pets_.push_back(new Animal("Bear")); + pets_.push_back(new Animal("Tiger")); + pets_.push_back(new Animal("Lion")); + } + + ~Zoo() + { + for(auto pet : pets_) + { + delete pet; + } + pets_.clear(); + } + + Object getPets(void) { + Array pets; + for(auto p: pets_) { + pets.push(p); + } + return pets; + } + + private: + std::vector<Animal*> pets_; + }; +} + +SETUP(Keep_Alive_No_Wrapper) +{ + embed_ruby(); +} + +TESTCASE(test_keep_alive_no_wrapper) +{ + define_class<Animal>("Animal") + .define_constructor(Constructor<Animal, char const *>()) + .define_method("get_name", &Animal::getName); + + define_class<Zoo>("Zoo") + .define_constructor(Constructor<Zoo>()) + .define_method("get_pets", &Zoo::getPets, Return().keepAlive()); + + Module m = define_module("TestingModule"); + Object zoo = m.module_eval("@zoo = Zoo.new"); + + // get_pets returns an Array (builtin type) so Return().keepAlive() + // shall result in std::runtime_error + ASSERT_EXCEPTION_CHECK( + Exception, + m.module_eval("@zoo.get_pets.each do |pet| puts pet.name; end"), + ASSERT_EQUAL("When calling the method `get_pets' we could not find the wrapper for the 'Array' return type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.", + ex.what()) + ); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_Object.cpp new/test/test_Object.cpp --- old/test/test_Object.cpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/test_Object.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -174,20 +174,29 @@ TESTCASE(call_with_keywords) { - Module kernel = Module("Kernel"); + Module m(anonymous_module()); + m.module_eval(R"( + def self.keywords_test(value, exception:) + if exception + raise "An exception!" + end + + value + end + )"); Hash keywords; keywords[":exception"] = false; - Object result = kernel.call("Integer", "charlie", keywords); - ASSERT_EQUAL(Qnil, result.value()); + Object result = m.call_kw("keywords_test", "charlie", keywords); + ASSERT_EQUAL("charlie", detail::From_Ruby<const char*>().convert(result.value())); keywords[":exception"] = true; ASSERT_EXCEPTION_CHECK( Exception, - kernel.call("Integer", "charlie", keywords), - ASSERT_EQUAL("invalid value for Integer(): \"charlie\"", ex.what()) + m.call_kw("keywords_test", "charlie", keywords), + ASSERT_EQUAL("An exception!", ex.what()) ); } @@ -240,4 +249,4 @@ Object o(INT2NUM(42)); rb_gc_start(); ASSERT_EQUAL(42, detail::From_Ruby<int>().convert(o.value())); -} \ No newline at end of file +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_Stl_SmartPointer.cpp new/test/test_Stl_SmartPointer.cpp --- old/test/test_Stl_SmartPointer.cpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/test_Stl_SmartPointer.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -109,6 +109,11 @@ define_global_function("extract_flag_unique_ptr_ref", &extractFlagUniquePtrRef); define_global_function("extract_flag_shared_ptr", &extractFlagSharedPtr); define_global_function("extract_flag_shared_ptr_ref", &extractFlagSharedPtrRef); + + define_global_function("extract_flag_shared_ptr_with_default", &extractFlagSharedPtr, + Arg("myClass") = std::make_shared<MyClass>()); + define_global_function("extract_flag_shared_ptr_ref_with_default", &extractFlagSharedPtrRef, + Arg("myClass") = std::make_shared<MyClass>()); } TESTCASE(TransferOwnership) @@ -169,6 +174,7 @@ extract_flag_unique_ptr_ref(my_class))"; Object result = m.module_eval(code); + ASSERT_EQUAL(7, detail::From_Ruby<int>().convert(result)); } TESTCASE(SharedPtrParameter) @@ -179,10 +185,11 @@ std::string code = R"(factory = Factory.new my_class = factory.share - my_class.set_flag(7) + my_class.set_flag(8) extract_flag_shared_ptr(my_class))"; - + Object result = m.module_eval(code); + ASSERT_EQUAL(8, detail::From_Ruby<int>().convert(result)); } TESTCASE(SharedPtrRefParameter) @@ -193,8 +200,41 @@ std::string code = R"(factory = Factory.new my_class = factory.share - my_class.set_flag(7) + my_class.set_flag(9) extract_flag_shared_ptr_ref(my_class))"; Object result = m.module_eval(code); -} \ No newline at end of file + ASSERT_EQUAL(9, detail::From_Ruby<int>().convert(result)); +} + +TESTCASE(SharedPtrDefaultParameter) +{ + MyClass::reset(); + + Module m = define_module("TestingModule"); + + std::string code = R"(factory = Factory.new + my_class = factory.share + my_class.set_flag(7) + extract_flag_shared_ptr_with_default())"; + + Object result = m.module_eval(code); + // The default value kicks in and ignores any previous pointer + ASSERT_EQUAL(0, detail::From_Ruby<int>().convert(result)); +} + +TESTCASE(SharedPtrRefDefaultParameter) +{ + MyClass::reset(); + + Module m = define_module("TestingModule"); + + std::string code = R"(factory = Factory.new + my_class = factory.share + my_class.set_flag(7) + extract_flag_shared_ptr_ref_with_default())"; + + Object result = m.module_eval(code); + // The default value kicks in and ignores any previous pointer + ASSERT_EQUAL(0, detail::From_Ruby<int>().convert(result)); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_Stl_String.cpp new/test/test_Stl_String.cpp --- old/test/test_Stl_String.cpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/test_Stl_String.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -27,7 +27,10 @@ Object object(value); Object encoding = object.call("encoding"); Object encodingName = encoding.call("name"); - ASSERT_EQUAL("ASCII-8BIT", detail::From_Ruby<std::string>().convert(encodingName)); + std::string result = detail::From_Ruby<std::string>().convert(encodingName); + if(result != "ASCII-8BIT" && result != "US-ASCII" && result != "UTF-8") { + FAIL("Encoding incorrect", "ASCII-8BIT, US-ASCII, or UTF-8 (Windows)", result); + } } TESTCASE(std_string_to_ruby_encoding_utf8) @@ -71,4 +74,4 @@ std::string got = detail::From_Ruby<std::string>().convert(rb_str_new("\000test", 5)); ASSERT_EQUAL(5ul, got.length()); ASSERT_EQUAL(std::string("\000test", 5), got); -} \ No newline at end of file +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_Stl_Variant.cpp new/test/test_Stl_Variant.cpp --- old/test/test_Stl_Variant.cpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/test_Stl_Variant.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -4,6 +4,7 @@ #include <rice/stl.hpp> #include <variant> +#include <complex> using namespace Rice; @@ -11,7 +12,14 @@ namespace { - using Intrinsic_Variant_T = std::variant<std::string, double, bool, int>; + using Intrinsic_Variant_T = std::variant< + std::string, + std::complex<double>, + std::vector<int>, + double, + bool, + int + >; inline std::ostream& operator<<(std::ostream& stream, Intrinsic_Variant_T const& variant) { @@ -30,6 +38,19 @@ return result; } + Intrinsic_Variant_T variantComplex() + { + using namespace std::complex_literals; + Intrinsic_Variant_T result { 1i }; + return result; + } + + Intrinsic_Variant_T variantVector() + { + Intrinsic_Variant_T result { std::vector<int>{1, 2, 3} }; + return result; + } + Intrinsic_Variant_T variantDouble() { Intrinsic_Variant_T result { 3.3 }; @@ -73,6 +94,8 @@ define_class<MyClass>("MyClass"). define_constructor(Constructor<MyClass>()). define_method("variant_string", &MyClass::variantString). + define_method("variant_complex", &MyClass::variantComplex). + define_method("variant_vector", &MyClass::variantVector). define_method("variant_double", &MyClass::variantDouble). define_method("variant_bool_true", &MyClass::variantBoolTrue). define_method("variant_bool_false", &MyClass::variantBoolFalse). @@ -83,12 +106,21 @@ TESTCASE(IntrinsicReturns) { + using namespace std::complex_literals; + Module m = define_module("Testing"); Object myClass = m.module_eval("MyClass.new"); Object result = myClass.call("variant_string"); ASSERT_EQUAL("a string", detail::From_Ruby<std::string>().convert(result)); - + + result = myClass.call("variant_complex"); + ASSERT_EQUAL(1i, detail::From_Ruby<std::complex<double>>().convert(result)); + + result = myClass.call("variant_vector"); + std::vector<int> converted = detail::From_Ruby<std::vector<int>>().convert(result); + ASSERT_EQUAL(3, converted.size()); + result = myClass.call("variant_double"); ASSERT_EQUAL(3.3, detail::From_Ruby<double>().convert(result)); @@ -104,6 +136,8 @@ TESTCASE(IntrinsicRoundtrip) { + using namespace std::complex_literals; + Module m = define_module("Testing"); Object myClass = m.module_eval("MyClass.new"); @@ -113,6 +147,17 @@ ASSERT_EQUAL("roundtrip string", detail::From_Ruby<std::string>().convert(result)); code = R"(my_class = MyClass.new + my_class.roundtrip(Complex(2, 3)))"; + result = m.module_eval(code); + ASSERT_EQUAL((2.0 + 3i), detail::From_Ruby<std::complex<double>>().convert(result)); + + code = R"(my_class = MyClass.new + my_class.roundtrip([1, 2, 3]))"; + result = m.module_eval(code); + std::vector<int> expected = {1, 2, 3}; + ASSERT_EQUAL(expected, detail::From_Ruby<std::vector<int>>().convert(result)); + + code = R"(my_class = MyClass.new my_class.roundtrip(44.4))"; result = m.module_eval(code); ASSERT_EQUAL(44.4, detail::From_Ruby<double>().convert(result)); @@ -150,7 +195,7 @@ ASSERT_EQUAL(77.7, detail::From_Ruby<double>().convert(result)); result = myClass.call("variant_attr"); ASSERT_EQUAL(77.7, detail::From_Ruby<double>().convert(result)); - + result = myClass.call("variant_attr=", true); ASSERT(detail::From_Ruby<bool>().convert(result)); result = myClass.call("variant_attr"); @@ -298,4 +343,4 @@ hello = instance2.call("say_hello"); ASSERT_EQUAL("Hi from MyClass2", detail::From_Ruby<std::string>().convert(hello)); } -#endif \ No newline at end of file +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_To_From_Ruby.cpp new/test/test_To_From_Ruby.cpp --- old/test/test_To_From_Ruby.cpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/test_To_From_Ruby.cpp 2024-01-20 14:53:39.000000000 +0100 @@ -229,7 +229,7 @@ ASSERT_EXCEPTION_CHECK( Exception, detail::From_Ruby<unsigned long long>().convert(rb_str_new2("bad value")), - ASSERT_EQUAL("no implicit conversion from string", ex.what()) + ASSERT(std::string(ex.what()).find("no implicit conversion") == 0) ); } @@ -396,4 +396,4 @@ detail::From_Ruby<const char*>().convert(rb_float_new(11.11)), ASSERT_EQUAL("wrong argument type Float (expected String)", ex.what()) ); -} \ No newline at end of file +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/unittest.hpp new/test/unittest.hpp --- old/test/unittest.hpp 2023-04-23 02:19:48.000000000 +0200 +++ new/test/unittest.hpp 2024-01-20 14:53:39.000000000 +0100 @@ -236,7 +236,7 @@ if constexpr (is_streamable<std::stringstream, T>::value && is_streamable<std::stringstream, U>::value) { - strm << s_t << " != " << s_u; + strm << s_t << " != " << s_u << " (" << u << ") "; } strm << " at " << file << ":" << line; throw Assertion_Failed(strm.str()); @@ -263,6 +263,14 @@ } } +#define FAIL(message, expect, got) \ + do \ + { \ + std::stringstream strm; \ + strm << message << " expected: " << (expect) << " got: " << (got); \ + throw Assertion_Failed(strm.str()); \ + } while(0) + #define ASSERT_EQUAL(x, y) \ do \ { \