diff --git a/Doc/Manual/Go.html b/Doc/Manual/Go.html index 20e923d19..0be378f6a 100644 --- a/Doc/Manual/Go.html +++ b/Doc/Manual/Go.html @@ -29,6 +29,17 @@
  • Go Templates
  • Go Director Classes +
  • Default Go primitive type mappings
  • Output arguments
  • Adding additional go code @@ -574,49 +585,547 @@ In order to use C++ templates in Go, you must tell SWIG to create wrappers for a particular template instantation. To do this, use the %template directive. +

    23.4.7 Go Director Classes

    -SWIG's director feature permits a Go type to act as the subclass of a -C++ class with virtual methods. This is complicated by the fact that -C++ and Go define inheritance differently. In Go, structs can inherit -methods via anonymous field embedding. However, when a method is -called for an embedded struct, if that method calls any other methods, -they are called for the embedded struct, not for the original type. -Therefore, SWIG must use Go interfaces to represent C++ inheritance. +SWIG's director feature permits a Go type to act as the subclass of a C++ class. +This is complicated by the fact that C++ and Go define inheritance differently. +SWIG normally represents the C++ class inheritance automatically in Go via +interfaces but with a Go type representing a subclass of a C++ class quite some +manual work is necessary. +

    + +

    +This subchapter gives a step by step guide how to properly sublass a C++ class +with a Go type. In general it is strongly recommended to follow this guide +completely to avoid common pitfalls with directors in Go. +

    + + +

    23.4.7.1 Example C++ code

    + +

    +The step by step guide is based on two example C++ classes. FooBarAbs is an +abstract C++ class and the FooBarCpp class inherits from it. This guide +explains how to implement a FooBarGo class similar to the FooBarCpp class.

    -In order to use the director feature in Go, you must define a type in -your Go code. You must then add methods for the type. Define a -method in Go for each C++ virtual function that you want to override. -You must then create a value of your new type, and pass a pointer to -it to the function NewDirectorClassName, -where ClassName is the name of the C++ class. That will -return a value of type ClassName. -

    - -

    -For example: +FooBarAbs abstract C++ class:

    -type GoClass struct { }
    -func (p *GoClass) VirtualFunction() { }
    -func MakeClass() ClassName {
    -	return NewDirectorClassName(&GoClass{})
    +class FooBarAbs
    +{
    +public:
    +	FooBarAbs() {};
    +	virtual ~FooBarAbs() {};
    +
    +	std::string FooBar() {
    +		return this->Foo() + ", " + this->Bar();
    +	};
    +
    +protected:
    +	virtual std::string Foo() {
    +		return "Foo";
    +	};
    +
    +	virtual std::string Bar() = 0;
    +};
    +
    +
    + +

    +FooBarCpp C++ class: +

    + +
    +
    +class FooBarCpp : public FooBarAbs
    +{
    +protected:
    +	virtual std::string Foo() {
    +		return "C++ " + FooBarAbs::Foo();
    +	}
    +
    +	virtual std::string Bar() {
    +		return "C++ Bar";
    +	}
    +};
    +
    +
    + +

    +Returned string by the FooBarCpp::FooBar method is: +

    + +
    +
    +C++ Foo, C++ Bar
    +
    +
    + + +

    +The complete example, including the FooBarGoo class implementation, can +be found in the end of the guide. +

    + + +

    23.4.7.2 Enable director feature

    + + +

    +The director feature is disabled by default. To use directors you must make two +changes to the interface file. First, add the "directors" option to the %module +directive, like this: +

    + +
    +
    +%module(directors="1") modulename
    +
    +
    + +

    +Second, you must use the %feature("director") directive to tell SWIG which +classes should get directors. In the example the FooBarAbs class needs the +director feature enabled so that the FooBarGo class can inherit from it, like +this: +

    + +
    +
    +%feature("director") FooBarAbs;
    +
    +
    + +

    +For a more detailed documentation of the director feature and how to enable or +disable it for specific classes and virtual methods see SWIG's Java +documentation on directors. +

    + + +

    23.4.7.3 Constructor and destructor

    + + +

    +SWIG creates an additional set of constructor and destructor functions once the +director feature has been enabled for a C++ class. NewDirectorClassName + allows to override virtual methods on the new object instance and +DeleteDirectorClassName needs to be used to free a director object instance +created with NewDirectorClassName. More on overriding virtual methods +follows later in this guide under +overriding virtual methods. +

    + +

    +The default constructor and destructor functions NewClassName and +DeleteClassName can still be used as before so that existing code +doesn't break just because the director feature has been enabled for a C++ +class. The behavior is undefined if the default and director constructor and +destructor functions get mixed and so great care needs to be taken that only one +of the constructor and destructor function pairs is used for any object +instance. Both constructor functions, the default and the director one, return +the same interface type. This makes it potentially hard to know which +destructor function, the default or the director one, needs to be called to +delete an object instance. +

    + +

    +In theory the DirectorInterface method could be used to +determine if an object instance was created via NewDirectorClassName: +

    + +
    +
    +if o.DirectorInterface() != nil {
    +	DeleteDirectorClassName(o)
    +} else {
    +	DeleteClassName(o)
     }
     

    -Any call in C++ code to the virtual function will wind up calling the -method defined in Go. The Go code may of course call other methods on -itself, and those methods may be defined either in Go or in C++. +In practice it is strongly recommended to embed a director object +instance in a Go struct so that a director object instance will be represented +as a distinct Go type that subclasses a C++ class. For this Go type custom +constructor and destructor functions take care of the director constructor and +destructor function calls and the resulting Go class will appear to the user as +any other SWIG wrapped C++ class. More on properly subclassing a C++ class +follows later in this guide under subclass via +embedding.

    + +

    23.4.7.4 Override virtual methods

    + + +

    +In order to override virtual methods on a C++ class with Go methods the +NewDirectorClassName constructor functions receives a +DirectorInterface argument. The methods in the +DirectorInterface are a subset of the public and protected virtual methods +of the C++ class. If the DirectorInterface contains a method with a +matching signature to a virtual method of the C++ class then the virtual C++ +method will be overwritten with the Go method. As Go doesn't support protected +methods all overriden protected virtual C++ methods will be public in Go. +

    + +

    +As an example see part of the FooBarGo class: +

    + +
    +
    +type overwritenMethodsOnFooBarAbs struct {
    +	fb FooBarAbs
    +}
    +
    +func (om *overwritenMethodsOnFooBarAbs) Foo() string {
    +	...
    +}
    +
    +func (om *overwritenMethodsOnFooBarAbs) Bar() string {
    +	...
    +}
    +
    +func NewFooBarGo() FooBarGo {
    +	om := &overwritenMethodsOnFooBarAbs{}
    +	fb := NewDirectorFooBarAbs(om)
    +	om.fb = fb
    +	...
    +}
    +
    +
    + +

    +The complete example, including the FooBarGoo class implementation, can +be found in the end of the guide. In +this part of the example the virtual methods FooBarAbs::Foo and +FooBarAbs::Bar have been overwritten with Go methods similarly to how +the FooBarAbs virtual methods are overwritten by the FooBarCpp +class. +

    + +

    +The DirectorInterface in the example is implemented by the +overwritenMethodsOnFooBarAbs Go struct type. A pointer to a +overwritenMethodsOnFooBarAbs struct instance will be given to the +NewDirectorFooBarAbs constructor function. The constructor return +value implements the FooBarAbs interface. +overwritenMethodsOnFooBarAbs could in theory be any Go type but in +practice a struct is used as it typically contains at least a value of the +C++ class interface so that the overwritten methods can use the rest of the +C++ class. If the FooBarGo class would receive additional constructor +arguments then these would also typically be stored in the +overwritenMethodsOnFooBarAbs struct so that they can be used by the +Go methods. +

    + + +

    23.4.7.5 Call base methods

    + + +

    +Often a virtual method will be overwritten to extend the original behavior of +the method in the base class. This is also the case for the FooBarCpp::Foo + method of the example code: +

    + +
    +
    +virtual std::string Foo() {
    +	return "C++ " + FooBarAbs::Foo();
    +}
    +
    +
    + +

    +To use base methods the DirectorClassNameMethodName wrapper functions +are automatically generated by SWIG for public and protected virtual methods. +The FooBarGo.Foo implementation in the example looks like this: +

    + +
    +
    +func (om *overwritenMethodsOnFooBarAbs) Foo() string {
    +	return "Go " + DirectorFooBarAbsFoo(om.fb)
    +}
    +
    +
    + +

    +The complete example, including the FooBarGoo class implementation, can +be found in the end of the guide. +

    + + +

    23.4.7.6 Subclass via embedding

    + + +

    +As previously mentioned in this guide the +default and director constructor functions return the same interface type. To +properly subclass a C++ class with a Go type the director object instance +returned by the NewDirectorClassName constructor function should be +embedded into a Go struct so that it represents a distinct but compatible type +in Go's type system. This Go struct should be private and the constructor and +destructor functions should instead work with a public interface type so that +the Go class that subclasses a C++ class can be used as a compatible drop in. +

    + +

    +The subclassing part of the FooBarGo class for an example looks like +this: +

    + +
    +
    +type FooBarGo interface {
    +	FooBarAbs
    +	deleteFooBarAbs()
    +	IsFooBarGo()
    +}
    +
    +type fooBarGo struct {
    +	FooBarAbs
    +}
    +
    +func (fbgs *fooBarGo) deleteFooBarAbs() {
    +	DeleteDirectorFooBarAbs(fbgs.FooBarAbs)
    +}
    +
    +func (fbgs *fooBarGo) IsFooBarGo() {}
    +
    +func NewFooBarGo() FooBarGo {
    +	om := &overwritenMethodsOnFooBarAbs{}
    +	fb := NewDirectorFooBarAbs(om)
    +	om.fb = fb
    +
    +	return &fooBarGo{FooBarAbs: fb}
    +}
    +
    +func DeleteFooBarGo(fbg FooBarGo) {
    +	fbg.deleteFooBarAbs()
    +}
    +
    +
    + + +

    +The complete example, including the FooBarGoo class implementation, can +be found in the end of the guide. In +this part of the example the private fooBarGo struct embeds +FooBarAbs which lets the fooBarGo Go type "inherit" all the +methods of the FooBarAbs C++ class by means of embedding. The public +FooBarGo interface type includes the FooBarAbs interface and +hence FooBarGo can be used as a drop in replacement for +FooBarAbs while the reverse isn't possible and would raise a compile +time error. Furthemore the constructor and destructor functions +NewFooBarGo and DeleteFooBarGo take care of all the director +specifics and to the user the class appears as any other SWIG wrapped C++ +class. +

    + + +

    23.4.7.7 Memory management with runtime.SetFinalizer

    + + +

    +In general all guidelines for C++ class memory +management apply as well to director classes. One often overlooked +limitation with runtime.SetFinalizer is that a finalizer doesn't run +in case of a cycle and director classes typically have a cycle. The cycle +in the FooBarGo class is here: +

    + +
    +
    +type overwritenMethodsOnFooBarAbs struct {
    +	fb FooBarAbs
    +}
    +
    +func NewFooBarGo() FooBarGo {
    +	om := &overwritenMethodsOnFooBarAbs{}
    +	fb := NewDirectorFooBarAbs(om) // fb.v = om
    +	om.fb = fb // Backlink causes cycle as fb.v = om!
    +	...
    +}
    +
    +
    + +

    +In order to be able to use runtime.SetFinalizer nevertheless the +finalizer needs to be set on something that isn't in a cycle and that references +the director object instance. In the FooBarGo class example the +FooBarAbs director instance can be automatically deleted by setting the +finalizer on fooBarGo: +

    + +
    +
    +type fooBarGo struct {
    +	FooBarAbs
    +}
    +
    +type overwritenMethodsOnFooBarAbs struct {
    +	fb FooBarAbs
    +}
    +
    +func NewFooBarGo() FooBarGo {
    +	om := &overwritenMethodsOnFooBarAbs{}
    +	fb := NewDirectorFooBarAbs(om)
    +	om.fb = fb // Backlink causes cycle as fb.v = om!
    +
    +	fbgs := &fooBarGo{FooBarAbs: fb}
    +	runtime.SetFinalizer(fbgs, FooBarGo.deleteFooBarAbs)
    +	return fbgs
    +}
    +
    +
    + +

    +Furthermore if runtime.SetFinalizer is in use either the +DeleteClassName destructor function needs to be removed or the +fooBarGo struct needs additional data to prevent double deletion. Please +read the C++ class memory management subchapter +before using runtime.SetFinalizer to know all of its gotchas. +

    + + +

    23.4.7.8 Complete FooBarGo example class

    + + +

    +The complete and annotated FooBarGo class looks like this: +

    + +
    +
    +// FooBarGo is a superset of FooBarAbs and hence FooBarGo can be used as a drop
    +// in replacement for FooBarAbs but the reverse causes a compile time error.
    +type FooBarGo interface {
    +	FooBarAbs
    +	deleteFooBarAbs()
    +	IsFooBarGo()
    +}
    +
    +// Via embedding fooBarGo "inherits" all methods of FooBarAbs.
    +type fooBarGo struct {
    +	FooBarAbs
    +}
    +
    +func (fbgs *fooBarGo) deleteFooBarAbs() {
    +	DeleteDirectorFooBarAbs(fbgs.FooBarAbs)
    +}
    +
    +// The IsFooBarGo method ensures that FooBarGo is a superset of FooBarAbs.
    +// This is also how the class hierarchy gets represented by the SWIG generated
    +// wrapper code.  For an instance FooBarCpp has the IsFooBarAbs and IsFooBarCpp
    +// methods.
    +func (fbgs *fooBarGo) IsFooBarGo() {}
    +
    +// Go type that defines the DirectorInterface. It contains the Foo and Bar
    +// methods that overwrite the respective virtual C++ methods on FooBarAbs.
    +type overwritenMethodsOnFooBarAbs struct {
    +	// Backlink to FooBarAbs so that the rest of the class can be used by the
    +	// overridden methods.
    +	fb FooBarAbs
    +
    +	// If additional constructor arguments have been given they are typically
    +	// stored here so that the overriden methods can use them.
    +}
    +
    +func (om *overwritenMethodsOnFooBarAbs) Foo() string {
    +	// DirectorFooBarAbsFoo calls the base method FooBarAbs::Foo.
    +	return "Go " + DirectorFooBarAbsFoo(om.fb)
    +}
    +
    +func (om *overwritenMethodsOnFooBarAbs) Bar() string {
    +	return "Go Bar"
    +}
    +
    +func NewFooBarGo() FooBarGo {
    +	// Instantiate FooBarAbs with selected methods overridden.  The methods that
    +	// will be overwritten are defined on overwritenMethodsOnFooBarAbs and have
    +	// a compatible signature to the respective virtual C++ methods.
    +	// Furthermore additional constructor arguments will be typically stored in
    +	// the overwritenMethodsOnFooBarAbs struct.
    +	om := &overwritenMethodsOnFooBarAbs{}
    +	fb := NewDirectorFooBarAbs(om)
    +	om.fb = fb // Backlink causes cycle as fb.v = om!
    +
    +	fbgs := &fooBarGo{FooBarAbs: fb}
    +	// The memory of the FooBarAbs director object instance can be automatically
    +	// freed once the FooBarGo instance is garbage collected by uncommenting the
    +	// following line.  Please make sure to understand the runtime.SetFinalizer
    +	// specific gotchas before doing this.  Furthemore DeleteFooBarGo should be
    +	// deleted if a finalizer is in use or the fooBarGo struct needs additional
    +	// data to prevent double deletion.
    +	// runtime.SetFinalizer(fbgs, FooBarGo.deleteFooBarAbs)
    +	return fbgs
    +}
    +
    +// Recommended to be removed if runtime.SetFinalizer is in use.
    +func DeleteFooBarGo(fbg FooBarGo) {
    +	fbg.deleteFooBarAbs()
    +}
    +
    +
    + +

    +Returned string by the FooBarGo.FooBar method is: +

    + +
    +
    +Go Foo, Go Bar
    +
    +
    + +

    +For comparison the FooBarCpp class looks like this: +

    + +
    +
    +class FooBarCpp : public FooBarAbs
    +{
    +protected:
    +	virtual std::string Foo() {
    +		return "C++ " + FooBarAbs::Foo();
    +	}
    +
    +	virtual std::string Bar() {
    +		return "C++ Bar";
    +	}
    +};
    +
    +
    + +

    +For comparison the returned string by the FooBarCpp::FooBar method is: +

    + +
    +
    +C++ Foo, C++ Bar
    +
    +
    + +

    +The complete source of this example can be found under + +SWIG/Examples/go/director/. +

    + +

    23.4.8 Default Go primitive type mappings

    diff --git a/Examples/go/check.list b/Examples/go/check.list index 5399b8979..b3f34b306 100644 --- a/Examples/go/check.list +++ b/Examples/go/check.list @@ -2,6 +2,7 @@ callback class constants +director enum extend funcptr diff --git a/Examples/go/director/Makefile b/Examples/go/director/Makefile new file mode 100644 index 000000000..8a5fd508a --- /dev/null +++ b/Examples/go/director/Makefile @@ -0,0 +1,17 @@ +TOP = ../.. +SWIG = $(TOP)/../preinst-swig +CXXSRCS = +GOSRCS = example.go director.go # example.go gets generated by SWIG +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)' GOSRCS='$(GOSRCS)' \ + SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' go_cpp + +clean: + $(MAKE) -f $(TOP)/Makefile SRCDIR='$(SRCDIR)' INTERFACE='$(INTERFACE)' go_clean diff --git a/Examples/go/director/director.go b/Examples/go/director/director.go new file mode 100644 index 000000000..c3132d217 --- /dev/null +++ b/Examples/go/director/director.go @@ -0,0 +1,70 @@ +package example + +// FooBarGo is a superset of FooBarAbs and hence FooBarGo can be used as a drop +// in replacement for FooBarAbs but the reverse causes a compile time error. +type FooBarGo interface { + FooBarAbs + deleteFooBarAbs() + IsFooBarGo() +} + +// Via embedding fooBarGo "inherits" all methods of FooBarAbs. +type fooBarGo struct { + FooBarAbs +} + +func (fbgs *fooBarGo) deleteFooBarAbs() { + DeleteDirectorFooBarAbs(fbgs.FooBarAbs) +} + +// The IsFooBarGo method ensures that FooBarGo is a superset of FooBarAbs. +// This is also how the class hierarchy gets represented by the SWIG generated +// wrapper code. For an instance FooBarCpp has the IsFooBarAbs and IsFooBarCpp +// methods. +func (fbgs *fooBarGo) IsFooBarGo() {} + +// Go type that defines the DirectorInterface. It contains the Foo and Bar +// methods that overwrite the respective virtual C++ methods on FooBarAbs. +type overwritenMethodsOnFooBarAbs struct { + // Backlink to FooBarAbs so that the rest of the class can be used by the + // overridden methods. + fb FooBarAbs + + // If additional constructor arguments have been given they are typically + // stored here so that the overriden methods can use them. +} + +func (om *overwritenMethodsOnFooBarAbs) Foo() string { + // DirectorFooBarAbsFoo calls the base method FooBarAbs::Foo. + return "Go " + DirectorFooBarAbsFoo(om.fb) +} + +func (om *overwritenMethodsOnFooBarAbs) Bar() string { + return "Go Bar" +} + +func NewFooBarGo() FooBarGo { + // Instantiate FooBarAbs with selected methods overridden. The methods that + // will be overwritten are defined on overwritenMethodsOnFooBarAbs and have + // a compatible signature to the respective virtual C++ methods. + // Furthermore additional constructor arguments will be typically stored in + // the overwritenMethodsOnFooBarAbs struct. + om := &overwritenMethodsOnFooBarAbs{} + fb := NewDirectorFooBarAbs(om) + om.fb = fb // Backlink causes cycle as fb.v = om! + + fbgs := &fooBarGo{FooBarAbs: fb} + // The memory of the FooBarAbs director object instance can be automatically + // freed once the FooBarGo instance is garbage collected by uncommenting the + // following line. Please make sure to understand the runtime.SetFinalizer + // specific gotchas before doing this. Furthemore DeleteFooBarGo should be + // deleted if a finalizer is in use or the fooBarGo struct needs additional + // data to prevent double deletion. + // runtime.SetFinalizer(fbgs, FooBarGo.deleteFooBarAbs) + return fbgs +} + +// Recommended to be removed if runtime.SetFinalizer is in use. +func DeleteFooBarGo(fbg FooBarGo) { + fbg.deleteFooBarAbs() +} diff --git a/Examples/go/director/director.h b/Examples/go/director/director.h new file mode 100644 index 000000000..e08c11594 --- /dev/null +++ b/Examples/go/director/director.h @@ -0,0 +1,41 @@ +#ifndef DIRECTOR_H +#define DIRECTOR_H + + +#include +#include + + +class FooBarAbs +{ +public: + FooBarAbs() {}; + virtual ~FooBarAbs() {}; + + std::string FooBar() { + return this->Foo() + ", " + this->Bar(); + }; + +protected: + virtual std::string Foo() { + return "Foo"; + }; + + virtual std::string Bar() = 0; +}; + + +class FooBarCpp : public FooBarAbs +{ +protected: + virtual std::string Foo() { + return "C++ " + FooBarAbs::Foo(); + } + + virtual std::string Bar() { + return "C++ Bar"; + } +}; + + +#endif diff --git a/Examples/go/director/example.i b/Examples/go/director/example.i new file mode 100644 index 000000000..b56998e6d --- /dev/null +++ b/Examples/go/director/example.i @@ -0,0 +1,11 @@ +/* File : example.i */ +%module(directors="1") example + +%include "std_string.i" + +%header %{ +#include "director.h" +%} + +%feature("director") FooBarAbs; +%include "director.h" diff --git a/Examples/go/director/index.html b/Examples/go/director/index.html new file mode 100644 index 000000000..d1d5a74bc --- /dev/null +++ b/Examples/go/director/index.html @@ -0,0 +1,28 @@ + + +SWIG:Examples:go:director + + + + +SWIG/Examples/go/director/ +
    + +

    How to subclass a C++ class with a Go type

    + +

    +See the Go Director +Classes documentation subsection for an explanation of this example. +

    + +

    +

      +
    • director.go. Go source with the definition of the FooBarGo class. +
    • director.h. Header with the definition of the FooBarAbs and FooBarCpp classes. +
    • example.i. SWIG interface file. +
    • runme.go. Sample Go program. +
    + +
    + + diff --git a/Examples/go/director/runme.go b/Examples/go/director/runme.go new file mode 100644 index 000000000..0d839bc88 --- /dev/null +++ b/Examples/go/director/runme.go @@ -0,0 +1,39 @@ +package main + +import ( + "./example" + "fmt" + "os" +) + +func Compare(name string, got string, exp string) error { + fmt.Printf("%s; Got: '%s'; Expected: '%s'\n", name, got, exp) + if got != exp { + return fmt.Errorf("%s returned unexpected string! Got: '%s'; Expected: '%s'\n", name, got, exp) + } + return nil +} + +func TestFooBarCpp() error { + fb := example.NewFooBarCpp() + defer example.DeleteFooBarCpp(fb) + return Compare("FooBarCpp.FooBar()", fb.FooBar(), "C++ Foo, C++ Bar") +} + +func TestFooBarGo() error { + fb := example.NewFooBarGo() + defer example.DeleteFooBarGo(fb) + return Compare("FooBarGo.FooBar()", fb.FooBar(), "Go Foo, Go Bar") +} + +func main() { + fmt.Println("Test output:") + fmt.Println("------------") + err := TestFooBarCpp() + err = TestFooBarGo() + fmt.Println("------------") + if err != nil { + fmt.Fprintf(os.Stderr, "Tests failed! Last error: %s\n", err.Error()) + os.Exit(1) + } +}