From 255715d9ad38d9d1faf265f890712a99b540f9f8 Mon Sep 17 00:00:00 2001
From: Michael Schaller
-Calling NewClassName for some C++ class ClassName
-will allocate memory using the C++ memory allocator. This memory will
-not be automatically freed by Go's garbage collector as the object ownership is
-not tracked. When you are done with the C++ object you must free it manually
-using DeleteClassName.
-
-A common technique is to store the C++ object into a Go object, and
-use the Go function runtime.SetFinalizer to free the C++ object when
-the Go object is freed. It is strongly recommended to read the
-runtime.SetFinalizer
-documentation before using this technique to understand its limitations.
-For example, if the SWIG package is imported as "wrap":
+Calling NewClassName for a C++ class ClassName will allocate
+memory using the C++ memory allocator. This memory will not be automatically
+freed by Go's garbage collector as the object ownership is not tracked. When
+you are done with the C++ object you must free it using
+DeleteClassName.
+With increasing complexity, especially complex C++ object hierarchies, the
+correct placement of defer statements becomes harder and harder as C++
+objects need to be freed in the correct order. This problem can be eased by
+keeping a C++ object function local so that it is only available to the function
+that creates a C++ object and functions called by this function. Example:
+
+Using defer has limitations though, especially when it comes to
+long-lived C++ objects whichs lifetimes are hard to predict. For such C++
+objects a common technique is to store the C++ object into a Go object, and to
+use the Go function runtime.SetFinalizer to add a finalizer which frees
+the C++ object when the Go object is freed. It is strongly recommended to read
+the runtime.SetFinalizer
+ documentation before using this technique to understand the
+runtime.SetFinalizer limitations.
+
+The most Go idiomatic way to manage the memory for some C++ class is to call
+NewClassName followed by a
+defer of
+the DeleteClassName call. Using defer ensures that the memory
+of the C++ object is freed as soon as the function containing the defer
+statement returns. Furthemore defer works great for short-lived
+objects and fits nicely C++'s RAII idiom. Example:
+func UseClassName(...) ... {
+ o := NewClassName(...)
+ defer DeleteClassName(o)
+ // Use the ClassName object
+ return ...
+}
+
+
+func WithClassName(constructor args, f func(ClassName, ...interface{}) error, data ...interface{}) error {
+ o := NewClassName(constructor args)
+ defer DeleteClassName(o)
+ return f(o, data...)
+}
+
+func UseClassName(o ClassName, data ...interface{}) (err error) {
+ // Use the ClassName object and additional data and return error.
+}
+
+func main() {
+ WithClassName(constructor args, UseClassName, additional data)
+}
+
+
+
+Common pitfalls with runtime.SetFinalizer are:
+
+
+
+runtime.SetFinalizer Example: +
+
+import (
+ "runtime"
+ "wrap" // SWIG generated wrapper code
+)
+
type GoClassName struct {
- w wrap.ClassName
+ wcn wrap.ClassName
}
func NewGoClassName() *GoClassName {
- r := &GoClassName{wrap.NewClassName()}
- runtime.SetFinalizer(r,
- func(r *GoClassName) {
- wrap.DeleteClassName(r.w)
- })
- return r
+ o := &GoClassName{wcn: wrap.NewClassName()}
+ runtime.SetFinalizer(o, deleteGoClassName)
+ return o
+}
+
+func deleteGoClassName(o *GoClassName) {
+ // Runs typically in a different OS thread!
+ wrap.DeleteClassName(o.wcn)
+ o.wcn = nil
+}
+
+func (o *GoClassName) Close() {
+ // If the C++ object has a Close method.
+ o.wcn.Close()
+
+ // If the GoClassName object is no longer in an usable state.
+ runtime.SetFinalizer(o, nil) // Remove finalizer.
+ deleteGoClassName() // Free the C++ object.
}