diff --git a/CHANGES.current b/CHANGES.current index 04a7e277b..a4fd78430 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,24 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 3.0.12 (in progress) ============================ +2017-01-13: wsfulton + [C# D Java] Add new %proxycode directive which is a macro for %insert("proxycode"). + This is a way of adding pure C#/D/Java code into the appropriate proxy class, eg: + + %extend Proxy2 { + %proxycode %{ + public int proxycode2(int i) { + return i+2; + } + %} + } + + %inline %{ + struct Proxy2 {}; + %} + + There will then be a pure Java/C#/D method called proxycode2 in the Proxy2 class. + 2016-12-31: ajrheading1 Issue #860 - Remove use of std::unary_function and std::binary_function which is deprecated in C++11. diff --git a/Doc/Manual/Contents.html b/Doc/Manual/Contents.html index 11a90c4c3..f196da53c 100644 --- a/Doc/Manual/Contents.html +++ b/Doc/Manual/Contents.html @@ -1016,6 +1016,7 @@ diff --git a/Doc/Manual/D.html b/Doc/Manual/D.html index 1af61afd8..45b57e18b 100644 --- a/Doc/Manual/D.html +++ b/Doc/Manual/D.html @@ -174,6 +174,9 @@

dconstructor, ddestructor, ddispose and ddispose_derived are used to generate the class constructor, destructor and dispose() method, respectively. The auxiliary code for handling the pointer to the C++ object is stored in dbody and dbody_derived. You can override them for specific types.

+

+Code can also be injected into the D proxy class using %proxycode. +

22.3.7 Special variable macros

diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index bff0b186b..05fc9b930 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -100,6 +100,7 @@ @@ -4363,7 +4364,144 @@ Vector(2, 3, 4) in any way---the extensions only show up in the Java interface.

-

25.7.3 Exception handling with %exception and %javaexception

+

25.7.3 Class extension with %proxycode

+ + +

+The previous section described how to extend a wrapped class with C or C++ code. +This section describes how to extend a wrapped class with Java code instead of C/C++ code. +The %proxycode directive is used and is just a macro for %insert("proxycode"). +The Code insertion block section describes the %insert directive. +The section of code for insertion is "proxycode", that is, the Java proxy class. +This directive must hence only be used within the scope of a class, otherwise it is silently ignored. +There are two common ways to get the scope correct. +

+ +

+The first is to use %proxycode inside a class that SWIG parses, for example a toString() method can be added to a C++ class using pure Java code. +A C++ header file can mix C++ and Java code inside the C++ class as follows: +

+ +
+
+// flag.h header file
+class Flag {
+  bool flag;
+public:
+  Flag(bool flag) : flag(flag) {}
+  bool FetchFlag() { return flag; }
+#if defined(SWIG)
+%proxycode %{
+  public String toString() {
+    boolean flag = FetchFlag();
+    return Boolean.toString(flag);
+  }
+%}
+#endif
+};
+
+
+ +

+and wrapped using: +

+ +
+
+%{
+#include "flag.h"
+%}
+%include "flag.h"
+
+
+ +

+The second is to use %proxycode within %extend as everything within a %extend block is effectively within the scope of the class, for example: +

+ +
+
+// flag.h header file
+class Flag {
+  bool flag;
+public:
+  Flag(bool flag) : flag(flag) {}
+  bool FetchFlag() { return flag; }
+};
+
+
+ +

+and wrapped using: +

+ +
+
+%{
+#include "flag.h"
+%}
+%include "flag.h"
+
+%extend Flag {
+#if defined(SWIG)
+%proxycode %{
+  public String toString() {
+    boolean flag = FetchFlag();
+    return Boolean.toString(flag);
+  }
+%}
+#endif
+}
+
+
+ +

+There is some very limited support of typemaps within a %proxycode block. +A useful trick is to obtain the Java type for a given C/C++ type using the $typemap special macro. +The following C++ template demonstrates this: +

+ +
+
+%inline %{
+template<typename T> struct Value {
+  T value;
+  Value(const T& val) : value(val) {}
+};
+%}
+
+%extend Value {
+%proxycode %{
+  public String toString() {
+    // Note template type expansion is supported, so T is expanded to 'unsigned int' in this example
+    // and $typemap(jstype, unsigned int) in turn is expanded to 'long'
+    $typemap(jstype, T) val = getValue();
+    return "$javaclassname value: " + val + " Java type: $typemap(jstype, T) JNI type: $typemap(jni, T)";
+  }
+%}
+}
+%template(ValueUnsignedInt) Value<unsigned int>;
+
+
+ +

+The generated Java contains the expanded special variable and macro resulting in Java proxy code: +

+ +
+
+public class ValueUnsignedInt {
+  ...
+  public String toString() {
+    long val = getValue();
+    return "ValueUnsignedInt value: " + val + " Java type: long JNI type: jlong";
+  }
+
+}
+
+
+ +

25.7.4 Exception handling with %exception and %javaexception

@@ -4522,7 +4660,7 @@ to raise exceptions. See the SWIG Library ch The typemap example Handling C++ exception specifications as Java exceptions provides further exception handling capabilities.

-

25.7.4 Method access with %javamethodmodifiers

+

25.7.5 Method access with %javamethodmodifiers

diff --git a/Doc/Manual/SWIG.html b/Doc/Manual/SWIG.html index 8b9aa8257..e5b441fbb 100644 --- a/Doc/Manual/SWIG.html +++ b/Doc/Manual/SWIG.html @@ -3174,6 +3174,7 @@ the module upon loading.

5.6.2 Code insertion blocks

+

The %insert directive enables inserting blocks of code into a given section of the generated code. It can be used in one of two ways: diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index e3881afd6..fb5aaa597 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -335,6 +335,7 @@ CPP_TEST_CASES += \ preproc_constants \ primitive_ref \ private_assign \ + proxycode \ protected_rename \ pure_virtual \ redefined \ diff --git a/Examples/test-suite/csharp/proxycode_runme.cs b/Examples/test-suite/csharp/proxycode_runme.cs new file mode 100644 index 000000000..24e76c0c6 --- /dev/null +++ b/Examples/test-suite/csharp/proxycode_runme.cs @@ -0,0 +1,33 @@ +using System; +using proxycodeNamespace; + +public class proxycode_runme { + + public static void Main() { + if (new Proxy1().proxycode1(100) != 101) + throw new Exception("Fail"); + if (new Proxy2().proxycode2a(100) != 102) + throw new Exception("Fail"); + if (new Proxy2().proxycode2b(100) != 102) + throw new Exception("Fail"); + if (new Proxy3().proxycode3(100) != 103) + throw new Exception("Fail"); + + if (new Proxy4().proxycode4(100) != 104) + throw new Exception("Fail"); + if (new Proxy4.Proxy4Nested().proxycode4nested(100) != 144) + throw new Exception("Fail"); + + if (new Proxy5a().proxycode5((short)100) != (short)100) + throw new Exception("Fail"); + if (new Proxy5b().proxycode5(100) != 100) + throw new Exception("Fail"); + if (new Proxy5b().proxycode5(100, 100) != 255) + throw new Exception("Fail"); + + uint t1 = 10; + uint t2 = 100; + Proxy6 p = new Proxy6().proxyUseT(t1, t2); + p.useT(t1, t2); + } +} diff --git a/Examples/test-suite/d/proxycode_runme.2.d b/Examples/test-suite/d/proxycode_runme.2.d new file mode 100644 index 000000000..133217f2e --- /dev/null +++ b/Examples/test-suite/d/proxycode_runme.2.d @@ -0,0 +1,41 @@ +module proxycode_runme; + +import std.exception; +import proxycode.Proxy1; +import proxycode.Proxy2; +import proxycode.Proxy3; +import proxycode.Proxy4; +import proxycode.Proxy5a; +import proxycode.Proxy5b; +import proxycode.Proxy6; + +void main() { + if (new Proxy1().proxycode1(100) != 101) + throw new Exception("Fail"); + + if (new Proxy1().proxycode1(100) != 101) + throw new Exception("Fail"); + if (new Proxy2().proxycode2a(100) != 102) + throw new Exception("Fail"); + if (new Proxy2().proxycode2b(100) != 102) + throw new Exception("Fail"); + if (new Proxy3().proxycode3(100) != 103) + throw new Exception("Fail"); + + if (new Proxy4().proxycode4(100) != 104) + throw new Exception("Fail"); +// if (new Proxy4.Proxy4Nested().proxycode4nested(100) != 144) +// throw new Exception("Fail"); + + if (new Proxy5a().proxycode5(100) != 100) + throw new Exception("Fail"); + if (new Proxy5b().proxycode5(100) != 100) + throw new Exception("Fail"); + if (new Proxy5b().proxycode5(100, 100) != 255) + throw new Exception("Fail"); + + uint t1 = 10; + uint t2 = 100; + Proxy6 p = new Proxy6().proxyUseT(t1, t2); + p.useT(t1, t2); +} diff --git a/Examples/test-suite/java/proxycode_runme.java b/Examples/test-suite/java/proxycode_runme.java new file mode 100644 index 000000000..cf981e723 --- /dev/null +++ b/Examples/test-suite/java/proxycode_runme.java @@ -0,0 +1,42 @@ +import proxycode.*; + +public class proxycode_runme { + + static { + try { + System.loadLibrary("proxycode"); + } 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 + { + if (new Proxy1().proxycode1(100) != 101) + throw new RuntimeException("Fail"); + if (new Proxy2().proxycode2a(100) != 102) + throw new RuntimeException("Fail"); + if (new Proxy2().proxycode2b(100) != 102) + throw new RuntimeException("Fail"); + if (new Proxy3().proxycode3(100) != 103) + throw new RuntimeException("Fail"); + + if (new Proxy4().proxycode4(100) != 104) + throw new RuntimeException("Fail"); + if (new Proxy4.Proxy4Nested().proxycode4nested(100) != 144) + throw new RuntimeException("Fail"); + + if (new Proxy5a().proxycode5((short)100) != (short)100) + throw new RuntimeException("Fail"); + if (new Proxy5b().proxycode5(100) != 100) + throw new RuntimeException("Fail"); + if (new Proxy5b().proxycode5(100, 100) != 255) + throw new RuntimeException("Fail"); + + long t1 = 10; + long t2 = 100; + Proxy6 p = new Proxy6().proxyUseT(t1, t2); + p.useT(t1, t2); + } +} diff --git a/Examples/test-suite/proxycode.i b/Examples/test-suite/proxycode.i new file mode 100644 index 000000000..02dab9ade --- /dev/null +++ b/Examples/test-suite/proxycode.i @@ -0,0 +1,141 @@ +%module proxycode + +%warnfilter(SWIGWARN_PARSE_NAMED_NESTED_CLASS) Proxy4::Proxy4Nested; + +#if defined(SWIGJAVA) || defined(SWIGCSHARP) || defined(SWIGD) + +%{ +struct Proxy1 {}; +%} +struct Proxy1 { +%proxycode %{ + public int proxycode1(int i) { + return i+1; + } +%} +}; + +%proxycode %{ + this should be ignored as it is not in scope of a class +%} + +%extend Proxy2 { +%proxycode %{ + public int proxycode2a(int i) { + return i+2; + } +%} +} + +%extend Proxy2 { +%proxycode %{ + public int proxycode2b(int i) { + return i+2; + } +%} +} + +%inline %{ +struct Proxy2 {}; +struct Proxy3 {}; +struct Proxy4 { + struct Proxy4Nested {}; +}; +%} + +%extend Proxy3 { +%proxycode %{ + public int proxycode3(int i) { + return i+3; + } +%} +} + +%extend Proxy4 { +%proxycode %{ + public int proxycode4(int i) { + return i+4; + } +%} +} +%extend Proxy4::Proxy4Nested { +%proxycode %{ + public int proxycode4nested(int i) { + return i+44; + } +%} +} + +%extend TemplateProxy { +%proxycode %{ + public T proxycode5(T i) { + return i; + } +%} +} + +%extend TemplateProxy { +%proxycode %{ + public int proxycode5(int i, int j) { + return i+j+55; + } +%} +} + +%inline %{ +template struct TemplateProxy {}; +%} + +%template(Proxy5a) TemplateProxy; +%template(Proxy5b) TemplateProxy; + +%inline %{ +template struct TypemapProxy { + T useT(T t1, T const& t2) { + return t1+t2; + } +}; +%} + +%extend TypemapProxy { +#if defined(SWIGJAVA) +%proxycode %{ + public $javaclassname proxyUseT(long t1, long t2) { + $typemap(jstype, unsigned int) tt1 = t1; + $typemap(jstype, unsigned int const&) tt2 = t2; + long ret = useT(tt1, tt2); + if (ret != t1+t2) + throw new RuntimeException("wrong sum"); + return this; + } +%} +#elif defined(SWIGCSHARP) +%proxycode %{ + public $csclassname proxyUseT(uint t1, uint t2) { + $typemap(cstype, unsigned int) tt1 = t1; + $typemap(cstype, unsigned int const&) tt2 = t2; + uint ret = useT(tt1, tt2); + if (ret != t1+t2) + throw new System.Exception("wrong sum"); + return this; + } +%} +#elif defined(SWIGD) +%proxycode %{ + public $dclassname proxyUseT(uint t1, uint t2) { + $typemap(dtype, unsigned int) tt1 = t1; + $typemap(dtype, unsigned int const&) tt2 = t2; + uint ret = useT(tt1, tt2); + if (ret != t1+t2) + throw new Exception("wrong sum"); + return this; + } +%} +#else +#error "missing test" +#endif +} + +%template(Proxy6) TypemapProxy; + +#endif diff --git a/Lib/csharp/csharp.swg b/Lib/csharp/csharp.swg index a703899d8..8dc2ba813 100644 --- a/Lib/csharp/csharp.swg +++ b/Lib/csharp/csharp.swg @@ -995,6 +995,7 @@ SWIG_CSBODY_TYPEWRAPPER(internal, protected, internal, SWIGTYPE) #define %csmethodmodifiers %feature("cs:methodmodifiers") #define %csnothrowexception %feature("except") #define %csattributes %feature("cs:attributes") +#define %proxycode %insert("proxycode") %pragma(csharp) imclassclassmodifiers="class" %pragma(csharp) moduleclassmodifiers="public class" diff --git a/Lib/d/ddirectives.swg b/Lib/d/ddirectives.swg index 6972a0c18..145cf01f0 100644 --- a/Lib/d/ddirectives.swg +++ b/Lib/d/ddirectives.swg @@ -8,3 +8,4 @@ #define %dconstvalue(value) %feature("d:constvalue",value) #define %dmethodmodifiers %feature("d:methodmodifiers") #define %dnothrowexception %feature("except") +#define %proxycode %insert("proxycode") diff --git a/Lib/java/java.swg b/Lib/java/java.swg index f1227d0ad..94fbe6ded 100644 --- a/Lib/java/java.swg +++ b/Lib/java/java.swg @@ -1317,6 +1317,7 @@ SWIG_PROXY_CONSTRUCTOR(true, true, SWIGTYPE) #define %javaexception(exceptionclasses) %feature("except",throws=exceptionclasses) #define %nojavaexception %feature("except","0",throws="") #define %clearjavaexception %feature("except","",throws="") +#define %proxycode %insert("proxycode") %pragma(java) jniclassclassmodifiers="public class" %pragma(java) moduleclassmodifiers="public class" diff --git a/Source/Modules/csharp.cxx b/Source/Modules/csharp.cxx index 086e626f6..0d198f639 100644 --- a/Source/Modules/csharp.cxx +++ b/Source/Modules/csharp.cxx @@ -1584,11 +1584,23 @@ public: * ----------------------------------------------------------------------------- */ virtual int insertDirective(Node *n) { + int ret = SWIG_OK; String *code = Getattr(n, "code"); + String *section = Getattr(n, "section"); Replaceall(code, "$module", module_class_name); Replaceall(code, "$imclassname", imclass_name); Replaceall(code, "$dllimport", dllimport); - return Language::insertDirective(n); + + if (!ImportMode && (Cmp(section, "proxycode") == 0)) { + if (proxy_class_code) { + Swig_typemap_replace_embedded_typemap(code, n); + int offset = Len(code) > 0 && *Char(code) == '\n' ? 1 : 0; + Printv(proxy_class_code, Char(code) + offset, "\n", NIL); + } + } else { + ret = Language::insertDirective(n); + } + return ret; } /* ----------------------------------------------------------------------------- diff --git a/Source/Modules/d.cxx b/Source/Modules/d.cxx index ec66ebed2..de7afdab1 100644 --- a/Source/Modules/d.cxx +++ b/Source/Modules/d.cxx @@ -717,9 +717,20 @@ public: * D::insertDirective() * --------------------------------------------------------------------------- */ virtual int insertDirective(Node *n) { + int ret = SWIG_OK; String *code = Getattr(n, "code"); + String *section = Getattr(n, "section"); replaceModuleVariables(code); - return Language::insertDirective(n); + + if (!ImportMode && (Cmp(section, "proxycode") == 0)) { + if (proxy_class_body_code) { + Swig_typemap_replace_embedded_typemap(code, n); + Printv(proxy_class_body_code, code, NIL); + } + } else { + ret = Language::insertDirective(n); + } + return ret; } /* --------------------------------------------------------------------------- diff --git a/Source/Modules/java.cxx b/Source/Modules/java.cxx index eb809ff59..9808476eb 100644 --- a/Source/Modules/java.cxx +++ b/Source/Modules/java.cxx @@ -1612,10 +1612,22 @@ public: * ----------------------------------------------------------------------------- */ virtual int insertDirective(Node *n) { + int ret = SWIG_OK; String *code = Getattr(n, "code"); + String *section = Getattr(n, "section"); Replaceall(code, "$module", module_class_name); Replaceall(code, "$imclassname", imclass_name); - return Language::insertDirective(n); + + if (!ImportMode && (Cmp(section, "proxycode") == 0)) { + if (proxy_class_code) { + Swig_typemap_replace_embedded_typemap(code, n); + int offset = Len(code) > 0 && *Char(code) == '\n' ? 1 : 0; + Printv(proxy_class_code, Char(code) + offset, "\n", NIL); + } + } else { + ret = Language::insertDirective(n); + } + return ret; } /* ----------------------------------------------------------------------------- diff --git a/Source/Swig/swig.h b/Source/Swig/swig.h index 35a67640f..f25f0993e 100644 --- a/Source/Swig/swig.h +++ b/Source/Swig/swig.h @@ -400,6 +400,7 @@ extern int ParmList_is_compactdefargs(ParmList *p); extern void Swig_typemap_clear(const_String_or_char_ptr tmap_method, ParmList *pattern); extern int Swig_typemap_apply(ParmList *srcpat, ParmList *destpat); extern void Swig_typemap_clear_apply(ParmList *pattern); + extern void Swig_typemap_replace_embedded_typemap(String *s, Node *file_line_node); extern void Swig_typemap_debug(void); extern void Swig_typemap_search_debug_set(void); extern void Swig_typemap_used_debug_set(void); diff --git a/Source/Swig/typemap.c b/Source/Swig/typemap.c index ab2a8c0df..8970c719d 100644 --- a/Source/Swig/typemap.c +++ b/Source/Swig/typemap.c @@ -1876,6 +1876,21 @@ static List *split_embedded_typemap(String *s) { return args; } +/* ----------------------------------------------------------------------------- + * Swig_typemap_replace_embedded_typemap() + * + * For special variable macro $typemap(...) expansion outside of typemaps. + * Only limited usage works as most typemap special variables ($1, $input etc) + * are not expanded correctly outside of typemaps. + * ----------------------------------------------------------------------------- */ + +void Swig_typemap_replace_embedded_typemap(String *s, Node *file_line_node) { + Setfile(s, Getfile(file_line_node)); + Setline(s, Getline(file_line_node)); + Replaceall(s, "$typemap", "$TYPEMAP"); + replace_embedded_typemap(s, 0, 0, file_line_node); +} + /* ----------------------------------------------------------------------------- * replace_embedded_typemap() *