Skip to content

Memory management

CppBind uses either a raw pointer or a shared pointer for managing the underlying C++ object. This means a class can only be associated with only one of them. By default, a raw pointer is used. This behaviour can be changed by using a variable called shared_ref.

Shared pointers

If the type is marked with shared_ref: True, then the underlying C++ objects of this type are managed by shared pointers. Whenever an object of such type is returned, the reference counter is incremented, and the ownership is given to the target language. This means the reference counter is decremented whenever the wrapper object is deallocated. The instances can be returned by value or wrapped with std::shared_ptr. A reference or a pointer can be returned only if the type is inherited from std::enable_shared_from_this, otherwise, CppBind raises an error. There are no restrictions on function arguments having a type marked as shared_ref: True.

Let's see an example on how instances of type with shared_ref: True can be returned:

  const double PI = 3.14;

  /**
   * An example marked with shared_ref=True and directly inherited from std::enable_shared_from_this
   * Contains a method which returns a reference on this.
   * __API__
   * action: gen_class
   * shared_ref: True
   * package: inheritance
   */
  class Shape : public std::enable_shared_from_this<Shape> {
  public:
      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
      virtual double area() const = 0;

      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
      Shape& thisObject() {
          return *this;
      }

      virtual ~Shape() = default;
  };

  /**
   * An example marked with shared_ref=True and indirectly inherited from std::enable_shared_from_this.
   * Contains a method returning a pointer on this.
   * __API__
   * action: gen_class
   * shared_ref: True
   * package: inheritance
   */
  class Circle : public Shape {
  public:
      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
       static std::shared_ptr<Circle> create(double r) {
          return std::shared_ptr<Circle>(new Circle(r));
       }

      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
      virtual double area() const { return PI * _radius * _radius; }

      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
      Circle* incrementRadius(double value = 1.0) {
          _radius += value;
          return this;
      }


  private:
      Circle(double r) : _radius(r) {}
      Circle(const Circle&) = default;
      Circle& operator=(const Circle& other) = default;

      Circle(Circle&& other) = default;
      Circle& operator=(Circle&& other) = default;
      double _radius;
  };


  /**
   * An example without an API and indirectly inherited from std::enable_shared_from_this.
   * Used as a base type for another type having API and marked with shared_ref=True.
   */
  class Triangle : public Shape {
  public:
      Triangle(double side1, double side2, double side3)
        : _side1(side1), _side2(side2), _side3(side3) {}

      double area() const override{
          double s = (_side1 + _side2 + _side3) / 2;
          return sqrt(s * (s - _side1) * (s - _side2) * (s - _side3));
      }

  protected:
      double _side1;
      double _side2;
      double _side3;
  };

  /**
   * An example marked with shared_ref=True, indirectly inherited from std::enable_shared_from_this from a base having no API.
   * Contains a method returning reference on this.
   * __API__
   * action: gen_class
   * shared_ref: True
   * package: inheritance
   */
  class RegularTriangle : public Triangle {

  public:
      /**
       * __API__
       * action: gen_constructor
       * throws: no_throw
       */
      RegularTriangle(double side)
        : Triangle(side, side, side) {}

      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
      double area() const override {
          return (sqrt(3) / 4) * (_side1 * _side1);
      }

      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
      RegularTriangle& incrementSide(double value = 1.0) {
          _side1 = _side2 = _side3 = _side1 + value;
          return *this;
      };

      /**
       * __API__
       * action: gen_method
       * throws: no_throw
       */
      RegularTriangle multiplySide(double value = 1.0) {
          _side1 = _side2 = _side3 = _side1 * value;
          return *this;
      };

  };

In this example, we have different types marked with shared_ref: True, which are directly or indirectly inherited from std::enable_shared_from_this. These types have different methods returning this by value, reference, pointer or shared pointer.

And here's a small usage example:

val circle = Circle.create(2.0)
assert(circle.area() * 4 == circle.incrementRadius(2.0).area())

val regularTriangle = RegularTriangle(10.0)
assert(regularTriangle.area() * 4 == regularTriangle.incrementSide(10.0).area())
assert(regularTriangle.area() * 4 == regularTriangle.multiplySide(2.0).area())
circle = Circle.create(2.0)
assert circle.area() * 4 == circle.increment_radius(2.0).area()

regularTriangle = RegularTriangle(10.0)
assert regularTriangle.area() * 4 == regularTriangle.increment_side(10.0).area()
assert regularTriangle.area() * 4 == regularTriangle.multiply_side(2.0).area()
let circle = Circle.create(r: 2.0)
assert(circle.area() * 4 == circle.incrementRadius(value: 2.0).area())

let regularTriangle = RegularTriangle(side: 10.0)
assert(regularTriangle.area() * 4 == regularTriangle.incrementSide(value: 10.0).area())
assert(regularTriangle.area() * 4 == regularTriangle.multiplySide(value: 2.0).area())

Raw pointers

When the type is marked with shared_ref: False the underlying C++ object is kept as a raw pointer. For such types, shared pointers are not supported, i.e., function arguments and return values cannot be shared pointers.

Note

Users must be careful when returning a reference or a pointer of a type marked with shared_ref: False. For these cases, the ownership is given to C++. To make sure the returned object is not deallocated while its wrapper object is alive, the return value policy must be properly specified. More on return value policies can be found here.


Last update: December 1, 2022