diff options
author | Olly Betts <olly@survex.com> | 2023-05-08 15:56:37 +1200 |
---|---|---|
committer | Olly Betts <olly@survex.com> | 2023-05-08 15:56:37 +1200 |
commit | 6085a9661edd6bbfbe0dbffd187afda8be8aefd0 (patch) | |
tree | 747308c4a6a0008fe1ba2b5ab7b74b425b063f73 | |
parent | 0fa2ab8945ff40e770045dd86ed0380c006643fc (diff) | |
download | swig-6085a9661edd6bbfbe0dbffd187afda8be8aefd0.tar.gz |
Initial support for std::string_view
So far C#, Java, Lua and PHP are supported.
Closes: #2540
See #1567
-rw-r--r-- | CHANGES.current | 4 | ||||
-rw-r--r-- | Doc/Manual/Contents.html | 1 | ||||
-rw-r--r-- | Doc/Manual/Library.html | 54 | ||||
-rw-r--r-- | Examples/test-suite/common.mk | 1 | ||||
-rw-r--r-- | Examples/test-suite/cpp17_string_view.i | 112 | ||||
-rw-r--r-- | Examples/test-suite/csharp/cpp17_string_view_runme.cs | 84 | ||||
-rw-r--r-- | Examples/test-suite/csharp/director_string_view_runme.cs | 52 | ||||
-rw-r--r-- | Examples/test-suite/director_string_view.i | 56 | ||||
-rw-r--r-- | Examples/test-suite/java/cpp17_string_view_runme.java | 92 | ||||
-rw-r--r-- | Examples/test-suite/java/director_string_view_runme.java | 55 | ||||
-rw-r--r-- | Examples/test-suite/lua/cpp17_string_view_runme.lua | 50 | ||||
-rw-r--r-- | Examples/test-suite/php/cpp17_string_view_runme.php | 44 | ||||
-rw-r--r-- | Examples/test-suite/php/director_string_view_runme.php | 34 | ||||
-rw-r--r-- | Lib/csharp/std_string_view.i | 113 | ||||
-rw-r--r-- | Lib/java/std_string_view.i | 138 | ||||
-rw-r--r-- | Lib/lua/std_string_view.i | 41 | ||||
-rw-r--r-- | Lib/php/std_string_view.i | 69 | ||||
-rw-r--r-- | Lib/swig.swg | 5 |
18 files changed, 994 insertions, 11 deletions
diff --git a/CHANGES.current b/CHANGES.current index 4a4304545..8200b7d7a 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -8,6 +8,10 @@ Version 4.2.0 (in progress) =========================== 2023-05-08: olly + #1567 Add support for std::string_view (new in C++17) for C#, Java, + Lua and PHP. + +2023-05-08: olly [PHP] #2544 Wrap overloaded method with both static and non-static forms. We now wrap this as a non-static method in PHP, which means the static form only callable via an object. diff --git a/Doc/Manual/Contents.html b/Doc/Manual/Contents.html index e78410af6..f3517ef9a 100644 --- a/Doc/Manual/Contents.html +++ b/Doc/Manual/Contents.html @@ -445,6 +445,7 @@ <li><a href="Library.html#Library_stl_cpp_library">STL/C++ library</a> <ul> <li><a href="Library.html#Library_std_string">std::string</a> +<li><a href="Library.html#Library_std_string_view">std::string_view</a> <li><a href="Library.html#Library_std_vector">std::vector</a> <li><a href="Library.html#Library_stl_exceptions">STL exceptions</a> <li><a href="Library.html#Library_std_shared_ptr">shared_ptr smart pointer</a> diff --git a/Doc/Manual/Library.html b/Doc/Manual/Library.html index f308c31f1..be262753a 100644 --- a/Doc/Manual/Library.html +++ b/Doc/Manual/Library.html @@ -30,6 +30,7 @@ <li><a href="#Library_stl_cpp_library">STL/C++ library</a> <ul> <li><a href="#Library_std_string">std::string</a> +<li><a href="#Library_std_string_view">std::string_view</a> <li><a href="#Library_std_vector">std::vector</a> <li><a href="#Library_stl_exceptions">STL exceptions</a> <li><a href="#Library_std_shared_ptr">shared_ptr smart pointer</a> @@ -1450,14 +1451,15 @@ The following table shows which C++ classes are supported and the equivalent SWI <tr> <td>std::multiset (C++11)</td> <td>multiset</td> <td>std_multiset.i</td> </tr> <tr> <td>std::pair</td> <td>utility</td> <td>std_pair.i</td> </tr> <tr> <td>std::set</td> <td>set</td> <td>std_set.i</td> </tr> +<tr> <td>std::shared_ptr (C++11)</td> <td>shared_ptr</td> <td>std_shared_ptr.i</td> </tr> <tr> <td>std::string</td> <td>string</td> <td>std_string.i</td> </tr> +<tr> <td>std::string_view (C++17)</td> <td>string_view</td> <td>std_string_view.i</td> </tr> <tr> <td>std::unordered_map (C++11)</td> <td>unordered_map</td> <td>std_unordered_map.i</td> </tr> <tr> <td>std::unordered_multimap (C++11)</td> <td>unordered_multimap</td> <td>std_unordered_multimap.i</td> </tr> <tr> <td>std::unordered_multiset (C++11)</td> <td>unordered_multiset</td> <td>std_unordered_multiset.i</td> </tr> <tr> <td>std::unordered_set (C++11)</td> <td>unordered_set</td> <td>std_unordered_set.i</td> </tr> <tr> <td>std::vector</td> <td>vector</td> <td>std_vector.i</td> </tr> <tr> <td>std::wstring</td> <td>wstring</td> <td>std_wstring.i</td> </tr> -<tr> <td>std::shared_ptr (C++11)</td> <td>shared_ptr</td> <td>std_shared_ptr.i</td> </tr> </table> @@ -1551,7 +1553,37 @@ void foo(string s, const String &t); // std_string typemaps still applie </pre> </div> -<H3><a name="Library_std_vector">12.4.2 std::vector</a></H3> +<H3><a name="Library_std_string_view">12.4.2 std::string_view</a></H3> + + +<p> +The <tt>std_string_view.i</tt> library provides typemaps for converting C++17 <tt>std::string_view</tt> +objects to and from strings in the target scripting language. For example: +</p> + +<div class="code"> +<pre> +%module example +%include "std_string_view.i" + +std::string_view foo(); +void bar(std::string_view x); +</pre> +</div> + +<p> +In the target language: +</p> + +<div class="targetlang"> +<pre> +x = foo(); # Returns a string object +bar("Hello World"); # Pass string as std::string_view +</pre> +</div> + + +<H3><a name="Library_std_vector">12.4.3 std::vector</a></H3> <p> @@ -1730,7 +1762,7 @@ if you want to make their head explode. details and the public API exposed to the interpreter vary. </p> -<H3><a name="Library_stl_exceptions">12.4.3 STL exceptions</a></H3> +<H3><a name="Library_stl_exceptions">12.4.4 STL exceptions</a></H3> <p> @@ -1780,10 +1812,10 @@ The <tt>%exception</tt> directive can be used by placing the following code befo Any thrown STL exceptions will then be gracefully handled instead of causing a crash. </p> -<H3><a name="Library_std_shared_ptr">12.4.4 shared_ptr smart pointer</a></H3> +<H3><a name="Library_std_shared_ptr">12.4.5 shared_ptr smart pointer</a></H3> -<H4><a name="Library_shared_ptr_basics">12.4.4.1 shared_ptr basics</a></H4> +<H4><a name="Library_shared_ptr_basics">12.4.5.1 shared_ptr basics</a></H4> <p> @@ -1879,7 +1911,7 @@ System.out.println(val1 + " " + val2); </pre> </div> -<H4><a name="Library_shared_ptr_inheritance">12.4.4.2 shared_ptr and inheritance</a></H4> +<H4><a name="Library_shared_ptr_inheritance">12.4.5.2 shared_ptr and inheritance</a></H4> <p> @@ -1970,7 +2002,7 @@ Adding the missing <tt>%shared_ptr</tt> macros will fix this: </pre> </div> -<H4><a name="Library_shared_ptr_overloading">12.4.4.3 shared_ptr and method overloading</a></H4> +<H4><a name="Library_shared_ptr_overloading">12.4.5.3 shared_ptr and method overloading</a></H4> <p> @@ -1992,7 +2024,7 @@ SWIG will choose to wrap just the first method by default. For the interested reader, SWIG detects that they are equivalent types via the <a href=Typemaps.html#Typemaps_typecheck_pointer>typecheck typemaps</a> in the shared_ptr library. </p> -<H4><a name="Library_shared_ptr_templates">12.4.4.4 shared_ptr and templates</a></H4> +<H4><a name="Library_shared_ptr_templates">12.4.5.4 shared_ptr and templates</a></H4> <p> @@ -2034,14 +2066,14 @@ The SWIG code below shows the required ordering: </pre> </div> -<H4><a name="Library_shared_ptr_directors">12.4.4.5 shared_ptr and directors</a></H4> +<H4><a name="Library_shared_ptr_directors">12.4.5.5 shared_ptr and directors</a></H4> <p> The languages that support shared_ptr also have support for using shared_ptr with directors. </p> -<H3><a name="Library_std_unique_ptr">12.4.5 unique_ptr smart pointer</a></H3> +<H3><a name="Library_std_unique_ptr">12.4.6 unique_ptr smart pointer</a></H3> <p> @@ -2163,7 +2195,7 @@ in a "Cannot release ownership as memory is not owned" exception. For example, i <b>Compatibility note:</b> Support for <tt>std::unique_ptr</tt> was added in SWIG-4.1.0. </p> -<H3><a name="Library_std_auto_ptr">12.4.6 auto_ptr smart pointer</a></H3> +<H3><a name="Library_std_auto_ptr">12.4.7 auto_ptr smart pointer</a></H3> <p> diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index c781bd930..7b007a2ac 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -657,6 +657,7 @@ CPP17_TEST_CASES += \ cpp17_hex_floating_literals \ cpp17_nested_namespaces \ cpp17_nspace_nested_namespaces \ + cpp17_string_view \ cpp17_u8_char_literals \ # Broken C++17 test cases. diff --git a/Examples/test-suite/cpp17_string_view.i b/Examples/test-suite/cpp17_string_view.i new file mode 100644 index 000000000..864cc97eb --- /dev/null +++ b/Examples/test-suite/cpp17_string_view.i @@ -0,0 +1,112 @@ +%module cpp17_string_view +#if defined SWIGCSHARP || defined SWIGJAVA || defined SWIGLUA || defined SWIGPHP +%include <std_string_view.i> + +// throw is invalid in C++17 and later, only SWIG to use it +#define TESTCASE_THROW1(T1) throw(T1) +%{ +#define TESTCASE_THROW1(T1) +%} + +%inline %{ + +std::string_view test_value(std::string_view x) { + return x; +} + +const std::string_view& test_const_reference(const std::string_view &x) { + return x; +} + +void test_const_reference_returning_void(const std::string_view &) { +} + +void test_const_reference_returning_void(const std::string_view &, int) { +} + +void test_pointer(std::string_view *x) { +} + +std::string_view *test_pointer_out() { + static std::string_view x = "x"; + return &x; +} + +void test_const_pointer(const std::string_view *x) { +} + +const std::string_view *test_const_pointer_out() { + static std::string_view x = "x"; + return &x; +} + +void test_reference(std::string_view &x) { +} + +std::string_view& test_reference_out() { + static std::string_view x = "test_reference_out message"; + return x; +} + +std::string_view test_reference_input(std::string_view &input) { + return input; +} + +void test_throw() TESTCASE_THROW1(std::string_view){ + static std::string_view x = "test_throw message"; + throw x; +} + +void test_const_reference_throw() TESTCASE_THROW1(const std::string_view &){ + static const std::string_view x = "test_const_reference_throw message"; + throw x; +} + +void test_pointer_throw() TESTCASE_THROW1(std::string_view *) { + throw new std::string_view("foo"); +} + +void test_const_pointer_throw() TESTCASE_THROW1(const std::string_view *) { + throw static_cast<const std::string_view*>(new std::string_view("foo")); +} +%} + +#ifdef SWIGSCILAB +%rename(ConstStr) ConstMemberString; +%rename(ConstStaticStr) ConstStaticMemberString; +#endif + +%inline %{ +const std::string_view ConstGlobalString = "const global string"; + +struct Structure { + const std::string_view ConstMemberString; + static const std::string_view ConstStaticMemberString; + + Structure() : ConstMemberString("const member string") {} +}; +%} + +%{ + const std::string_view Structure::ConstStaticMemberString = "const static member string"; +%} + + +%inline %{ + std::string_view stdstring_empty() { + return std::string_view(); + } + + char *c_empty() { + return (char *)""; + } + + char *c_null() { + return 0; + } + + const char *get_null(const char *a) { + return a == 0 ? a : "non-null"; + } +%} +#endif diff --git a/Examples/test-suite/csharp/cpp17_string_view_runme.cs b/Examples/test-suite/csharp/cpp17_string_view_runme.cs new file mode 100644 index 000000000..eba25f4de --- /dev/null +++ b/Examples/test-suite/csharp/cpp17_string_view_runme.cs @@ -0,0 +1,84 @@ +using System; +using cpp17_string_viewNamespace; + +public class runme +{ + static void Main() + { + // Checking expected use of %typemap(in) std::string {} + cpp17_string_view.test_value("Fee"); + + // Checking expected result of %typemap(out) std::string {} + if (cpp17_string_view.test_value("Fi") != "Fi") + throw new Exception("Test 1 failed"); + + // Verify type-checking for %typemap(in) std::string {} + try { + cpp17_string_view.test_value(null); + throw new Exception("Test 2 failed"); + } catch (ArgumentNullException) { + } + + // Checking expected use of %typemap(in) const std::string & {} + cpp17_string_view.test_const_reference("Fo"); + + // Checking expected result of %typemap(out) const std::string& {} + if (cpp17_string_view.test_const_reference("Fum") != "Fum") + throw new Exception("Test 3 failed"); + + // Verify type-checking for %typemap(in) const std::string & {} + try { + cpp17_string_view.test_const_reference(null); + throw new Exception("Test 4 failed"); + } catch (ArgumentNullException) { + } + + // + // Input and output typemaps for pointers and non-const references to + // std::string are *not* supported; the following tests confirm + // that none of these cases are slipping through. + // + + SWIGTYPE_p_std__string_view stringPtr = null; + + stringPtr = cpp17_string_view.test_pointer_out(); + + cpp17_string_view.test_pointer(stringPtr); + + stringPtr = cpp17_string_view.test_const_pointer_out(); + + cpp17_string_view.test_const_pointer(stringPtr); + + stringPtr = cpp17_string_view.test_reference_out(); + + cpp17_string_view.test_reference(stringPtr); + + // Check throw exception specification + try { + cpp17_string_view.test_throw(); + throw new Exception("Test 5 failed"); + } catch (ApplicationException e) { + if (e.Message != "test_throw message") + throw new Exception("Test 5 string check: " + e.Message); + } + try { + cpp17_string_view.test_const_reference_throw(); + throw new Exception("Test 6 failed"); + } catch (ApplicationException e) { + if (e.Message != "test_const_reference_throw message") + throw new Exception("Test 6 string check: " + e.Message); + } + + // Global variables + if (cpp17_string_view.ConstGlobalString != "const global string") + throw new Exception("ConstGlobalString test"); + + // Member variables + Structure myStructure = new Structure(); + if (myStructure.ConstMemberString != "const member string") + throw new Exception("ConstMemberString test"); + + if (Structure.ConstStaticMemberString != "const static member string") + throw new Exception("ConstStaticMemberString test"); + } +} diff --git a/Examples/test-suite/csharp/director_string_view_runme.cs b/Examples/test-suite/csharp/director_string_view_runme.cs new file mode 100644 index 000000000..7060b28a8 --- /dev/null +++ b/Examples/test-suite/csharp/director_string_view_runme.cs @@ -0,0 +1,52 @@ +using System; +using director_string_viewNamespace; + +public class runme +{ + static void Main() + { + runme r = new runme(); + r.run(); + } + + void run() + { + String s; + + director_string_view_A c = new director_string_view_A("hi"); + for (int i=0; i<3; i++) { + s = c.call_get(i); + Object ii = i; + if (s != ii.ToString()) throw new Exception("director_string_view_A.get(" + i + ") failed. Got:" + s); + } + + director_string_view_B b = new director_string_view_B("hello"); + + s = b.call_get_first(); + if (s != "director_string_view_B.get_first") throw new Exception("call_get_first() failed"); + + s = b.call_get(0); + if (s != "director_string_view_B.get: hello") throw new Exception("get(0) failed"); + } +} + +class director_string_view_B : A { + public director_string_view_B(String first) : base(first) { + } + public override String get_first() { + return "director_string_view_B.get_first"; + } + + public override String get(int n) { + return "director_string_view_B.get: " + base.get(n); + } +} + +class director_string_view_A : A { + public director_string_view_A(String first) : base(first) { + } + public override String get(int n) { + Object nn = n; + return nn.ToString(); + } +} diff --git a/Examples/test-suite/director_string_view.i b/Examples/test-suite/director_string_view.i new file mode 100644 index 000000000..19c2dbf01 --- /dev/null +++ b/Examples/test-suite/director_string_view.i @@ -0,0 +1,56 @@ +%module(directors="1") director_string_view; + +#if defined SWIGCSHARP || defined SWIGJAVA || defined SWIGLUA || defined SWIGPHP + +%include std_string.i +%include std_string_view.i + +// Using thread unsafe wrapping +%warnfilter(SWIGWARN_TYPEMAP_THREAD_UNSAFE,SWIGWARN_TYPEMAP_DIRECTOROUT_PTR) A; + +%{ +#include <vector> +#include <string> +#include <string_view> +%} + +%feature("director") A; +%inline %{ + +struct A +{ + A(const std::string& first) + : m_strings(1, first) + {} + + virtual ~A() {} + + virtual std::string_view get_first() const + { return get(0); } + + virtual std::string_view get(int n) const + { return m_strings[n]; } + + virtual std::string_view call_get_first() const + { return get_first(); } + + virtual std::string_view call_get(int n) const + { return get(n); } + + virtual int string_length(std::string_view s) const + { return (int)s.size(); } + + + virtual void process_text(const char *text) + { + } + + void call_process_func() { process_text("hello"); } + +private: + std::vector<std::string> m_strings; +}; + +%} + +#endif diff --git a/Examples/test-suite/java/cpp17_string_view_runme.java b/Examples/test-suite/java/cpp17_string_view_runme.java new file mode 100644 index 000000000..903970e39 --- /dev/null +++ b/Examples/test-suite/java/cpp17_string_view_runme.java @@ -0,0 +1,92 @@ +import cpp17_string_view.*; + +public class cpp17_string_view_runme { + + static { + try { + System.loadLibrary("cpp17_string_view"); + } 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[]) throws Throwable + { + // Checking expected use of %typemap(in) std::string_view {} + cpp17_string_view.test_value("Fee"); + + // Checking expected result of %typemap(out) std::string_view {} + if (!cpp17_string_view.test_value("Fi").equals("Fi")) + throw new RuntimeException("Test 1 failed"); + + // Verify type-checking for %typemap(in) std::string_view {} + try { + cpp17_string_view.test_value(null); + throw new RuntimeException("Test 2 failed"); + } catch (NullPointerException e) { + } + + // Checking expected use of %typemap(in) const std::string_view & {} + cpp17_string_view.test_const_reference("Fo"); + + // Checking expected result of %typemap(out) const std::string_view& {} + if (!cpp17_string_view.test_const_reference("Fum").equals("Fum")) + throw new RuntimeException("Test 3 failed"); + + // Verify type-checking for %typemap(in) const std::string_view & {} + try { + cpp17_string_view.test_const_reference(null); + throw new RuntimeException("Test 4 failed"); + } catch (NullPointerException e) { + } + + // + // Input and output typemaps for pointers and non-const references to + // std::string_view are *not* supported; the following tests confirm + // that none of these cases are slipping through. + // + + SWIGTYPE_p_std__string_view stringPtr = null; + + stringPtr = cpp17_string_view.test_pointer_out(); + + cpp17_string_view.test_pointer(stringPtr); + + stringPtr = cpp17_string_view.test_const_pointer_out(); + + cpp17_string_view.test_const_pointer(stringPtr); + + stringPtr = cpp17_string_view.test_reference_out(); + + cpp17_string_view.test_reference(stringPtr); + + // Check throw exception specification + try { + cpp17_string_view.test_throw(); + throw new Throwable("Test 5 failed"); + } catch (RuntimeException e) { + if (!e.getMessage().equals("test_throw message")) + throw new Exception("Test 5 string check: " + e.getMessage()); + } + try { + cpp17_string_view.test_const_reference_throw(); + throw new Throwable("Test 6 failed"); + } catch (RuntimeException e) { + if (!e.getMessage().equals("test_const_reference_throw message")) + throw new Exception("Test 6 string check: " + e.getMessage()); + } + + // Global variables + if (!cpp17_string_view.getConstGlobalString().equals("const global string")) + throw new Exception("ConstGlobalString test"); + + // Member variables + Structure myStructure = new Structure(); + if (!myStructure.getConstMemberString().equals("const member string")) + throw new Exception("ConstMemberString test"); + + if (!Structure.getConstStaticMemberString().equals("const static member string")) + throw new Exception("ConstStaticMemberString test"); + } +} diff --git a/Examples/test-suite/java/director_string_view_runme.java b/Examples/test-suite/java/director_string_view_runme.java new file mode 100644 index 000000000..7aabf7d96 --- /dev/null +++ b/Examples/test-suite/java/director_string_view_runme.java @@ -0,0 +1,55 @@ + +import director_string_view.*; + +public class director_string_view_runme { + + static { + try { + System.loadLibrary("director_string_view"); + } 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[]) { + + String s; + + director_string_view_A c = new director_string_view_A("hi"); + for (int i=0; i<3; i++) { + s = c.call_get(i); + if (!s.equals(Integer.valueOf(i).toString())) throw new RuntimeException("director_string_view_A.get(" + i + ") failed. Got:" + s); + } + + director_string_view_B b = new director_string_view_B("hello"); + + s = b.call_get_first(); + if (!s.equals("director_string_view_B.get_first")) throw new RuntimeException("call_get_first() failed"); + + s = b.call_get(0); + if (!s.equals("director_string_view_B.get: hello")) throw new RuntimeException("get(0) failed"); + } +} + +class director_string_view_B extends A { + public director_string_view_B(String first) { + super(first); + } + public String get_first() { + return "director_string_view_B.get_first"; + } + + public String get(int n) { + return "director_string_view_B.get: " + super.get(n); + } +} + +class director_string_view_A extends A { + public director_string_view_A(String first) { + super(first); + } + public String get(int n) { + return Integer.valueOf(n).toString(); + } +} diff --git a/Examples/test-suite/lua/cpp17_string_view_runme.lua b/Examples/test-suite/lua/cpp17_string_view_runme.lua new file mode 100644 index 000000000..b30d99273 --- /dev/null +++ b/Examples/test-suite/lua/cpp17_string_view_runme.lua @@ -0,0 +1,50 @@ +require("import") -- the import fn +import("cpp17_string_view") -- import lib + +for k,v in pairs(cpp17_string_view) do _G[k]=v end -- move to global + +-- catch "undefined" global variables +local env = _ENV -- Lua 5.2 +if not env then env = getfenv () end -- Lua 5.1 +setmetatable(env, {__index=function (t,i) error("undefined global variable `"..i.."'",2) end}) + +-- Checking expected use of %typemap(in) std::string_view {} +test_value("Fee") + +-- Checking expected result of %typemap(out) std::string_view {} +s=test_value("Fi") +assert(type(s)=="string" and s =="Fi") + +-- Checking expected use of %typemap(in) const std::string_view & {} +test_const_reference("Fo") + +-- Checking expected result of %typemap(out) const std::string_view& {} +s=test_const_reference("Fum") +assert(type(s)=="string" and s =="Fum") + +-- Input and output typemaps for pointers and non-const references to +-- std::string_view are *not* supported; the following tests confirm +-- that none of these cases are slipping through. + +stringPtr = test_pointer_out() + +test_pointer(stringPtr) + +stringPtr = test_const_pointer_out() + +test_const_pointer(stringPtr) + +stringPtr = test_reference_out() + +test_reference(stringPtr) + +-- Global variables +assert(cpp17_string_view.ConstGlobalString=="const global string") + +-- Member variables +myStructure = Structure() +assert(myStructure.ConstMemberString=="const member string") + +assert(Structure.ConstStaticMemberString=="const static member string") + +test_const_reference_returning_void("foo") diff --git a/Examples/test-suite/php/cpp17_string_view_runme.php b/Examples/test-suite/php/cpp17_string_view_runme.php new file mode 100644 index 000000000..5f756bb36 --- /dev/null +++ b/Examples/test-suite/php/cpp17_string_view_runme.php @@ -0,0 +1,44 @@ +<?php + +require "tests.php"; + +# Checking expected use of %typemap(in) std::string_view {} +cpp17_string_view::test_value("Fee"); + +# Checking expected result of %typemap(out) std::string_view {} +check::equal(cpp17_string_view::test_value("Fi"), "Fi", "Test 1"); + +# Checking expected use of %typemap(in) const std::string_view & {} +cpp17_string_view::test_const_reference("Fo"); + +# Checking expected result of %typemap(out) const std::string_view& {} +check::equal(cpp17_string_view::test_const_reference("Fum"), "Fum", "Test 3"); + +# Input and output typemaps for pointers and non-const references to +# std::string_view are *not* supported; the following tests confirm +# that none of these cases are slipping through. + +$stringPtr = cpp17_string_view::test_pointer_out(); + +cpp17_string_view::test_pointer($stringPtr); + +$stringPtr = cpp17_string_view::test_const_pointer_out(); + +cpp17_string_view::test_const_pointer($stringPtr); + +$stringPtr = cpp17_string_view::test_reference_out(); + +cpp17_string_view::test_reference($stringPtr); + +// Global variables +check::equal(ConstGlobalString_get(), "const global string", "ConstGlobalString test"); + +// Member variables +$myStructure = new Structure(); +check::equal($myStructure->ConstMemberString, "const member string", "ConstMemberString test"); + +check::equal(Structure::ConstStaticMemberString(), "const static member string", "ConstStaticMemberString test"); + +cpp17_string_view::test_const_reference_returning_void("foo"); + +check::done(); diff --git a/Examples/test-suite/php/director_string_view_runme.php b/Examples/test-suite/php/director_string_view_runme.php new file mode 100644 index 000000000..ae3c10d11 --- /dev/null +++ b/Examples/test-suite/php/director_string_view_runme.php @@ -0,0 +1,34 @@ +<?php + +require "tests.php"; + +// No new functions +check::functions(array()); +// New classes +check::classes(array('A')); +// No new vars +check::globals(array()); + +class B extends A { + public $smem; + + function get_first() { + return parent::get_first() . " world!"; + } + + function process_text($string) { + parent::process_text($string); + $this->smem = "hello"; + } +} + +$b = new B("hello"); + +$b->get(0); +check::equal($b->get_first(),"hello world!", "get_first failed"); + +$b->call_process_func(); + +check::equal($b->smem, "hello", "smem failed"); + +check::done(); diff --git a/Lib/csharp/std_string_view.i b/Lib/csharp/std_string_view.i new file mode 100644 index 000000000..b03c8461a --- /dev/null +++ b/Lib/csharp/std_string_view.i @@ -0,0 +1,113 @@ +/* ----------------------------------------------------------------------------- + * std_string_view.i + * + * Typemaps for std::string_view and const std::string_view& + * These are mapped to a C# String and are passed around by value. + * + * To use non-const std::string_view references use the following %apply. Note + * that they are passed by value. + * %apply const std::string_view & {std::string_view &}; + * ----------------------------------------------------------------------------- */ + +%{ +#include <string_view> +#include <string> +%} + +namespace std { + +%naturalvar string_view; + +class string_view; + +// string_view +%typemap(ctype) string_view "const char *" +%typemap(imtype) string_view "string" +%typemap(cstype) string_view "string" + +%typemap(csdirectorin) string_view "$iminput" +%typemap(csdirectorout) string_view "$cscall" + +%typemap(in, canthrow=1) string_view +%{ if (!$input) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return $null; + } + $1 = std::string_view($input); %} +%typemap(out) string_view %{ $result = SWIG_csharp_string_callback(std::string($1).c_str()); %} + +%typemap(directorout, canthrow=1) string_view +%{ if (!$input) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return $null; + } + $result = std::string_view($input); %} + +%typemap(directorin) string_view %{ $input = std::string($1).c_str(); %} + +%typemap(csin) string_view "$csinput" +%typemap(csout, excode=SWIGEXCODE) string_view { + string ret = $imcall;$excode + return ret; + } + +%typemap(typecheck) string_view = char *; + +%typemap(throws, canthrow=1) string_view +%{ SWIG_CSharpSetPendingException(SWIG_CSharpApplicationException, std::string($1).c_str()); + return $null; %} + +// const string_view & +%typemap(ctype) const string_view & "const char *" +%typemap(imtype) const string_view & "string" +%typemap(cstype) const string_view & "string" + +%typemap(csdirectorin) const string_view & "$iminput" +%typemap(csdirectorout) const string_view & "$cscall" + +%typemap(in, canthrow=1) const string_view & +%{ if (!$input) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return $null; + } + $*1_ltype $1_str($input); + $1 = &$1_str; %} +%typemap(out) const string_view & %{ $result = SWIG_csharp_string_callback(std::string(*$1).c_str()); %} + +%typemap(csin) const string_view & "$csinput" +%typemap(csout, excode=SWIGEXCODE) const string_view & { + string ret = $imcall;$excode + return ret; + } + +%typemap(directorout, canthrow=1, warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const string_view & +%{ if (!$input) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return $null; + } + /* possible thread/reentrant code problem */ + static std::string $1_str; + $1_str = $input; + static $*1_ltype $1_strview; + $1_strview = $1_str; + $result = &$1_strview; %} + +%typemap(directorin) const string_view & %{ $input = std::string($1).c_str(); %} + +%typemap(csvarin, excode=SWIGEXCODE2) const string_view & %{ + set { + $imcall;$excode + } %} +%typemap(csvarout, excode=SWIGEXCODE2) const string_view & %{ + get { + string ret = $imcall;$excode + return ret; + } %} + +%typemap(typecheck) const string_view & = char *; + +%typemap(throws, canthrow=1) const string_view & +%{ SWIG_CSharpSetPendingException(SWIG_CSharpApplicationException, std::string($1).c_str()); + return $null; %} + +} diff --git a/Lib/java/std_string_view.i b/Lib/java/std_string_view.i new file mode 100644 index 000000000..005b9c0f8 --- /dev/null +++ b/Lib/java/std_string_view.i @@ -0,0 +1,138 @@ +/* ----------------------------------------------------------------------------- + * std_string_view.i + * + * Typemaps for std::string_view and const std::string_view& + * These are mapped to a Java String and are passed around by value. + * + * To use non-const std::string_view references use the following %apply. Note + * that they are passed by value. + * %apply const std::string_view & {std::string_view &}; + * ----------------------------------------------------------------------------- */ + +%{ +#include <string_view> +#include <string> +%} + +namespace std { + +%naturalvar string_view; + +class string_view; + +// string_view +%typemap(jni) string_view "jstring" +%typemap(jtype) string_view "String" +%typemap(jstype) string_view "String" +%typemap(javadirectorin) string_view "$jniinput" +%typemap(javadirectorout) string_view "$javacall" + +%typemap(in) string_view +%{ if(!$input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string"); + return $null; + } + const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0); + if (!$1_pstr) return $null; + $1 = std::string_view($1_pstr); %} + +/* std::string_view requires the string data to remain valid while the + * string_view is in use. */ +%typemap(freearg) string_view +%{ jenv->ReleaseStringUTFChars($input, $1_pstr); %} + +%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) string_view +%{ if(!$input) { + if (!jenv->ExceptionCheck()) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string"); + } + return $null; + } + const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0); + if (!$1_pstr) return $null; + /* possible thread/reentrant code problem */ + static std::string $1_str; + $1_str = $1_pstr; + $result = std::string_view($1_str); + jenv->ReleaseStringUTFChars($input, $1_pstr); %} + +/* std::string_view::data() isn't zero-byte terminated, but NewStringUTF() + * requires a zero byte so it seems we have to make a copy (ick). The + * cleanest way to do that seems to be via a temporary std::string. + */ +%typemap(directorin,descriptor="Ljava/lang/String;") string_view +%{ $input = jenv->NewStringUTF(std::string($1).c_str()); + Swig::LocalRefGuard $1_refguard(jenv, $input); %} + +%typemap(out) string_view +%{ $result = jenv->NewStringUTF(std::string($1).c_str()); %} + +%typemap(javain) string_view "$javainput" + +%typemap(javaout) string_view { + return $jnicall; + } + +%typemap(typecheck) string_view = char *; + +%typemap(throws) string_view +%{ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, std::string($1).c_str()); + return $null; %} + +// const string_view & +%typemap(jni) const string_view & "jstring" +%typemap(jtype) const string_view & "String" +%typemap(jstype) const string_view & "String" +%typemap(javadirectorin) const string_view & "$jniinput" +%typemap(javadirectorout) const string_view & "$javacall" + +%typemap(in) const string_view & +%{ if(!$input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string"); + return $null; + } + const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0); + if (!$1_pstr) return $null; + $*1_ltype $1_str($1_pstr); + $1 = &$1_str; %} + +/* std::string_view requires the string data to remain valid while the + * string_view is in use. */ +%typemap(freearg) const string_view & +%{ jenv->ReleaseStringUTFChars($input, $1_pstr); %} + +%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const string_view & +%{ if(!$input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string"); + return $null; + } + const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0); + if (!$1_pstr) return $null; + /* possible thread/reentrant code problem */ + static std::string $1_str; + $1_str = $1_pstr; + static $*1_ltype $1_strview; + $1_strview = $1_str; + $result = &$1_str; + jenv->ReleaseStringUTFChars($input, $1_pstr); %} + +%typemap(directorin,descriptor="Ljava/lang/String;") const string_view & +%{ $input = jenv->NewStringUTF(std::string($1).c_str()); + Swig::LocalRefGuard $1_refguard(jenv, $input); %} + +%typemap(out) const string_view & +%{ $result = jenv->NewStringUTF(std::string(*$1).c_str()); %} + +%typemap(javain) const string_view & "$javainput" + +%typemap(javaout) const string_view & { + return $jnicall; + } + +%typemap(typecheck) const string_view & = char *; + +%typemap(throws) const string_view & +%{ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, std::string($1).c_str()); + return $null; %} + +} diff --git a/Lib/lua/std_string_view.i b/Lib/lua/std_string_view.i new file mode 100644 index 000000000..f2a439eaa --- /dev/null +++ b/Lib/lua/std_string_view.i @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------------- + * std_string_view.i + * + * std::string_view typemaps for LUA + * ----------------------------------------------------------------------------- */ + +%{ +#include <string_view> +%} + +namespace std { + +%naturalvar string_view; + +%typemap(in,checkfn="lua_isstring") string_view +%{$1 = std::string_view(lua_tostring(L,$input),lua_rawlen(L,$input));%} + +%typemap(out) string_view +%{ lua_pushlstring(L,$1.data(),$1.size()); SWIG_arg++;%} + +%typemap(in,checkfn="lua_isstring") const string_view& ($*1_ltype temp) +%{temp = std::string_view(lua_tostring(L,$input),lua_rawlen(L,$input)); $1=&temp;%} + +%typemap(out) const string_view& +%{ lua_pushlstring(L,$1->data(),$1->size()); SWIG_arg++;%} + +// for throwing of any kind of string_view, string_view ref's and string_view pointers +// we convert all to lua strings +%typemap(throws) string_view, string_view&, const string_view& +%{ lua_pushlstring(L,$1.data(),$1.size()); SWIG_fail;%} + +%typemap(throws) string_view*, const string_view* +%{ lua_pushlstring(L,$1->data(),$1->size()); SWIG_fail;%} + +%typecheck(SWIG_TYPECHECK_STRINGVIEW) string_view, const string_view& { + $1 = lua_isstring(L,$input); +} + +class string_view; + +} diff --git a/Lib/php/std_string_view.i b/Lib/php/std_string_view.i new file mode 100644 index 000000000..7f53f9e22 --- /dev/null +++ b/Lib/php/std_string_view.i @@ -0,0 +1,69 @@ +/* ----------------------------------------------------------------------------- + * std_string_view.i + * + * SWIG typemaps for std::string_view types + * ----------------------------------------------------------------------------- */ + +%include <exception.i> + +%{ +#include <string_view> +%} + +namespace std { + + %naturalvar string_view; + + class string_view; + + %typemap(typecheck,precedence=SWIG_TYPECHECK_STRINGVIEW) string_view, const string_view& %{ + $1 = (Z_TYPE($input) == IS_STRING) ? 1 : 0; + %} + + %typemap(in, phptype="string") string_view %{ + convert_to_string(&$input); + $1 = std::string_view(Z_STRVAL($input), Z_STRLEN($input)); + %} + + %typemap(directorout) string_view %{ + convert_to_string($input); + $result = std::string_view(Z_STRVAL_P($input), Z_STRLEN_P($input)); + %} + + %typemap(out, phptype="string") string_view %{ + ZVAL_STRINGL($result, $1.data(), $1.size()); + %} + + %typemap(directorin) string_view, const string_view& %{ + ZVAL_STRINGL($input, $1.data(), $1.size()); + %} + + %typemap(out, phptype="string") const string_view& %{ + ZVAL_STRINGL($result, $1->data(), $1->size()); + %} + + %typemap(throws) string_view, const string_view& %{ + { + zval swig_exception; + ZVAL_STRINGL(&swig_exception, $1.data(), $1.size()); + zend_throw_exception_object(&swig_exception); + goto fail; + } + %} + + %typemap(throws) string_view*, const string_view* %{ + { + zval swig_exception; + ZVAL_STRINGL(&swig_exception, $1->data(), $1->size()); + zend_throw_exception_object(&swig_exception); + goto fail; + } + %} + + %typemap(in, phptype="string") const string_view& ($*1_ltype temp) %{ + convert_to_string(&$input); + temp = std::string_view(Z_STRVAL($input), Z_STRLEN($input)); + $1 = &temp; + %} + +} diff --git a/Lib/swig.swg b/Lib/swig.swg index f904f53c6..696dc8145 100644 --- a/Lib/swig.swg +++ b/Lib/swig.swg @@ -361,6 +361,11 @@ static int NAME(TYPE x) { %define SWIG_TYPECHECK_STDUNISTRING 115 %enddef %define SWIG_TYPECHECK_UNISTRING 120 %enddef %define SWIG_TYPECHECK_CHAR 130 %enddef +/* Give std::string_view a slightly higher precedence because if there are + * overloaded forms then it may be more efficient to pass as std::string_view + * (e.g. to pass as std::string requires copying the data into a std::string). + */ +%define SWIG_TYPECHECK_STRINGVIEW 134 %enddef %define SWIG_TYPECHECK_STDSTRING 135 %enddef %define SWIG_TYPECHECK_STRING 140 %enddef %define SWIG_TYPECHECK_PAIR 150 %enddef |