Constructors

In the Tutorial we touched upon how to wrap C++ constructors. Now let’s go into more depth.

C++ supports several types of constructors, including:

  • Default constructors

  • Copy constructors

  • Move constructors

  • Custom constructors

In addition, unlike a Ruby class, a C++ class may include many constructors.

Example

For example, consider this simplified version of OpenCV’s Mat class:

class Mat
{
public:
    Mat();
    Mat(int rows, int cols, int type);
    Mat(const std::vector<int>& sizes, int type);
    Mat(const Mat& m);
    Mat(Mat&& m);
}

The Rice binding for the above example is:

#include <rice/rice.hpp>
#include <rice/stl.hpp>

using namespace Rice;

extern "C"
void Init_Mat()
{
  Data_Type<Test> rb_cMat = define_class<Mat>("Mat")
    .define_constructor(Constructor<Mat>())
    .define_constructor(Constructor<Mat, int, int, int>())
    .define_constructor(Constructor<Mat, const std::vector<int>&, int>())
    .define_constructor(Constructor<const Mat&>())
    .define_constructor(Constructor<Mat&&>());
}

Notice that the Constructor template requires the full list of argument types needed by the constructor.

For more information on how Rice decides which constructor to call, please see the overloaded methods section.

Default Constructor

Most C++ classes include a default constructor that takes no arguments. These are mapped to Rice like this:

define_constructor(Constructor<Mat>())

This defines a constructor that takes no arguments. It can be invoked from Ruby using the following code:

Mat.new

Under the hood, the define_constructor call creates a new initialize method on the Ruby Mat class. The initialize method is responsible for creating a new C++ Mat instance and associating it with the wrapper Ruby object. Thus if you override the initialize method you MUST call super:

class Mat
  def initialize
    super  # <--- You MUST call super first
    ...your code..
  end
end

Custom Constructors

The Mat class defines two custom constructors:

define_constructor(Constructor<Mat, int, int, int>())
define_constructor(Constructor<Mat, const std::vector<int>&, int>())

These are invoked from Ruby like this:

Mat.new(1, 2, 3)

vec = Std::Vector<int>.new
Mat.new(vec, 4)

Similarly to default constructors, calling define_constructor will creates a new initialize method on the corresponding Ruby class.

Copy Constructors

Most C++ classes include a copy constructor that takes one argument. These are mapped to Rice like this:

define_constructor(Constructor<const Mat&>())

Rice maps copy constructors to Ruby’s clone and dup methods:

mat1 = Mat.new(1, 2, 3)
mat2 = mat1.dup
mat3 = mat1.clone

Under the hood, the define_constructor call creates a new initialize_copy method on the Ruby Mat class. The initialize_copy method is responsible for calling the C++ copy constructor and assigning the new C++ instance to the wrapper Ruby object. Thus if you override the initialize_copy method you MUST call super:

class Mat
  def initialize_copy
    super  # <--- You MUST call super first
    ...your code..
  end
end

Move Constructors

Rice does not currently support move constructors. Of course, you can roll your own support as needed.