Add std::unique support

Simple copy of current auto_ptr support (just suppport for
functions returning std::unique_ptr).

Closes #1722
This commit is contained in:
William S Fulton 2022-07-02 16:06:32 +01:00
parent 1993b334a6
commit 299880e6a6
14 changed files with 443 additions and 15 deletions

View File

@ -7,6 +7,16 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.1.0 (in progress)
===========================
2022-07-02: wsfulton
#1722 [C#, Java, Python, Ruby] Add std::unique_ptr support. Ported from std::auto_ptr.
Use the %unique_ptr(T) macro as follows for usage std::unique_ptr<T>. For example, for
a class called Klass:
%include "std_unique_ptr.i"
%unique_ptr(Klass)
Support is currently limited to only returning a std::unique_ptr from a function.
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

View File

@ -1236,8 +1236,10 @@ While SWIG could provide wrappers for the new C++11 regular expressions classes,
<p>
SWIG provides special smart pointer handling for <tt>std::shared_ptr</tt> in the same way it has support for <tt>boost::shared_ptr</tt>.
Please see the <a href="Library.html#Library_std_shared_ptr">shared_ptr smart pointer</a> library section.
There is no special smart pointer handling available for <tt>std::weak_ptr</tt> and <tt>std::unique_ptr</tt> yet.
Please see the <a href="Library.html#Library_std_shared_ptr">shared_ptr smart pointer</a>
and <a href="Library.html#Library_std_unique_ptr">unique_ptr smart pointer</a> library sections.
There is no special smart pointer handling available for <tt>std::weak_ptr</tt>.
</p>
<H3><a name="CPlusPlus11_extensible_random_number_facility">7.3.6 Extensible random number facility</a></H3>

View File

@ -448,6 +448,7 @@
<li><a href="Library.html#Library_shared_ptr_templates">shared_ptr and templates</a>
<li><a href="Library.html#Library_shared_ptr_directors">shared_ptr and directors</a>
</ul>
<li><a href="Library.html#Library_std_unique_ptr">unique_ptr smart pointer</a>
<li><a href="Library.html#Library_std_auto_ptr">auto_ptr smart pointer</a>
</ul>
<li><a href="Library.html#Library_nn16">Utility Libraries</a>

View File

@ -40,6 +40,7 @@
<li><a href="#Library_shared_ptr_templates">shared_ptr and templates</a>
<li><a href="#Library_shared_ptr_directors">shared_ptr and directors</a>
</ul>
<li><a href="#Library_std_unique_ptr">unique_ptr smart pointer</a>
<li><a href="#Library_std_auto_ptr">auto_ptr smart pointer</a>
</ul>
<li><a href="#Library_nn16">Utility Libraries</a>
@ -2040,38 +2041,45 @@ The SWIG code below shows the required ordering:
The languages that support shared_ptr also have support for using shared_ptr with directors.
</p>
<H3><a name="Library_std_auto_ptr">12.4.5 auto_ptr smart pointer</a></H3>
<H3><a name="Library_std_unique_ptr">12.4.5 unique_ptr smart pointer</a></H3>
<p>
While <tt>std::auto_ptr</tt> is deprecated in C++11, some existing code may
still be using it, so SWIG provides limited support for this class:
<tt>std_auto_ptr.i</tt> defines the typemaps which apply to the functions
returning objects of this type. Any other use of <tt>std_auto_ptr.i</tt> is not
directly supported.
The <tt>std_unique_ptr.i</tt> library file provides SWIG's unique_ptr support.
It defines typemaps and a macro, <tt>%unique_ptr(T)</tt>, to use for handling
<tt>std::unique_ptr&lt;T&gt;</tt> for a type <tt>T</tt>.
The type <tt>T</tt> must be non-primitive.
This macro should be used before any code declaring or using type <tt>T</tt>.
Ordering requirements for using this smart pointer macro are the same as the
equivalent <tt>%shared_ptr(T)</tt> macro covered in the previous section.
</p>
<p>
A typical example of use would be
Note that the support provided is limited to returning this smart pointer from a function.
Any other use of <tt>std::auto_ptr</tt> is not directly provided yet.
</p>
<p>
Example usage would be
</p>
<div class="code">
<pre>
%include &lt;std_auto_ptr.i&gt;
%include &lt;std_unique_ptr.i&gt;
%auto_ptr(Klass)
%unique_ptr(Klass)
%inline %{
#include &lt;memory&gt;
class Klass {
public:
// Factory function creating objects of this class:
static std::auto_ptr&lt;Klass&gt; Create(int value) {
return std::auto_ptr&lt;Klass&gt;(new Klass(value));
static std::unique_ptr&lt;Klass&gt; Create(int value) {
return std::unique_ptr&lt;Klass&gt;(new Klass(value));
}
int getValue() const { return m_value; }
private:
DerivedIntValue(int value) : m_value(value) {}
Klass(int value) : m_value(value) {}
int m_value;
};
%}
@ -2090,6 +2098,89 @@ int value = k.getValue();
</pre>
</div>
<p>
The implementation simply calls <tt>std::unique_ptr::release()</tt> to obtain
the underlying raw pointer. The pointer is then used to create a target language
proxy class in the same way that SWIG handles a C++ function returning a class by value.
The target language proxy class then owns the memory pointed to by the raw pointer
and memory handling is identical to normal SWIG proxy class handling of the underlying C++ memory.
Note that an object returned by value is first copied/moved from the stack onto the heap in order to obtain
a raw pointer on the heap, whereas the underlying raw pointer in <tt>std::unique_ptr</tt> already points to an object the heap.
</p>
<p>
Note that the implementation is quite different to the <tt>std::shared_ptr</tt> smart pointer,
where the proxy class manages the underlying C++ memory as a pointer to a shared_ptr instead of a plain raw pointer.
</p>
<H3><a name="Library_std_auto_ptr">12.4.6 auto_ptr smart pointer</a></H3>
<p>
While <tt>std::auto_ptr</tt> is deprecated in C++11, some existing code may
still be using it, so SWIG provides limited support for this class by some target languages.
</p>
<p>
The <tt>std_auto_ptr.i</tt> library file provides SWIG's auto_ptr support.
It defines typemaps and a macro, <tt>%auto_ptr(T)</tt>, to use for handling
<tt>std::auto_ptr&lt;T&gt;</tt> for a type <tt>T</tt>.
The type <tt>T</tt> must be non-primitive.
This macro should be used before any code declaring or using type <tt>T</tt>.
Ordering requirements for using this smart pointer macro are the same as the
equivalent <tt>%shared_ptr(T)</tt> and <tt>%unique_ptr</tt> macros covered in
the previous two sections.
</p>
<p>
Note that the support provided is limited to returning this smart pointer from a function.
Any other use of <tt>std::auto_ptr</tt> is not directly provided.
</p>
<p>
Example usage would be
</p>
<div class="code">
<pre>
%include &lt;std_auto_ptr.i&gt;
%auto_ptr(Klass)
%inline %{
#include &lt;memory&gt;
class Klass {
public:
// Factory function creating objects of this class:
static std::auto_ptr&lt;Klass&gt; Create(int value) {
return std::auto_ptr&lt;Klass&gt;(new Klass(value));
}
int getValue() const { return m_value; }
private:
Klass(int value) : m_value(value) {}
int m_value;
};
%}
</pre>
</div>
<p>
The returned objects can be used naturally from the target language, e.g. from
C#:
</p>
<div class="targetlang">
<pre>
Klass k = Klass.Create(17);
int value = k.getValue();
</pre>
</div>
<p>
The implementation simply calls <tt>std::auto_ptr::release()</tt> to obtain the underlying raw pointer.
That is, it works the same way covered in the previous section for <tt>std::unique_ptr</tt>.
</p>
<H2><a name="Library_nn16">12.5 Utility Libraries</a></H2>

View File

@ -612,6 +612,7 @@ CPP11_TEST_CASES += \
cpp11_sizeof_object \
cpp11_static_assert \
cpp11_std_array \
cpp11_std_unique_ptr \
cpp11_strongly_typed_enumerations \
cpp11_thread_local \
cpp11_template_double_brackets \

View File

@ -0,0 +1,53 @@
%module cpp11_std_unique_ptr
#if defined(SWIGCSHARP) || defined(SWIGJAVA) || defined(SWIGPYTHON) || defined(SWIGRUBY)
%include "std_unique_ptr.i"
%unique_ptr(Klass)
%inline %{
#include <memory>
#include <string>
#include "swig_examples_lock.h"
class Klass {
public:
explicit Klass(const char* label) :
m_label(label)
{
SwigExamples::Lock lock(critical_section);
total_count++;
}
const char* getLabel() const { return m_label.c_str(); }
~Klass()
{
SwigExamples::Lock lock(critical_section);
total_count--;
}
static int getTotal_count() { return total_count; }
private:
static SwigExamples::CriticalSection critical_section;
static int total_count;
std::string m_label;
};
SwigExamples::CriticalSection Klass::critical_section;
int Klass::total_count = 0;
%}
%inline %{
std::unique_ptr<Klass> makeKlassUniquePtr(const char* label) {
return std::unique_ptr<Klass>(new Klass(label));
}
%}
#endif

View File

@ -0,0 +1,64 @@
using System;
using cpp11_std_unique_ptrNamespace;
public class cpp11_std_unique_ptr_runme {
private static void WaitForGC()
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.Threading.Thread.Sleep(10);
}
public static void Main()
{
Klass k1 = cpp11_std_unique_ptr.makeKlassUniquePtr("first");
if (k1.getLabel() != "first")
throw new Exception("wrong object label");
Klass k2 = cpp11_std_unique_ptr.makeKlassUniquePtr("second");
if (Klass.getTotal_count() != 2)
throw new Exception("number of objects should be 2");
using (Klass k3 = cpp11_std_unique_ptr.makeKlassUniquePtr("second")) {
if (Klass.getTotal_count() != 3)
throw new Exception("number of objects should be 3");
}
if (Klass.getTotal_count() != 2)
throw new Exception("number of objects should be 2");
k1 = null;
{
int countdown = 500;
int expectedCount = 1;
while (true) {
WaitForGC();
if (--countdown == 0)
break;
if (Klass.getTotal_count() == expectedCount)
break;
};
int actualCount = Klass.getTotal_count();
if (actualCount != expectedCount)
Console.Error.WriteLine("Expected count: " + expectedCount + " Actual count: " + actualCount); // Finalizers are not guaranteed to be run and sometimes they just don't
}
if (k2.getLabel() != "second")
throw new Exception("wrong object label");
k2 = null;
{
int countdown = 500;
int expectedCount = 0;
while (true) {
WaitForGC();
if (--countdown == 0)
break;
if (Klass.getTotal_count() == expectedCount)
break;
}
int actualCount = Klass.getTotal_count();
if (actualCount != expectedCount)
Console.Error.WriteLine("Expected count: " + expectedCount + " Actual count: " + actualCount); // Finalizers are not guaranteed to be run and sometimes they just don't
}
}
}

View File

@ -0,0 +1,68 @@
import cpp11_std_unique_ptr.*;
public class cpp11_std_unique_ptr_runme {
static {
try {
System.loadLibrary("cpp11_std_unique_ptr");
} 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);
}
}
private static void WaitForGC()
{
System.gc();
System.runFinalization();
try {
java.lang.Thread.sleep(10);
} catch (java.lang.InterruptedException e) {
}
}
public static void main(String argv[]) throws Throwable
{
Klass k1 = cpp11_std_unique_ptr.makeKlassUniquePtr("first");
if (!k1.getLabel().equals("first"))
throw new RuntimeException("wrong object label");
Klass k2 = cpp11_std_unique_ptr.makeKlassUniquePtr("second");
if (Klass.getTotal_count() != 2)
throw new RuntimeException("number of objects should be 2");
k1 = null;
{
int countdown = 500;
int expectedCount = 1;
while (true) {
WaitForGC();
if (--countdown == 0)
break;
if (Klass.getTotal_count() == expectedCount)
break;
}
int actualCount = Klass.getTotal_count();
if (actualCount != expectedCount)
System.err.println("GC failed to run (cpp11_std_unique_ptr 1). Expected count: " + expectedCount + " Actual count: " + actualCount); // Finalizers are not guaranteed to be run and sometimes they just don't
}
if (!k2.getLabel().equals("second"))
throw new RuntimeException("wrong object label");
k2 = null;
{
int countdown = 500;
int expectedCount = 0;
while (true) {
WaitForGC();
if (--countdown == 0)
break;
if (Klass.getTotal_count() == expectedCount)
break;
};
int actualCount = Klass.getTotal_count();
if (actualCount != expectedCount)
System.err.println("GC failed to run (cpp11_std_unique_ptr 2). Expected count: " + expectedCount + " Actual count: " + actualCount); // Finalizers are not guaranteed to be run and sometimes they just don't
}
}
}

View File

@ -0,0 +1,17 @@
from cpp11_std_unique_ptr import *
k1 = makeKlassUniquePtr("first")
k2 = makeKlassUniquePtr("second")
if Klass.getTotal_count() != 2:
raise "number of objects should be 2"
del k1
if Klass.getTotal_count() != 1:
raise "number of objects should be 1"
if k2.getLabel() != "second":
raise "wrong object label"
del k2
if Klass.getTotal_count() != 0:
raise "no objects should be left"

View File

@ -0,0 +1,27 @@
#!/usr/bin/env ruby
require 'swig_assert'
require 'cpp11_std_unique_ptr'
def gc_check(expected_count)
# GC.start(full_mark: true, immediate_sweep: true)
GC.start
# GC is not reliably run, skip check
# swig_assert_equal_simple(expected_count, Cpp11_std_unique_ptr::Klass::getTotal_count())
end
k1 = Cpp11_std_unique_ptr::makeKlassUniquePtr("first")
k2 = Cpp11_std_unique_ptr::makeKlassUniquePtr("second")
swig_assert_equal_simple(2, Cpp11_std_unique_ptr::Klass::getTotal_count())
gc_check(2)
k1 = nil
gc_check(1)
swig_assert_equal_simple(k2.getLabel(), "second")
gc_check(1)
k2 = nil
gc_check(0)

View File

@ -0,0 +1,27 @@
/* -----------------------------------------------------------------------------
* std_unique_ptr.i
*
* The typemaps here allow handling functions returning std::unique_ptr<>,
* which is the most common use of this type. If you have functions taking it
* as parameter, these typemaps can't be used for them and you need to do
* something else (e.g. use shared_ptr<> which SWIG supports fully).
* ----------------------------------------------------------------------------- */
%define %unique_ptr(TYPE)
%typemap (ctype) std::unique_ptr< TYPE > "void *"
%typemap (imtype, out="System.IntPtr") std::unique_ptr< TYPE > "HandleRef"
%typemap (cstype) std::unique_ptr< TYPE > "$typemap(cstype, TYPE)"
%typemap (out) std::unique_ptr< TYPE > %{
$result = (void *)$1.release();
%}
%typemap(csout, excode=SWIGEXCODE) std::unique_ptr< TYPE > {
System.IntPtr cPtr = $imcall;
$typemap(cstype, TYPE) ret = (cPtr == System.IntPtr.Zero) ? null : new $typemap(cstype, TYPE)(cPtr, true);$excode
return ret;
}
%template() std::unique_ptr< TYPE >;
%enddef
namespace std {
template <class T> class unique_ptr {};
}

29
Lib/java/std_unique_ptr.i Normal file
View File

@ -0,0 +1,29 @@
/* -----------------------------------------------------------------------------
* std_unique_ptr.i
*
* The typemaps here allow handling functions returning std::unique_ptr<>,
* which is the most common use of this type. If you have functions taking it
* as parameter, these typemaps can't be used for them and you need to do
* something else (e.g. use shared_ptr<> which SWIG supports fully).
* ----------------------------------------------------------------------------- */
%define %unique_ptr(TYPE)
%typemap (jni) std::unique_ptr< TYPE > "jlong"
%typemap (jtype) std::unique_ptr< TYPE > "long"
%typemap (jstype) std::unique_ptr< TYPE > "$typemap(jstype, TYPE)"
%typemap (out) std::unique_ptr< TYPE > %{
jlong lpp = 0;
*(TYPE **) &lpp = $1.release();
$result = lpp;
%}
%typemap(javaout) std::unique_ptr< TYPE > {
long cPtr = $jnicall;
return (cPtr == 0) ? null : new $typemap(jstype, TYPE)(cPtr, true);
}
%template() std::unique_ptr< TYPE >;
%enddef
namespace std {
template <class T> class unique_ptr {};
}

View File

@ -0,0 +1,19 @@
/* -----------------------------------------------------------------------------
* std_unique_ptr.i
*
* The typemaps here allow handling functions returning std::unique_ptr<>,
* which is the most common use of this type. If you have functions taking it
* as parameter, these typemaps can't be used for them and you need to do
* something else (e.g. use shared_ptr<> which SWIG supports fully).
* ----------------------------------------------------------------------------- */
%define %unique_ptr(TYPE)
%typemap (out) std::unique_ptr< TYPE > %{
%set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags));
%}
%template() std::unique_ptr< TYPE >;
%enddef
namespace std {
template <class T> class unique_ptr {};
}

19
Lib/ruby/std_unique_ptr.i Normal file
View File

@ -0,0 +1,19 @@
/* -----------------------------------------------------------------------------
* std_unique_ptr.i
*
* The typemaps here allow handling functions returning std::unique_ptr<>,
* which is the most common use of this type. If you have functions taking it
* as parameter, these typemaps can't be used for them and you need to do
* something else (e.g. use shared_ptr<> which SWIG supports fully).
* ----------------------------------------------------------------------------- */
%define %unique_ptr(TYPE)
%typemap (out) std::unique_ptr< TYPE > %{
%set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags));
%}
%template() std::unique_ptr< TYPE >;
%enddef
namespace std {
template <class T> class unique_ptr {};
}