diff --git a/CHANGES.current b/CHANGES.current index 1e06bd85b..6584e1781 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,39 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.1.0 (in progress) =========================== +2022-06-29: wsfulton + #999 #1044 Enhance SWIGTYPE "out" typemaps to use std::move when copying + objects, thereby making use of move semantics when wrapping a function returning + by value if the returned type supports move semantics. + + Wrapping functions that return move only types 'by value' now work out the box + without having to provide custom typemaps. + + The implementation removed all casts in the "out" typemaps to allow the compiler to + appropriately choose calling a move constructor, where possible, otherwise a copy + constructor. The implementation alsoand required modifying SwigValueWrapper to + change a cast operator from: + + SwigValueWrapper::operator T&() const; + + to + + #if __cplusplus >=201103L + SwigValueWrapper::operator T&&() const; + #else + SwigValueWrapper::operator T&() const; + #endif + + This is not backwards compatible for C++11 and later when using the valuewrapper feature + if a cast is explicitly being made in user supplied "out" typemaps. Suggested change + in custom "out" typemaps for C++11 and later code: + + 1. Try remove the cast altogether to let the compiler use an appropriate implicit cast. + 2. Change the cast, for example, from static_cast to static_cast, using the + __cplusplus macro if all versions of C++ need to be supported. + + *** POTENTIAL INCOMPATIBILITY *** + 2022-06-15: wsfulton #2039 Add move assignment operator to SwigValueWrapper used by the valuewrapper feature. diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html index 86a35bfaa..b98713961 100644 --- a/Doc/Manual/CPlusPlus11.html +++ b/Doc/Manual/CPlusPlus11.html @@ -15,6 +15,9 @@
  • Core language changes
    • Rvalue reference and move semantics +
    • Generalized constant expressions
    • Extern template
    • Initializer lists @@ -123,6 +126,82 @@ example.i:18: Warning 503: Can't wrap 'operator =' unless renamed to a valid ide +

      7.2.1.1 Movable and move-only types

      + + +

      +SWIG has traditionally relied on wrapped C++ types to be copy constructible or copy assignable, either via an explicit or implicit copy constructor and copy assignment operator. +Prior to C++11, a function could not return nor take a type by value that was not copyable. +In C++11 this is no longer the case. A type can also be movable if it has has a move constructor and a move assignment operator. +A move-only type is movable but not copyable; it has both the copy constructor and copy assignment operator deleted. +Movable types can appear in function signatures for passing 'by value' and in C++11 the object can then be moved rather than copied. +

      + +

      +SWIG has been enhanced with support for both copyable and/or movable types but this is currently just for function return values. +

      + +

      +The support for function return values is generically implemented in the "out" SWIGTYPE typemap which supports any type, including copyable, movable and move-only types. +The typemap code is very simple and written so that the compiler will call the move constructor if possible, +otherwise the copy constructor: +

      + +
      +%typemap(out) SWIGTYPE %{
      +  $result = new $1_ltype($1);
      +%}
      +
      + +

      +The above typemap is for C# and when used to wrap a move-only type such as: +

      + +
      +struct MoveOnly {
      +  int  val;
      +  MoveOnly(): val(0)  {}
      +
      +  MoveOnly(const MoveOnly &) = delete;
      +  MoveOnly(MoveOnly &&) = default;
      +
      +  MoveOnly & operator=(const MoveOnly &) = delete;
      +  MoveOnly & operator=(MoveOnly &&) = default;
      +
      +  static MoveOnly create() { return MoveOnly(); }
      +};
      +
      + +

      +will generate wrapper code for the create factory method: +

      + +
      +SWIGEXPORT void * SWIGSTDCALL CSharp_MoveOnly_create() {
      +  void * jresult ;
      +  SwigValueWrapper< MoveOnly > result;
      +
      +  result = MoveOnly::create();
      +  jresult = new MoveOnly(result);
      +  return jresult;
      +}
      +
      + +

      +SwigValueWrapper is covered in Pass and return by value. +Note that the generated code could be optimised further using the "optimal" attribute in the "out" typemap. +

      + +

      +There is currently only partial support for move-only types as +support for move-only types used as a parameter in a function, that are passed 'by value', is not yet available. +

      + +

      +Compatibility note: +SWIG-4.1.0 introduced support for taking advantage of types with move semantics and wrapping functions that return movable or move-only types 'by value'. +

      +

      7.2.2 Generalized constant expressions

      diff --git a/Doc/Manual/Contents.html b/Doc/Manual/Contents.html index 6f48360fd..ef87cf78d 100644 --- a/Doc/Manual/Contents.html +++ b/Doc/Manual/Contents.html @@ -297,6 +297,9 @@
    • Core language changes
      • Rvalue reference and move semantics +
      • Generalized constant expressions
      • Extern template
      • Initializer lists diff --git a/Doc/Manual/SWIGPlus.html b/Doc/Manual/SWIGPlus.html index 4cefdc365..820c5088b 100644 --- a/Doc/Manual/SWIGPlus.html +++ b/Doc/Manual/SWIGPlus.html @@ -584,7 +584,7 @@ automatically generate a wrapper for one.
      • If a C++ class does not declare an explicit copy constructor, SWIG will -automatically generate a wrapper for one if the %copyctor is used. +automatically generate a wrapper for one if %copyctor is used.
      • @@ -1352,16 +1352,19 @@ following:
         Vector *wrap_cross_product(Vector *a, Vector *b) {
        -  Vector x = *a;
        -  Vector y = *b;
        -  Vector r = cross_product(x, y);
        +  Vector x;
        +  Vector y;
        +  Vector r;
        +  x = *a;
        +  y = *b;
        +  r = cross_product(x, y);
           return new Vector(r);
         }

        -In order for the wrapper code to compile, Vector must define a copy constructor and a -default constructor. +In order for the wrapper code to compile, Vector must define a default constructor, copy assignment operator (and/or a move assignment operator for C++11 and later). +The Movable and move-only types section should be read regarding C++11 move semantics and return by value.

        @@ -1374,9 +1377,12 @@ called the "Fulton Transform". This produces a wrapper that looks like this:

         Vector cross_product(Vector *a, Vector *b) {
        -  SwigValueWrapper<Vector> x = *a;
        -  SwigValueWrapper<Vector> y = *b;
        -  SwigValueWrapper<Vector> r = cross_product(x, y);
        +  SwigValueWrapper<Vector> x;
        +  SwigValueWrapper<Vector> y;
        +  SwigValueWrapper<Vector> r;
        +  x = *a;
        +  y = *b;
        +  r = cross_product(x, y);
           return new Vector(r);
         }
         
        diff --git a/Doc/Manual/Typemaps.html b/Doc/Manual/Typemaps.html index 28f9d51ad..24a68b1ba 100644 --- a/Doc/Manual/Typemaps.html +++ b/Doc/Manual/Typemaps.html @@ -3364,7 +3364,7 @@ Consider running the following code through SWIG:
         %typemap(out) SWIGTYPE %{
        -  $result = new $1_ltype((const $1_ltype &)$1);
        +  $result = new $1_ltype($1);
         %}
         
         %inline %{
        @@ -3414,7 +3414,7 @@ If the typemap code is kept the same and just the "optimal" attribute specified
         
         %typemap(out, optimal="1") SWIGTYPE %{
        -  $result = new $1_ltype((const $1_ltype &)$1);
        +  $result = new $1_ltype($1);
         %}
         
        @@ -3441,7 +3441,7 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_XX_create() { void * jresult ; XX result; result = XX::create(); - jresult = new XX((const XX &)result); + jresult = new XX(result); return jresult; } @@ -3456,7 +3456,7 @@ With the "optimal" attribute, the code is:
         SWIGEXPORT void * SWIGSTDCALL CSharp_XX_create() {
           void * jresult ;
        -  jresult = new XX((const XX &)XX::create());
        +  jresult = new XX(XX::create());
           return jresult;
         }
         
        @@ -3513,7 +3513,7 @@ It should be clear that the above code cannot be used as the argument to the cop

        -Secondly, if the typemaps uses $1 more than once, then multiple calls to the wrapped function +Secondly, if the typemap uses $1 more than once, then multiple calls to the wrapped function will be made. Obviously that is not very optimal. In fact SWIG attempts to detect this and will issue a warning something like:

        diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 003138010..a640febc1 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -597,6 +597,7 @@ CPP11_TEST_CASES += \ cpp11_initializer_list \ cpp11_initializer_list_extend \ cpp11_lambda_functions \ + cpp11_move_only \ cpp11_move_only_valuewrapper \ cpp11_noexcept \ cpp11_null_pointer_constant \ diff --git a/Examples/test-suite/cpp11_move_only.i b/Examples/test-suite/cpp11_move_only.i new file mode 100644 index 000000000..7c91933c4 --- /dev/null +++ b/Examples/test-suite/cpp11_move_only.i @@ -0,0 +1,52 @@ +%module cpp11_move_only + +#if defined(SWIGD) +%rename(trace) debug; +#endif + +%include "cpp11_move_only_helper.i" + +%ignore MoveOnly::operator=; +//%valuewrapper MoveOnly; // SWIG sets %valuewrapper by default for move-only types + +%inline %{ +#include +using namespace std; + +bool debug = false; + +struct MoveOnly { + MoveOnly(int i = 0) { if (debug) cout << "MoveOnly(" << i << ")" << " " << this << endl; Counter::normal_constructor++; } + + MoveOnly(const MoveOnly &other) = delete; + MoveOnly & operator=(const MoveOnly &other) = delete; + + MoveOnly(MoveOnly &&other) noexcept { if (debug) cout << "MoveOnly(MoveOnly &&)" << " " << this << endl; Counter::move_constructor++; } + MoveOnly & operator=(MoveOnly &&other) noexcept { if (debug) cout << "operator=(MoveOnly &&)" << " " << this << endl; Counter::move_assignment++; return *this; } + ~MoveOnly() { if (debug) cout << "~MoveOnly()" << " " << this << endl; Counter::destructor++; } + + static MoveOnly create() { return MoveOnly(111); } + // static const MoveOnly createConst() { return MoveOnly(111); } // not supported by default +}; +%} + +%ignore MovableCopyable::operator=; +%ignore MovableCopyable::MovableCopyable(MovableCopyable &&); +// %valuewrapper MovableCopyable; // SWIG does not use valuewrapper by default for copyable types with a default constructor + +%inline %{ +// Movable and Copyable +struct MovableCopyable { + MovableCopyable(int i = 0) { if (debug) cout << "MovableCopyable(" << i << ")" << " " << this << endl; Counter::normal_constructor++; } + + MovableCopyable(const MovableCopyable &other) { if (debug) cout << "MovableCopyable(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_constructor++;} + MovableCopyable & operator=(const MovableCopyable &other) { if (debug) cout << "operator=(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_assignment++; return *this; } + + MovableCopyable(MovableCopyable &&other) noexcept { if (debug) cout << "MovableCopyable(MovableCopyable &&)" << " " << this << endl; Counter::move_constructor++; } + MovableCopyable & operator=(MovableCopyable &&other) noexcept { if (debug) cout << "operator=(MovableCopyable &&)" << " " << this << endl; Counter::move_assignment++; return *this; } + ~MovableCopyable() { if (debug) cout << "~MovableCopyable()" << " " << this << endl; Counter::destructor++; } + + static MovableCopyable create() { return MovableCopyable(111); } + static const MovableCopyable createConst() { return MovableCopyable(111); } +}; +%} diff --git a/Examples/test-suite/cpp11_move_only_helper.i b/Examples/test-suite/cpp11_move_only_helper.i new file mode 100644 index 000000000..89fc1509b --- /dev/null +++ b/Examples/test-suite/cpp11_move_only_helper.i @@ -0,0 +1,71 @@ +// Helper interface for cpp11_move_only.i and others + +%include +%catches(std::string) Counter::check_counts; + +%inline %{ +#include +using namespace std; + + +struct Counter { + static int normal_constructor; + static int copy_constructor; + static int copy_assignment; + static int move_constructor; + static int move_assignment; + static int destructor; + static void reset_counts() { + normal_constructor = 0; + copy_constructor = 0; + copy_assignment = 0; + move_constructor = 0; + move_assignment = 0; + destructor = 0; + } + // Check against expected counts of constructor, assignment operators etc. + // Not observed during development, but compiler optimisation could change the expected values. + // Throws exception if not correct (use %catches to catch them) + static void check_counts( + int normal_constructor, + int copy_constructor, + int copy_assignment, + int move_constructor, + int move_assignment, + int destructor) { + bool match = ( + normal_constructor == Counter::normal_constructor && + copy_constructor == Counter::copy_constructor && + copy_assignment == Counter::copy_assignment && + move_constructor == Counter::move_constructor && + move_assignment == Counter::move_assignment && + destructor == Counter::destructor); + if (!match) { + std::stringstream ss; + ss << "check_counts failed" << std::endl << + Counter::normal_constructor << " " << + Counter::copy_constructor << " " << + Counter::copy_assignment << " " << + Counter::move_constructor << " " << + Counter::move_assignment << " " << + Counter::destructor << " " << + " (actual)" << std::endl << + normal_constructor << " " << + copy_constructor << " " << + copy_assignment << " " << + move_constructor << " " << + move_assignment << " " << + destructor << " " << + " (expected)" << std::endl; + throw ss.str(); + } + } +}; + +int Counter::normal_constructor = 0; +int Counter::copy_constructor = 0; +int Counter::copy_assignment = 0; +int Counter::move_constructor = 0; +int Counter::move_assignment = 0; +int Counter::destructor = 0; +%} diff --git a/Examples/test-suite/cpp11_move_only_valuewrapper.i b/Examples/test-suite/cpp11_move_only_valuewrapper.i index fb9f5dc31..bde95f014 100644 --- a/Examples/test-suite/cpp11_move_only_valuewrapper.i +++ b/Examples/test-suite/cpp11_move_only_valuewrapper.i @@ -37,82 +37,28 @@ namespace std { #endif %} -%include +#if defined(SWIGD) +%rename(trace) debug; +#endif + +%include "cpp11_move_only_helper.i" %valuewrapper XXX; %ignore XXX::operator=; -%catches(std::string) XXX::check_counts; %inline %{ bool debug = false; struct XXX { - XXX(int i = 0) { if (debug) cout << "XXX(" << i << ")" << " " << this << endl; normal_constructor_count++; } - XXX(const XXX &other) { if (debug) cout << "XXX(const XXX &)" << " " << this << " " << &other << endl; copy_constructor_count++;} - XXX & operator=(const XXX &other) { if (debug) cout << "operator=(const XXX &)" << " " << this << " " << &other << endl; copy_assignment_count++; return *this; } + XXX(int i = 0) { if (debug) cout << "XXX(" << i << ")" << " " << this << endl; Counter::normal_constructor++; } + XXX(const XXX &other) { if (debug) cout << "XXX(const XXX &)" << " " << this << " " << &other << endl; Counter::copy_constructor++;} + XXX & operator=(const XXX &other) { if (debug) cout << "operator=(const XXX &)" << " " << this << " " << &other << endl; Counter::copy_assignment++; return *this; } #if defined(__cplusplus) && __cplusplus >= 201103L - XXX(XXX &&other) noexcept { if (debug) cout << "XXX(XXX &&)" << " " << this << endl; move_constructor_count++; } - XXX & operator=(XXX &&other) noexcept { if (debug) cout << "operator=(XXX &&)" << " " << this << endl; move_assignment_count++; return *this; } + XXX(XXX &&other) noexcept { if (debug) cout << "XXX(XXX &&)" << " " << this << endl; Counter::move_constructor++; } + XXX & operator=(XXX &&other) noexcept { if (debug) cout << "operator=(XXX &&)" << " " << this << endl; Counter::move_assignment++; return *this; } #endif - ~XXX() { if (debug) cout << "~XXX()" << " " << this << endl; destructor_count++; } - static int normal_constructor_count; - static int copy_constructor_count; - static int copy_assignment_count; - static int move_constructor_count; - static int move_assignment_count; - static int destructor_count; - static void reset_counts() { - XXX::normal_constructor_count = 0; - XXX::copy_constructor_count = 0; - XXX::copy_assignment_count = 0; - XXX::move_constructor_count = 0; - XXX::move_assignment_count = 0; - XXX::destructor_count = 0; - } - // Check against expected counts of constructor, assignment operators etc. - // Not observed during development, but compiler optimisation could change the expected values. - // Throws exception if not correct (use %catches to catch them) - static void check_counts( - int normal_constructor_count, - int copy_constructor_count, - int copy_assignment_count, - int move_constructor_count, - int move_assignment_count, - int destructor_count) { - bool match = ( - normal_constructor_count == XXX::normal_constructor_count && - copy_constructor_count == XXX::copy_constructor_count && - copy_assignment_count == XXX::copy_assignment_count && - move_constructor_count == XXX::move_constructor_count && - move_assignment_count == XXX::move_assignment_count && - destructor_count == XXX::destructor_count); - if (!match) { - std::stringstream ss; - ss << "check_counts failed" << std::endl << - XXX::normal_constructor_count << " " << - XXX::copy_constructor_count << " " << - XXX::copy_assignment_count << " " << - XXX::move_constructor_count << " " << - XXX::move_assignment_count << " " << - XXX::destructor_count << " " << - " (actual)" << std::endl << - normal_constructor_count << " " << - copy_constructor_count << " " << - copy_assignment_count << " " << - move_constructor_count << " " << - move_assignment_count << " " << - destructor_count << " " << - " (expected)" << std::endl; - throw ss.str(); - } - } + ~XXX() { if (debug) cout << "~XXX()" << " " << this << endl; Counter::destructor++; } }; -int XXX::normal_constructor_count = 0; -int XXX::copy_constructor_count = 0; -int XXX::copy_assignment_count = 0; -int XXX::move_constructor_count = 0; -int XXX::move_assignment_count = 0; -int XXX::destructor_count = 0; bool has_cplusplus11() { #if __cplusplus >= 201103L return true; @@ -122,20 +68,8 @@ bool has_cplusplus11() { } %} -#if defined(SWIGCSHARP) -%typemap(out) std::unique_ptr %{ - if (debug) cout << "out start" << endl; -#if __cplusplus >= 201103L - $result = new std::unique_ptr(std::move(static_cast&>($1))); -#else - $result = new std::unique_ptr((const std::unique_ptr &)$1); -#endif - if (debug) cout << "out done" << endl; -%} - std::unique_ptr makeUniqueXXX(); void cleanup(std::unique_ptr* p); -#endif %{ std::unique_ptr makeUniqueXXX() { @@ -145,6 +79,7 @@ std::unique_ptr makeUniqueXXX() { void cleanup(std::unique_ptr* p) { delete p; } +typedef XXX UUU; %} %inline %{ @@ -152,6 +87,16 @@ XXX createXXX() { if (debug) cout << "createXXX()" << endl; return XXX(111); } +XXX createXXX2() { + if (debug) cout << "createXXX2()" << endl; + return XXX(222); +} +UUU createUnknownType() { + if (debug) cout << "createXXX2()" << endl; + return XXX(222); +} +struct YYY {}; +void inputByValue(UUU uuu, XXX xxx, YYY yyy) {} %} @@ -165,32 +110,32 @@ XXX createXXX() { %inline %{ // 'unit tests' for SwigValueWrapper void test1() { - XXX::reset_counts(); + Counter::reset_counts(); { SwigValueWrapper x; x = XXX(); } #if __cplusplus >= 201103L - XXX::check_counts(1, 0, 0, 1, 0, 2); // was same as < c++11 counts below before move assignment operator added to SwigValueWrapper + Counter::check_counts(1, 0, 0, 1, 0, 2); // was same as < c++11 counts below before move assignment operator added to SwigValueWrapper #else - XXX::check_counts(1, 1, 0, 0, 0, 2); + Counter::check_counts(1, 1, 0, 0, 0, 2); #endif } void test2() { - XXX::reset_counts(); + Counter::reset_counts(); { SwigValueWrapper x; x = XXX(); x = XXX(); } #if __cplusplus >= 201103L - XXX::check_counts(2, 0, 0, 2, 0, 4); + Counter::check_counts(2, 0, 0, 2, 0, 4); #else - XXX::check_counts(2, 2, 0, 0, 0, 4); + Counter::check_counts(2, 2, 0, 0, 0, 4); #endif } void test3() { - XXX::reset_counts(); + Counter::reset_counts(); { SwigValueWrapper x; XXX a(999); @@ -199,41 +144,41 @@ void test3() { #endif } #if __cplusplus >= 201103L - XXX::check_counts(1, 0, 0, 1, 0, 2); + Counter::check_counts(1, 0, 0, 1, 0, 2); #endif } void test4() { - XXX::reset_counts(); + Counter::reset_counts(); { SwigValueWrapper > x; x = std::unique_ptr(new XXX(444)); } - XXX::check_counts(1, 0, 0, 0, 0, 1); + Counter::check_counts(1, 0, 0, 0, 0, 1); } void test5() { #if __cplusplus >= 201103L - XXX::reset_counts(); + Counter::reset_counts(); { SwigValueWrapper > x; x = std::unique_ptr(new XXX(550)); std::unique_ptr x2(new XXX(555)); x = std::move(x2); } - XXX::check_counts(2, 0, 0, 0, 0, 2); + Counter::check_counts(2, 0, 0, 0, 0, 2); #endif } void test6() { #if __cplusplus >= 201103L - XXX::reset_counts(); + Counter::reset_counts(); { // emulates how std::unique_ptr typemaps could be wrapped with SwigValueWrapper void *ptr = 0; SwigValueWrapper > x; // SWIG generated if std::unique_ptr<> definition not parsed x = makeUniqueXXX(); // SWIG generated code wrapping function returning std::unique_ptr - ptr = new std::unique_ptr(std::move((std::unique_ptr&)x)); // 'out' typemap (move std::unique_ptr from stack to the heap), note non-const cast std::unique_tr& + ptr = new std::unique_ptr(x); // 'out' typemap (move std::unique_ptr from stack to the heap) delete (std::unique_ptr *)ptr; // Final cleanup (user needs to call this) } - XXX::check_counts(1, 0, 0, 0, 0, 1); + Counter::check_counts(1, 0, 0, 0, 0, 1); #endif } %} diff --git a/Examples/test-suite/csharp/cpp11_move_only_runme.cs b/Examples/test-suite/csharp/cpp11_move_only_runme.cs new file mode 100644 index 000000000..57cb7aa18 --- /dev/null +++ b/Examples/test-suite/csharp/cpp11_move_only_runme.cs @@ -0,0 +1,23 @@ +using System; +using cpp11_move_onlyNamespace; + +public class cpp11_move_only_runme { + + public static void Main() { + Counter.reset_counts(); + using (MoveOnly mo = MoveOnly.create()) { + } + Counter.check_counts(1, 0, 0, 2, 0, 3); + + Counter.reset_counts(); + using (MovableCopyable mo = MovableCopyable.create()) { + } + Counter.check_counts(2, 1, 0, 0, 1, 3); + + // Move semantics not used + Counter.reset_counts(); + using (MovableCopyable mo = MovableCopyable.createConst()) { + } + Counter.check_counts(2, 1, 1, 0, 0, 3); + } +} diff --git a/Examples/test-suite/csharp/cpp11_move_only_valuewrapper_runme.cs b/Examples/test-suite/csharp/cpp11_move_only_valuewrapper_runme.cs index 69f788699..d6a42a3f7 100644 --- a/Examples/test-suite/csharp/cpp11_move_only_valuewrapper_runme.cs +++ b/Examples/test-suite/csharp/cpp11_move_only_valuewrapper_runme.cs @@ -4,11 +4,18 @@ using cpp11_move_only_valuewrapperNamespace; public class cpp11_move_only_valuewrapper_runme { public static void Main() { - XXX.reset_counts(); + Counter.reset_counts(); using (XXX xxx = cpp11_move_only_valuewrapper.createXXX()) { } if (cpp11_move_only_valuewrapper.has_cplusplus11()) - XXX.check_counts(1, 1, 0, 1, 0, 3); // Was (1, 2, 0, 0, 0, 3) before SwigValueWrapper::operator=(T &&) was added. + // Was (1, 2, 0, 0, 0, 3) before SwigValueWrapper::operator=(T &&) was added. + // Was (1, 1, 0, 1, 0, 3) before SwigValueWrapper::operator T&&() was added with new "out" typemaps + Counter.check_counts(1, 0, 0, 2, 0, 3); + Counter.reset_counts(); + using (XXX xxx = cpp11_move_only_valuewrapper.createXXX2()) { + } + if (cpp11_move_only_valuewrapper.has_cplusplus11()) + Counter.check_counts(1, 0, 0, 2, 0, 3); cpp11_move_only_valuewrapper.test1(); cpp11_move_only_valuewrapper.test2(); cpp11_move_only_valuewrapper.test3(); @@ -16,10 +23,11 @@ public class cpp11_move_only_valuewrapper_runme { cpp11_move_only_valuewrapper.test5(); cpp11_move_only_valuewrapper.test6(); - // C# only test (SwigValueWrapper and custom typemaps for std::unique_ptr) + // Tests SwigValueWrapper, std::unique_ptr (SWIG not parsing a type that is move-only) + Counter.reset_counts(); SWIGTYPE_p_std__unique_ptrT_XXX_t ptr = cpp11_move_only_valuewrapper.makeUniqueXXX(); cpp11_move_only_valuewrapper.cleanup(ptr); - + Counter.check_counts(1, 0, 0, 0, 0, 1); } } diff --git a/Examples/test-suite/csharp/typemap_out_optimal_runme.cs b/Examples/test-suite/csharp/typemap_out_optimal_runme.cs index 5bc1d14be..0d697498a 100644 --- a/Examples/test-suite/csharp/typemap_out_optimal_runme.cs +++ b/Examples/test-suite/csharp/typemap_out_optimal_runme.cs @@ -3,10 +3,14 @@ using typemap_out_optimalNamespace; public class typemap_out_optimal_runme { - public static XX x = null; public static void Main() { XX.debug = false; - x = XX.create(); + if (XX.debug) + Console.WriteLine("calling create()"); + using (XX x = XX.create()) { } + if (XX.debug) + Console.WriteLine("calling createConst()"); + using (XX x = XX.createConst()) { } } } diff --git a/Examples/test-suite/d/typemap_out_optimal_runme.1.d b/Examples/test-suite/d/typemap_out_optimal_runme.1.d index 16aab3cff..1267197e2 100644 --- a/Examples/test-suite/d/typemap_out_optimal_runme.1.d +++ b/Examples/test-suite/d/typemap_out_optimal_runme.1.d @@ -6,4 +6,5 @@ void main() { XX x; XX.trace = false; x = XX.create(); + x = XX.createConst(); } diff --git a/Examples/test-suite/d/typemap_out_optimal_runme.2.d b/Examples/test-suite/d/typemap_out_optimal_runme.2.d index 16aab3cff..1267197e2 100644 --- a/Examples/test-suite/d/typemap_out_optimal_runme.2.d +++ b/Examples/test-suite/d/typemap_out_optimal_runme.2.d @@ -6,4 +6,5 @@ void main() { XX x; XX.trace = false; x = XX.create(); + x = XX.createConst(); } diff --git a/Examples/test-suite/go/typemap_out_optimal_runme.go b/Examples/test-suite/go/typemap_out_optimal_runme.go index 0cccd97a6..0467ce293 100644 --- a/Examples/test-suite/go/typemap_out_optimal_runme.go +++ b/Examples/test-suite/go/typemap_out_optimal_runme.go @@ -5,4 +5,5 @@ import . "swigtests/typemap_out_optimal" func main() { SetXXDebug(false) _ = XXCreate() + _ = XXCreateConst() } diff --git a/Examples/test-suite/java/cpp11_move_only_runme.java b/Examples/test-suite/java/cpp11_move_only_runme.java new file mode 100644 index 000000000..b652634e5 --- /dev/null +++ b/Examples/test-suite/java/cpp11_move_only_runme.java @@ -0,0 +1,39 @@ + +import cpp11_move_only.*; + +public class cpp11_move_only_runme { + + static { + try { + System.loadLibrary("cpp11_move_only"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void main(String argv[]) { + + { + Counter.reset_counts(); + MoveOnly mo = MoveOnly.create(); + mo.delete(); + Counter.check_counts(1, 0, 0, 2, 0, 3); + } + + { + Counter.reset_counts(); + MovableCopyable mo = MovableCopyable.create(); + mo.delete(); + Counter.check_counts(2, 1, 0, 0, 1, 3); + } + + // Move semantics not used + { + Counter.reset_counts(); + MovableCopyable mo = MovableCopyable.createConst(); + mo.delete(); + Counter.check_counts(2, 1, 1, 0, 0, 3); + } + } +} diff --git a/Examples/test-suite/java/cpp11_move_only_valuewrapper_runme.java b/Examples/test-suite/java/cpp11_move_only_valuewrapper_runme.java new file mode 100644 index 000000000..c857a7b81 --- /dev/null +++ b/Examples/test-suite/java/cpp11_move_only_valuewrapper_runme.java @@ -0,0 +1,44 @@ +import cpp11_move_only_valuewrapper.*; + +public class cpp11_move_only_valuewrapper_runme { + + static { + try { + System.loadLibrary("cpp11_move_only_valuewrapper"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void main(String argv[]) { + Counter.reset_counts(); + { + XXX xxx = cpp11_move_only_valuewrapper.createXXX(); + xxx.delete(); + } + if (cpp11_move_only_valuewrapper.has_cplusplus11()) + // Was (1, 2, 0, 0, 0, 3) before SwigValueWrapper::operator=(T &&) was added. + // Was (1, 1, 0, 1, 0, 3) before SwigValueWrapper::operator T&&() was added with new "out" typemaps + Counter.check_counts(1, 0, 0, 2, 0, 3); + Counter.reset_counts(); + { + XXX xxx = cpp11_move_only_valuewrapper.createXXX2(); + xxx.delete(); + } + if (cpp11_move_only_valuewrapper.has_cplusplus11()) + Counter.check_counts(1, 0, 0, 2, 0, 3); + cpp11_move_only_valuewrapper.test1(); + cpp11_move_only_valuewrapper.test2(); + cpp11_move_only_valuewrapper.test3(); + cpp11_move_only_valuewrapper.test4(); + cpp11_move_only_valuewrapper.test5(); + cpp11_move_only_valuewrapper.test6(); + + // Tests SwigValueWrapper, std::unique_ptr (SWIG not parsing a type that is move-only) + Counter.reset_counts(); + SWIGTYPE_p_std__unique_ptrT_XXX_t ptr = cpp11_move_only_valuewrapper.makeUniqueXXX(); + cpp11_move_only_valuewrapper.cleanup(ptr); + Counter.check_counts(1, 0, 0, 0, 0, 1); + } +} diff --git a/Examples/test-suite/java/typemap_out_optimal_runme.java b/Examples/test-suite/java/typemap_out_optimal_runme.java index 8a87f0c4b..b48e5d697 100644 --- a/Examples/test-suite/java/typemap_out_optimal_runme.java +++ b/Examples/test-suite/java/typemap_out_optimal_runme.java @@ -12,10 +12,16 @@ public class typemap_out_optimal_runme { } } - public static XX x = null; public static void main(String argv[]) { XX.setDebug(false); - x = XX.create(); + { + XX x = XX.create(); + x.delete(); + } + { + XX x = XX.createConst(); + x.delete(); + } } } diff --git a/Examples/test-suite/python/cpp11_move_only_runme.py b/Examples/test-suite/python/cpp11_move_only_runme.py new file mode 100644 index 000000000..1c3183327 --- /dev/null +++ b/Examples/test-suite/python/cpp11_move_only_runme.py @@ -0,0 +1,17 @@ +from cpp11_move_only import * + +Counter.reset_counts() +mo = MoveOnly.create() +del mo +Counter.check_counts(1, 0, 0, 2, 0, 3) + +Counter.reset_counts() +mo = MovableCopyable.create() +del mo +Counter.check_counts(2, 1, 0, 0, 1, 3) + +# Move semantics not used +Counter.reset_counts() +mo = MovableCopyable.createConst() +del mo +Counter.check_counts(2, 1, 1, 0, 0, 3) diff --git a/Examples/test-suite/python/typemap_out_optimal_runme.py b/Examples/test-suite/python/typemap_out_optimal_runme.py index c325ece55..beed46bfc 100644 --- a/Examples/test-suite/python/typemap_out_optimal_runme.py +++ b/Examples/test-suite/python/typemap_out_optimal_runme.py @@ -2,3 +2,6 @@ from typemap_out_optimal import * cvar.XX_debug = False x = XX.create() +del x +x = XX.createConst() +del x diff --git a/Examples/test-suite/typemap_out_optimal.i b/Examples/test-suite/typemap_out_optimal.i index d707ed2d5..11f1db80f 100644 --- a/Examples/test-suite/typemap_out_optimal.i +++ b/Examples/test-suite/typemap_out_optimal.i @@ -4,15 +4,15 @@ // Just the following languages tested #if defined (SWIGCSHARP) || defined (SWIGD) %typemap(out, optimal="1") SWIGTYPE %{ - $result = new $1_ltype((const $1_ltype &)$1); + $result = new $1_ltype($1); %} #elif defined (SWIGJAVA) %typemap(out, optimal="1") SWIGTYPE %{ - *($&1_ltype*)&$result = new $1_ltype((const $1_ltype &)$1); + *($&1_ltype*)&$result = new $1_ltype($1); %} #elif defined (SWIGUTL) %typemap(out,noblock="1", optimal="1") SWIGTYPE { - %set_output(SWIG_NewPointerObj(%new_copy($1, $ltype), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj(new $1_ltype($1), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags)); } #endif @@ -32,9 +32,14 @@ struct XX { XX(const XX &other) { if (debug) cout << "XX(const XX &)" << endl; } XX& operator =(const XX &other) { if (debug) cout << "operator=(const XX &)" << endl; return *this; } ~XX() { if (debug) cout << "~XX()" << endl; } - static XX create() { + +// Note: best observed RVO for C#, Java and Python with g++-6 to g++-10 (just one constructor and one destructor call) + static XX create() { return XX(123); } + static const XX createConst() { + return XX(456); + } static bool debug; }; bool XX::debug = true; diff --git a/Lib/cffi/cffi.swg b/Lib/cffi/cffi.swg index 690081032..205bf7900 100644 --- a/Lib/cffi/cffi.swg +++ b/Lib/cffi/cffi.swg @@ -90,7 +90,8 @@ enum SWIGTYPE, SWIGTYPE *, SWIGTYPE[ANY], SWIGTYPE &, SWIGTYPE && "$result = $1;"; #ifdef __cplusplus -%typemap(out) SWIGTYPE "$result = new $1_type($1);"; +%typemap(out) SWIGTYPE +%{ $result = new $1_ltype($1); %} #else %typemap(out) SWIGTYPE { $result = ($&1_ltype) malloc(sizeof($1_type)); diff --git a/Lib/csharp/csharp.swg b/Lib/csharp/csharp.swg index 0e180f576..d4bcd3c80 100644 --- a/Lib/csharp/csharp.swg +++ b/Lib/csharp/csharp.swg @@ -399,7 +399,7 @@ SWIGINTERN const char * SWIG_UnpackData(const char *c, void *ptr, size_t sz) { %typemap(out) SWIGTYPE #ifdef __cplusplus -%{ $result = new $1_ltype((const $1_ltype &)$1); %} +%{ $result = new $1_ltype($1); %} #else { $&1_ltype $1ptr = ($&1_ltype) malloc(sizeof($1_ltype)); diff --git a/Lib/d/dswigtype.swg b/Lib/d/dswigtype.swg index 1d97cb089..535e8d576 100644 --- a/Lib/d/dswigtype.swg +++ b/Lib/d/dswigtype.swg @@ -52,7 +52,7 @@ %typemap(out) SWIGTYPE #ifdef __cplusplus -%{ $result = new $1_ltype((const $1_ltype &)$1); %} +%{ $result = new $1_ltype($1); %} #else { $&1_ltype $1ptr = ($&1_ltype) malloc(sizeof($1_ltype)); diff --git a/Lib/guile/typemaps.i b/Lib/guile/typemaps.i index f4d3a0118..4ad1aefa8 100644 --- a/Lib/guile/typemaps.i +++ b/Lib/guile/typemaps.i @@ -130,7 +130,7 @@ #ifdef __cplusplus { $&1_ltype resultptr; - resultptr = new $1_ltype((const $1_ltype &) $1); + resultptr = new $1_ltype($1); $result = SWIG_NewPointerObj (resultptr, $&1_descriptor, 1); } #else @@ -145,8 +145,7 @@ %typemap(varout) SWIGTYPE #ifdef __cplusplus { - $&1_ltype resultptr; - resultptr = new $1_ltype((const $1_ltype&) $1); + $&1_ltype resultptr = ($&1_ltype)&$1; $result = SWIG_NewPointerObj (resultptr, $&1_descriptor, 0); } #else diff --git a/Lib/java/java.swg b/Lib/java/java.swg index 8f95f3a3b..7967c1882 100644 --- a/Lib/java/java.swg +++ b/Lib/java/java.swg @@ -665,7 +665,7 @@ Swig::LocalRefGuard $1_refguard(jenv, $input); } %typemap(out) SWIGTYPE #ifdef __cplusplus -%{ *($&1_ltype*)&$result = new $1_ltype((const $1_ltype &)$1); %} +%{ *($&1_ltype*)&$result = new $1_ltype($1); %} #else { $&1_ltype $1ptr = ($&1_ltype) malloc(sizeof($1_ltype)); diff --git a/Lib/lua/luatypemaps.swg b/Lib/lua/luatypemaps.swg index f8e12846b..97ecef41e 100644 --- a/Lib/lua/luatypemaps.swg +++ b/Lib/lua/luatypemaps.swg @@ -217,7 +217,7 @@ $1=($1_ltype)&temp;%} #ifdef __cplusplus %typemap(out) SWIGTYPE { - $&1_ltype resultptr = new $1_ltype((const $1_ltype &) $1); + $&1_ltype resultptr = new $1_ltype($1); SWIG_NewPointerObj(L,(void *) resultptr,$&1_descriptor,1); SWIG_arg++; } #else diff --git a/Lib/mzscheme/typemaps.i b/Lib/mzscheme/typemaps.i index 09bda2cca..059dd06ff 100644 --- a/Lib/mzscheme/typemaps.i +++ b/Lib/mzscheme/typemaps.i @@ -127,7 +127,7 @@ #ifdef __cplusplus { $&1_ltype resultptr; - resultptr = new $1_ltype(($1_ltype &) $1); + resultptr = new $1_ltype($1); $result = SWIG_NewPointerObj (resultptr, $&1_descriptor, 1); } #else diff --git a/Lib/ocaml/ocaml.swg b/Lib/ocaml/ocaml.swg index afb01daea..0c190bbc4 100644 --- a/Lib/ocaml/ocaml.swg +++ b/Lib/ocaml/ocaml.swg @@ -93,10 +93,14 @@ $1 = *(($&1_ltype) caml_ptr_val($input,$&1_descriptor)) ; } +%typemap(varout) SWIGTYPE { + $result = SWIG_Ocaml_ptr_to_val("create_$ntype_from_ptr", (void *)&$1, $&1_descriptor); +} + #ifdef __cplusplus %typemap(out) SWIGTYPE { - $&1_ltype temp = new $ltype((const $1_ltype &) $1); + $&1_ltype temp = new $1_ltype($1); $result = SWIG_Ocaml_ptr_to_val("create_$ntype_from_ptr", (void *)temp, $&1_descriptor); } @@ -110,6 +114,10 @@ #endif +%typemap(varout) SWIGTYPE * { + $result = SWIG_Ocaml_ptr_to_val("create_$ntype_from_ptr", (void *)$1, $1_descriptor); +} + %typemap(directorin) SWIGTYPE { $<ype temp = new $ltype((const $ltype &)$1); swig_result = SWIG_Ocaml_ptr_to_val("create_$ltype_from_ptr", (void *)temp, $&1_descriptor); diff --git a/Lib/ocaml/std_string.i b/Lib/ocaml/std_string.i index 712c3bb73..a8881a16c 100644 --- a/Lib/ocaml/std_string.i +++ b/Lib/ocaml/std_string.i @@ -83,9 +83,18 @@ class wstring; $result = caml_val_string_len($1.c_str(),$1.size()); } +%typemap(varout) string { + $result = caml_val_string_len($1.c_str(),$1.size()); +} + %typemap(out) string * { $result = caml_val_string_len((*$1).c_str(),(*$1).size()); } + +%typemap(varout) string * { + $result = caml_val_string_len((*$1).c_str(),(*$1).size()); +} + %typemap(typecheck) string, const string & = char *; } diff --git a/Lib/php/php.swg b/Lib/php/php.swg index ffcffde70..7d6d5f29d 100644 --- a/Lib/php/php.swg +++ b/Lib/php/php.swg @@ -443,7 +443,7 @@ %typemap(out, phptype="SWIGTYPE") SWIGTYPE { #ifdef __cplusplus - $&1_ltype resultobj = new $1_ltype((const $1_ltype &) $1); + $&1_ltype resultobj = new $1_ltype($1); #else $&1_ltype resultobj = ($&1_ltype) malloc(sizeof($1_type)); memcpy(resultobj, &$1, sizeof($1_type)); diff --git a/Lib/ruby/std_set.i b/Lib/ruby/std_set.i index e38702ef5..1b425c6b5 100644 --- a/Lib/ruby/std_set.i +++ b/Lib/ruby/std_set.i @@ -180,17 +180,14 @@ // Redefine std::set iterator/reverse_iterator typemap %typemap(out,noblock=1) iterator, reverse_iterator { - $result = SWIG_NewPointerObj(swig::make_set_nonconst_iterator(%static_cast($1,const $type &), - self), - swig::Iterator::descriptor(),SWIG_POINTER_OWN); + $result = SWIG_NewPointerObj((swig::make_set_nonconst_iterator<$type>($1, self)), swig::Iterator::descriptor(), SWIG_POINTER_OWN); } // Redefine std::set std::pair typemap %typemap(out,noblock=1,fragment="RubyPairBoolOutputIterator") std::pair { $result = rb_ary_new2(2); - rb_ary_push($result, SWIG_NewPointerObj(swig::make_set_nonconst_iterator(%static_cast($1,$type &).first), - swig::Iterator::descriptor(),SWIG_POINTER_OWN)); + rb_ary_push($result, SWIG_NewPointerObj((swig::make_set_nonconst_iterator($1.first)), swig::Iterator::descriptor(), SWIG_POINTER_OWN)); rb_ary_push($result, SWIG_From(bool)(%static_cast($1,const $type &).second)); } diff --git a/Lib/swig.swg b/Lib/swig.swg index f6ba3a69f..188c47e03 100644 --- a/Lib/swig.swg +++ b/Lib/swig.swg @@ -677,10 +677,12 @@ template class SwigValueWrapper { public: SwigValueWrapper() : pointer(0) { } SwigValueWrapper& operator=(const T& t) { SwigSmartPointer tmp(new T(t)); pointer = tmp; return *this; } -#if __cplusplus >= 201103L +#if __cplusplus >=201103L SwigValueWrapper& operator=(T&& t) { SwigSmartPointer tmp(new T(std::move(t))); pointer = tmp; return *this; } -#endif + operator T&&() const { return std::move(*pointer.ptr); } +#else operator T&() const { return *pointer.ptr; } +#endif T *operator&() { return pointer.ptr; } };%} diff --git a/Lib/typemaps/swigtype.swg b/Lib/typemaps/swigtype.swg index 402313ebf..6973e3a10 100644 --- a/Lib/typemaps/swigtype.swg +++ b/Lib/typemaps/swigtype.swg @@ -146,9 +146,15 @@ } /* Return by value */ +#ifdef __cplusplus %typemap(out, noblock=1) SWIGTYPE { - %set_output(SWIG_NewPointerObj(%new_copy($1, $ltype), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj((new $1_ltype($1)), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags)); } +#else +%typemap(out, noblock=1) SWIGTYPE { + %set_output(SWIG_NewPointerObj(%new_copy($1, $1_ltype), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags)); +} +#endif /* ----------------------------------------------------------------------------- * --- Variable input --- @@ -534,7 +540,7 @@ * ------------------------------------------------------------ */ %typemap(throws,noblock=1) SWIGTYPE { - %raise(SWIG_NewPointerObj(%new_copy($1, $ltype),$&descriptor,SWIG_POINTER_OWN), "$type", $&descriptor); + %raise(SWIG_NewPointerObj(%new_copy($1, $1_ltype),$&descriptor,SWIG_POINTER_OWN), "$type", $&descriptor); } %typemap(throws,noblock=1) SWIGTYPE * { @@ -703,9 +709,15 @@ /* INSTANCE typemap */ +#ifdef __cplusplus +%typemap(out,noblock=1) SWIGTYPE INSTANCE { + %set_output(SWIG_NewInstanceObj((new $1_ltype($1)), $&1_descriptor, SWIG_POINTER_OWN | %newinstance_flags)); +} +#else %typemap(out,noblock=1) SWIGTYPE INSTANCE { %set_output(SWIG_NewInstanceObj(%new_copy($1, $1_ltype), $&1_descriptor, SWIG_POINTER_OWN | %newinstance_flags)); } +#endif %typemap(out,noblock=1) SWIGTYPE *INSTANCE, SWIGTYPE &INSTANCE, SWIGTYPE INSTANCE[] { %set_output(SWIG_NewInstanceObj(%as_voidptr($1), $1_descriptor, $owner | %newinstance_flags));