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 @@
-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. +
+ + ++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. +
+ + ++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. +
+ + ++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.
+ ++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. +
+ + ++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. +
+ + ++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. +
+ + ++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. +
+ + ++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/. +
+ ++See the Go Director +Classes documentation subsection for an explanation of this example. +
+ ++