From fb4c69b4b02c1b3fac28624c16e684ae77de91fc Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 18 Mar 2022 21:40:11 -0500 Subject: [PATCH 1/4] [Go] Add missing newlines for goin typemaps In some cases the \n was missing from goin directives in some places. Without this change, you will get } swig_r = (int)(C.... in the output in some cases, and that won't compile in Go, it will error with: ../gensio/gensio.go:1508:3: expected ';', found swig_r It only did this on director callbacks with godirectorin, and only if the virtual function wasn't assigned to zero. So the following will fail: class callbacks { public: virtual bool call1(int v, const char * const *strarray); virtual ~callbacks() = default; }; but the following won't: class callbacks { public: virtual bool call1(int v, const char * const *strarray) = 0; virtual ~callbacks() = default; }; Add "\n" to the proper places to fix this. --- Source/Modules/go.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Modules/go.cxx b/Source/Modules/go.cxx index 071d39ec7..d828b00aa 100644 --- a/Source/Modules/go.cxx +++ b/Source/Modules/go.cxx @@ -3518,7 +3518,7 @@ private: goin = Copy(goin); Replaceall(goin, "$input", ln); Replaceall(goin, "$result", ivar); - Printv(f_go_wrappers, goin, NULL); + Printv(f_go_wrappers, goin, "\n", NULL); Delete(goin); } @@ -3772,7 +3772,7 @@ private: goin = Copy(goin); Replaceall(goin, "$input", ln); Replaceall(goin, "$result", ivar); - Printv(f_go_wrappers, goin, NULL); + Printv(f_go_wrappers, goin, "\n", NULL); Delete(goin); } From 546763ffbc333b98808802fff0d601d018763d0c Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Sun, 20 Mar 2022 15:04:27 -0500 Subject: [PATCH 2/4] [Go] Add an example for goin and godirectorin Shows how to use a go directory for passing an NULL terminated argv type array. This also exercises the bug in the previous commit. That's why the call1 function isn't assigned to zero, that the only case where that bug happens. Signed-off-by: Corey Minyard --- Examples/go/check.list | 1 + Examples/go/goin/Makefile | 18 ++++++ Examples/go/goin/example.i | 106 ++++++++++++++++++++++++++++++++++++ Examples/go/goin/index.html | 26 +++++++++ Examples/go/goin/runme.go | 38 +++++++++++++ Examples/go/index.html | 1 + 6 files changed, 190 insertions(+) create mode 100644 Examples/go/goin/Makefile create mode 100644 Examples/go/goin/example.i create mode 100644 Examples/go/goin/index.html create mode 100644 Examples/go/goin/runme.go diff --git a/Examples/go/check.list b/Examples/go/check.list index b3f34b306..6046c8310 100644 --- a/Examples/go/check.list +++ b/Examples/go/check.list @@ -12,3 +12,4 @@ reference simple template variables +goin diff --git a/Examples/go/goin/Makefile b/Examples/go/goin/Makefile new file mode 100644 index 000000000..f79b083cb --- /dev/null +++ b/Examples/go/goin/Makefile @@ -0,0 +1,18 @@ +TOP = ../.. +SWIGEXE = $(TOP)/../swig +SWIG_LIB_DIR = $(TOP)/../$(TOP_BUILDDIR_TO_TOP_SRCDIR)Lib +CXXSRCS = +TARGET = example +INTERFACE = example.i +SWIGOPT = + +check: build + $(MAKE) -f $(TOP)/Makefile SRCDIR='$(SRCDIR)' CXXSRCS='$(CXXSRCS)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' go_run + +build: + $(MAKE) -f $(TOP)/Makefile SRCDIR='$(SRCDIR)' CXXSRCS='$(CXXSRCS)' \ + SWIG_LIB_DIR='$(SWIG_LIB_DIR)' SWIGEXE='$(SWIGEXE)' \ + SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' go_cpp + +clean: + $(MAKE) -f $(TOP)/Makefile SRCDIR='$(SRCDIR)' INTERFACE='$(INTERFACE)' go_clean diff --git a/Examples/go/goin/example.i b/Examples/go/goin/example.i new file mode 100644 index 000000000..1d8289af8 --- /dev/null +++ b/Examples/go/goin/example.i @@ -0,0 +1,106 @@ + +%module(directors="1") example + +%inline %{ +// Helper functions for converting string arrays +#include +void *alloc_ptr_array(unsigned int len) +{ + return calloc(len, sizeof(void *)); +} +void set_ptr_array(void *ain, unsigned int pos, void *val) +{ + void **a = (void **) ain; + a[pos] = val; +} +void *get_ptr_array(void *ain, unsigned int pos) +{ + void **a = (void **) ain; + return a[pos]; +} +void free_ptr_array(void *ain) +{ + void **a = (void **) ain; + unsigned int i; + + if (!a) + return; + for (i = 0; a[i]; i++) { + free(a[i]); + } + free(a); +} +char *uintptr_to_string(void *in) +{ + return (char *) in; +} +void *string_to_uintptr(char *in) +{ + return strdup(in); +} +%} + +// These typemaps convert between an array of strings in Go and a +// const char** that is NULL terminated in C++. +%typemap(gotype) (const char * const *) "[]string"; +%typemap(imtype) (const char * const *) "uintptr"; +%typemap(goin) (const char * const *) { + if $input == nil || len($input) == 0 { + $result = 0 + } else { + $result = Alloc_ptr_array(uint(len($input) + 1)) + defer func() { + Free_ptr_array($result) + }() + var i uint + for i = 0; i < uint(len($input)); i++ { + Set_ptr_array($result, i, String_to_uintptr($input[i])) + } + } +} +%typemap(in) (const char * const *) { + $1 = (char **) $input; +} +%typemap(godirectorin) (const char * const *) { + if ($input == 0) { + $result = nil + } else { + var i uint + for i = 0; ; i++ { + var v uintptr = Get_ptr_array($input, i) + if v == 0 { + break + } + } + if i == 0 { + $result = nil + } else { + $result = make([]string, i) + for i = 0; ; i++ { + var v uintptr = Get_ptr_array($input, i) + if v == 0 { + break + } + $result[i] = Uintptr_to_string(v) + } + } + } +} + +%feature("director") callbacks; + +%inline %{ + class callbacks { + public: + virtual bool call1(int v, const char * const *strarray); + virtual ~callbacks() {} + }; + + bool check1(callbacks *c, int v, const char * const *strarray) { + return c->call1(v, strarray); + } + + bool callbacks::call1(int v, const char * const *strarray) { + return false; + } +%} diff --git a/Examples/go/goin/index.html b/Examples/go/goin/index.html new file mode 100644 index 000000000..852b068da --- /dev/null +++ b/Examples/go/goin/index.html @@ -0,0 +1,26 @@ + + +SWIG:Examples:go:going + + + + + +SWIG/Examples/go/goin/ +
+ +

Example of using goin and godirectorin

+ +

+This example converts between a Go []string and a "const char * const *" +in C/C++. It does this for a director and for a normal call. + +

+

+ +
+ + diff --git a/Examples/go/goin/runme.go b/Examples/go/goin/runme.go new file mode 100644 index 000000000..dfc7b0936 --- /dev/null +++ b/Examples/go/goin/runme.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "swigtests/example" +) + +type mycallbacks struct { + example.Callbacks +} + +var tststrs = []string{ "A", "BCD", "EFGH" } +var tstint int = 5 + +func (v *mycallbacks) Call1(val int, strarray []string) bool { + var rv bool = true + + for i, s := range strarray { + fmt.Printf("%d: %s\n", i, s) + if s != tststrs[i] { + fmt.Printf(" ***Mismatch, expected %s\n", tststrs[i]) + rv = false + } + } + if val != tstint { + rv = false + } + return rv +} + +func main() { + cbs := &mycallbacks{} + cbs.Callbacks = example.NewDirectorCallbacks(cbs) + worked := example.Check1(cbs, tstint, tststrs) + if !worked { + panic("Data mismatch") + } +} diff --git a/Examples/go/index.html b/Examples/go/index.html index 467f4ecb7..370424c54 100644 --- a/Examples/go/index.html +++ b/Examples/go/index.html @@ -24,6 +24,7 @@ certain C declarations are turned into constants.
  • callback. C++ callbacks using directors.
  • extend. Polymorphism using directors.
  • director. Example how to utilize the director feature. +
  • director. Example how to use goin and godirectorin.

    Compilation Issues

    From 3cc67d42f39b816e04d4d31fa624923c094a6ff0 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 18 Apr 2022 19:53:59 -0500 Subject: [PATCH 3/4] [Go] Add a test for godirectorin with const char ** This exercises a bug that was found with this fairly complex mapping, it wasn't putting newlines in the proper place. A previous commit added the newlines, this makes sure it doesn't happen again. Signed-off-by: Corey Minyard --- .../test-suite/go/go_director_inout_runme.go | 11 ++ Examples/test-suite/go_director_inout.i | 102 +++++++++++++++++- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/Examples/test-suite/go/go_director_inout_runme.go b/Examples/test-suite/go/go_director_inout_runme.go index f2be9d30b..55f32dfc3 100644 --- a/Examples/test-suite/go/go_director_inout_runme.go +++ b/Examples/test-suite/go/go_director_inout_runme.go @@ -34,6 +34,15 @@ func (p *GoMyClass) S3(s *string) { *s = "R3" } +func (p *GoMyClass) S4(s []string) { + if s[0] != "T1" { + panic(s) + } + if s[1] != "T2" { + panic(s) + } +} + func main() { a := wrap.NewDirectorMyClass(&GoMyClass{}) m := map[string]interface{}{ @@ -56,6 +65,8 @@ func main() { panic(str) } + a.CallS4([]string{ "T1", "T2" }) + a = wrap.NewDirectorMyClass(nil) s = a.Adjust(m) if s.Str != `{"first":"second"}` { diff --git a/Examples/test-suite/go_director_inout.i b/Examples/test-suite/go_director_inout.i index 38059ea40..b12e5da52 100644 --- a/Examples/test-suite/go_director_inout.i +++ b/Examples/test-suite/go_director_inout.i @@ -120,6 +120,92 @@ type GoRetStruct struct { $1.assign(tmp->p, tmp->p + tmp->n); } +%inline %{ +// Helper functions for converting string arrays +#include +void *alloc_ptr_array(unsigned int len) +{ + return calloc(len, sizeof(void *)); +} +void set_ptr_array(void *ain, unsigned int pos, void *val) +{ + void **a = (void **) ain; + a[pos] = val; +} +void *get_ptr_array(void *ain, unsigned int pos) +{ + void **a = (void **) ain; + return a[pos]; +} +void free_ptr_array(void *ain) +{ + void **a = (void **) ain; + unsigned int i; + + if (!a) + return; + for (i = 0; a[i]; i++) { + free(a[i]); + } + free(a); +} +char *uintptr_to_string(void *in) +{ + return (char *) in; +} +void *string_to_uintptr(char *in) +{ + return strdup(in); +} +%} + +// These typemaps convert between an array of strings in Go and a +// const char** that is NULL terminated in C++. +%typemap(gotype) (const char * const *) "[]string"; +%typemap(imtype) (const char * const *) "uintptr"; +%typemap(goin) (const char * const *) { + if $input == nil || len($input) == 0 { + $result = 0 + } else { + $result = Alloc_ptr_array(uint(len($input) + 1)) + defer func() { + Free_ptr_array($result) + }() + var i uint + for i = 0; i < uint(len($input)); i++ { + Set_ptr_array($result, i, String_to_uintptr($input[i])) + } + } +} +%typemap(in) (const char * const *) { + $1 = (char **) $input; +} +%typemap(godirectorin) (const char * const *) { + if ($input == 0) { + $result = nil + } else { + var i uint + for i = 0; ; i++ { + var v uintptr = Get_ptr_array($input, i) + if v == 0 { + break + } + } + if i == 0 { + $result = nil + } else { + $result = make([]string, i) + for i = 0; ; i++ { + var v uintptr = Get_ptr_array($input, i) + if v == 0 { + break + } + $result[i] = Uintptr_to_string(v) + } + } + } +} + %feature("director") MyClass; %inline @@ -134,9 +220,23 @@ class MyClass { return r; } - virtual void S1(std::string s) = 0; + void CallS4(const char * const *strarray); + virtual void S1(std::string s); virtual void S2(std::string& s) = 0; virtual void S3(std::string* s) = 0; + virtual void S4(const char * const *strarray); }; +void MyClass::S1(std::string s) { + throw "Base S1 called!"; +} + +void MyClass::S4(const char * const *strarray) { + throw "Base S4 called!"; +} + +void MyClass::CallS4(const char * const *strarray) { + this->S4(strarray); +} + %} From 537669ba3dc97e0e6033160c9a736b63d0f9427f Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 20 Apr 2022 17:54:15 -0500 Subject: [PATCH 4/4] CHANGES.current: Add an entry for the godirectorin bug Document what was fixed in the previous few changes. Signed-off-by: Corey Minyard --- CHANGES.current | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.current b/CHANGES.current index 234e730c3..0c6db7c90 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,17 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.1.0 (in progress) =========================== +2022-04-20: cminyard + Fix an issue where newlines were not properly generated + for godirectorin typemaps. If you have a virtual function + not assigned to zero, in some cases it won't generate a + newline and you will see errors: + example.go:1508:3: expected ';', found swig_r + when compiling the go code. + + Also add an example of using goin and godirectorin and add + a test for this situation. + 2022-04-11: robinst #2257 Fix new Ruby 3.2 warning "undefining the allocator of T_DATA class swig_runtime_data".