From 83a389d3fbccbd588f3a649c4a736feeae617341 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Wed, 12 Apr 2017 19:08:23 +0100 Subject: [PATCH] Add support for pointers to shared_ptr and null shared_ptr in Ruby containers Upcasting of pointers to shared_ptr would need some more fundamental changes, but not done yet ... pointers to shared_ptr are not common. --- Examples/test-suite/cpp11_shared_ptr_upcast.i | 70 +++++++++++++-- .../ruby/cpp11_shared_ptr_upcast_runme.rb | 37 ++++++++ Lib/ruby/std_shared_ptr.i | 85 +++++++++++++------ 3 files changed, 160 insertions(+), 32 deletions(-) diff --git a/Examples/test-suite/cpp11_shared_ptr_upcast.i b/Examples/test-suite/cpp11_shared_ptr_upcast.i index 9f65eb7e1..3427829d9 100644 --- a/Examples/test-suite/cpp11_shared_ptr_upcast.i +++ b/Examples/test-suite/cpp11_shared_ptr_upcast.i @@ -36,27 +36,27 @@ typedef std::shared_ptr DerivedPtr; // non-overloaded int derived_num1(DerivedPtr v) { - return (*v).get_n(); + return v == nullptr ? 999 : (*v).get_n(); } int derived_num2(std::vector v) { - return (*v[0]).get_n(); + return v[0] == nullptr ? 999 : (*v[0]).get_n(); } int derived_num3(std::map v) { - return (*v[0]).get_n(); + return v[0] == nullptr ? 999 : (*v[0]).get_n(); } int base_num1(BasePtr v) { - return (*v).get_m(); + return v == nullptr ? 999 : (*v).get_m(); } int base_num2(std::vector v) { - return (*v[0]).get_m(); + return v[0] == nullptr ? 999 : (*v[0]).get_m(); } int base_num3(std::map v) { - return (*v[0]).get_m(); + return v[0] == nullptr ? 999 : (*v[0]).get_m(); } // overloaded @@ -128,3 +128,61 @@ int derived_num(std::map v); int base_num(BasePtr); int base_num(std::vector > v); int base_num(std::map v); + +// ptr to shared_ptr +%shared_ptr(Base2); +%shared_ptr(Derived2) + +%inline %{ +class Base2 { +public: + Base2() : m(-1) {} + Base2(int i) : m(i) {} + int get_m() { return m; } + int m; +}; + + +class Derived2 : public Base2 { +public: + Derived2() : n(0) {} + Derived2(int i) : n(i) {} + int get_n_2() { return n; } + int n; +}; +%} + +%template(Base2List) std::vector * >; +%template(Base2Map) std::map * >; + +%template(Derived2List) std::vector * >; +%template(Derived2Map) std::map * >; + +%inline %{ +typedef std::shared_ptr * Derived2Ptr; +typedef std::shared_ptr * Base2Ptr; + +int base2_num1(Base2Ptr v) { + return v == nullptr ? 999 : *v == nullptr ? 888 : (*v)->get_m(); +} + +int base2_num2(std::vector v) { + return v[0] == nullptr ? 999 : *v[0] == nullptr ? 888 : (*v[0])->get_m(); +} + +int base2_num3(std::map v) { + return v[0] == nullptr ? 999 : *v[0] == nullptr ? 888 : (*v[0])->get_m(); +} + +int derived2_num1(Derived2Ptr v) { + return v == nullptr ? 999 : *v == nullptr ? 888 : (*v)->get_n_2(); +} + +int derived2_num2(std::vector v) { + return v[0] == nullptr ? 999 : *v[0] == nullptr ? 888 : (*v[0])->get_n_2(); +} + +int derived2_num3(std::map v) { + return v[0] == nullptr ? 999 : *v[0] == nullptr ? 888 : (*v[0])->get_n_2(); +} +%} diff --git a/Examples/test-suite/ruby/cpp11_shared_ptr_upcast_runme.rb b/Examples/test-suite/ruby/cpp11_shared_ptr_upcast_runme.rb index ef83ab367..864be1382 100644 --- a/Examples/test-suite/ruby/cpp11_shared_ptr_upcast_runme.rb +++ b/Examples/test-suite/ruby/cpp11_shared_ptr_upcast_runme.rb @@ -13,6 +13,14 @@ simple_assert_equal(-1, base_num1(Derived.new(7)) ) simple_assert_equal(-1, base_num2([Derived.new(7)]) ) simple_assert_equal(-1, base_num3({0 => Derived.new(7)}) ) +simple_assert_equal( 999, derived_num1(nil) ) +simple_assert_equal( 999, derived_num2([nil]) ) +simple_assert_equal( 999, derived_num3({0 => nil}) ) + +simple_assert_equal( 999, base_num1(nil) ) +simple_assert_equal( 999, base_num2([nil]) ) +simple_assert_equal( 999, base_num3({0 => nil}) ) + # overloaded simple_assert_equal( 7, derived_num(Derived.new(7)) ) simple_assert_equal( 7, derived_num([Derived.new(7)]) ) @@ -21,3 +29,32 @@ simple_assert_equal( 7, derived_num({0 => Derived.new(7)}) ) simple_assert_equal(-1, base_num(Derived.new(7)) ) simple_assert_equal(-1, base_num([Derived.new(7)]) ) simple_assert_equal(-1, base_num({0 => Derived.new(7)}) ) + +# ptr to shared_ptr +simple_assert_equal( 7, derived2_num1(Derived2.new(7)) ) +simple_assert_equal( 7, derived2_num2([Derived2.new(7)]) ) +simple_assert_equal( 7, derived2_num3({0 => Derived2.new(7)}) ) + +simple_assert_equal( -1, base2_num1(Derived2.new(7)) ) + +begin + # Upcast for pointers to shared_ptr in this generic framework has not been implemented + simple_assert_equal( -1, base2_num2([Derived2.new(7)]) ) + raise RuntimeError, "Failed to catch TypeError" +rescue TypeError +end +begin + # Upcast for pointers to shared_ptr in this generic framework has not been implemented + simple_assert_equal( -1, base2_num3({0 => Derived2.new(7)}) ) + raise RuntimeError, "Failed to catch TypeError" +rescue TypeError +end + +simple_assert_equal( 888, derived2_num1(nil) ) +simple_assert_equal( 999, derived2_num2([nil]) ) # although 888 would be more consistent +simple_assert_equal( 999, derived2_num3({0 => nil}) ) # although 888 would be more consistent + +simple_assert_equal( 888, base2_num1(nil) ) +simple_assert_equal( 999, base2_num2([nil]) ) # although 888 would be more consistent +simple_assert_equal( 999, base2_num3({0 => nil}) ) # although 888 would be more consistent + diff --git a/Lib/ruby/std_shared_ptr.i b/Lib/ruby/std_shared_ptr.i index 5d56b8750..4e0798d56 100644 --- a/Lib/ruby/std_shared_ptr.i +++ b/Lib/ruby/std_shared_ptr.i @@ -7,23 +7,32 @@ { namespace swig { /* - template specialization for functions defined in rubystdcommon.swg. - here we should treat smart pointers in a way different from the way we treat raw pointers. + Template specialization for functions defined in rubystdcommon.swg. Special handling for shared_ptr + is required as, shared_ptr * is used rather than the usual T *, see shared_ptr.i. */ template struct traits_asptr > { static int asptr(VALUE obj, std::shared_ptr **val) { - std::shared_ptr *p=0; + std::shared_ptr *p = 0; swig_type_info *descriptor = type_info >(); swig_ruby_owntype newmem = {0, 0}; int res = descriptor ? SWIG_ConvertPtrAndOwn(obj, (void **)&p, descriptor, 0, &newmem) : SWIG_ERROR; - if (SWIG_IsOK(res) && p) { - if (val && *val) **val = *p; - if (newmem.own & SWIG_CAST_NEW_MEMORY) delete p; - return SWIG_OK; - } else { - return SWIG_ERROR; + if (SWIG_IsOK(res)) { + if (val) { + if (*val) { + **val = p ? *p : std::shared_ptr(); + } else { + *val = p; + if (newmem.own & SWIG_CAST_NEW_MEMORY) { + // Upcast for pointers to shared_ptr in this generic framework has not been implemented + res = SWIG_ERROR; + } + } + } + if (newmem.own & SWIG_CAST_NEW_MEMORY) + delete p; } + return res; } }; @@ -31,12 +40,13 @@ namespace swig { struct traits_asval > { static int asval(VALUE obj, std::shared_ptr *val) { if (val) { - std::shared_ptr ret; - std::shared_ptr *p=&ret; + std::shared_ptr ret; + std::shared_ptr *p = &ret; int res = traits_asptr >::asptr(obj, &p); - if (!SWIG_IsOK(res)) return res; - if (val) *val = ret; - return SWIG_OK; + if (!SWIG_IsOK(res)) + return res; + *val = ret; + return SWIG_OK; } else { return traits_asptr >::asptr(obj, (std::shared_ptr **)(0)); } @@ -44,17 +54,24 @@ namespace swig { }; template - struct traits_asval*> { + struct traits_asval *> { static int asval(VALUE obj, std::shared_ptr **val) { - if (val && *val) { - typedef typename noconst_traits >::noconst_type noconst_type; - noconst_type ret; - noconst_type *p = &ret; - int res = traits_asptr::asptr(obj, &p); - if (SWIG_IsOK(res)) { - **(const_cast(val)) = ret; + if (val) { + typedef typename noconst_traits >::noconst_type noconst_type; + if (*val) { + noconst_type ret; + noconst_type *p = &ret; + int res = traits_asptr::asptr(obj, &p); + if (SWIG_IsOK(res)) + **(const_cast(val)) = ret; + return res; + } else { + noconst_type *p = 0; + int res = traits_asptr::asptr(obj, &p); + if (SWIG_IsOK(res)) + *val = p; + return res; } - return res; } else { return traits_asptr >::asptr(obj, (std::shared_ptr **)(0)); } @@ -73,16 +90,32 @@ namespace swig { // Uninitialized return value, no Type() constructor required. if (throw_error) throw std::invalid_argument("bad type"); VALUE lastErr = rb_gv_get("$!"); - if (lastErr == Qnil) { + if (lastErr == Qnil) SWIG_Error(SWIG_TypeError, swig::type_name >()); - } - static std::shared_ptr *v_def = (std::shared_ptr*) malloc(sizeof(std::shared_ptr)); + static std::shared_ptr *v_def = (std::shared_ptr *) malloc(sizeof(std::shared_ptr)); memset(v_def,0,sizeof(std::shared_ptr)); return *v_def; } } }; + template + struct traits_as *, pointer_category> { + static std::shared_ptr * as(VALUE obj, bool throw_error) { + std::shared_ptr *p = 0; + int res = traits_asptr >::asptr(obj, &p); + if (SWIG_IsOK(res)) { + return p; + } else { + if (throw_error) throw std::invalid_argument("bad type"); + VALUE lastErr = rb_gv_get("$!"); + if (lastErr == Qnil) + SWIG_Error(SWIG_TypeError, swig::type_name *>()); + return 0; + } + } + }; + /* we have to remove the const qualifier to work around a BUG SWIG_TypeQuery("std::shared_ptr") == NULL,