Library for automatically genereating ffi-bindings for c/c++ libraries

Rbind is developed to automatically generate ruby bindings for OpenCV but is not tight to this library. It allows to import already wrapped types from other gems/libraries using rbind to share the same types across multiple gems/libraries. For now rbind uses a copy of the OpenCV python hdr parser to parse c/c++ header files and looks for certain defines. This gem is still under heavy development and the API might change in the future.

Features:

  • inheritance
  • method overloading
  • default values
  • argument annotation
  • type sharing across libraries
  • gem support

TODO:

  • add documentation
  • add unit test especially for string normalization
  • move to clang
  • make parser more robust (empty lines will produce an error)
  • proper error if name of a argument is missing
  • add better support for type specializing on the ruby side
  • add support for yard
  • use directly type lib types as inter-media types (optional)
  • check that all functions have a return value or are constructors
  • add alias for the original c++ method names
  • make ruby/lib configurable
  • support for changing ownership (add special flag)

Defines which are picked up from the c++ header files

  • CV_EXPORTS_W CV_EXPORTS
  • CV_EXPORTS_W_SIMPLE CV_EXPORTS
  • CV_EXPORTS_AS(synonym) CV_EXPORTS
  • CV_EXPORTS_W_MAP CV_EXPORTS
  • CV_IN_OUT
  • CV_OUT
  • CV_PROP
  • CV_PROP_RW
  • CV_WRAP
  • CV_WRAP_AS(synonym)
  • CV_WRAP_DEFAULT(value)

Example1 (ropencv)

file rbind.rb

require 'rbind'
rbind = Rbind::Rbind.new("OpenCV")

# add dependency to opencv
rbind.pkg_config << "opencv"

#add opencv headers
rbind.includes =   ["opencv2/core/core_c.h", "opencv2/core/types_c.h",
                    "opencv2/core/core.hpp", "opencv2/flann/miniflann.hpp",
                    "opencv2/imgproc/imgproc_c.h", "opencv2/imgproc/types_c.h",
                    "opencv2/imgproc/imgproc.hpp", "opencv2/photo/photo_c.h",
                    "opencv2/photo/photo.hpp", "opencv2/video/video.hpp",
                    "opencv2/features2d/features2d.hpp", "opencv2/objdetect/objdetect.hpp",
                    "opencv2/calib3d/calib3d.hpp", "opencv2/ml/ml.hpp",
                    "opencv2/highgui/highgui_c.h", "opencv2/highgui/highgui.hpp",
                    "opencv2/contrib/contrib.hpp", "opencv2/nonfree/nonfree.hpp",
                    "opencv2/nonfree/features2d.hpp"]

# auto add vector and ptr types
rbind.on_type_not_found do |owner,type|
    if type =~ /Ptr_(.*)/
        t = rbind.parser.find_type(owner,$1)
        t2 = Rbind::RPtr.new(type,rbind,t).typedef("cv::Ptr<#{t.full_name} >")
        rbind.parser.add_type t2
    elsif type =~ /vector_(.*)/
        t = rbind.parser.find_type(owner,$1)
        t2 = Rbind::RVector.new(type,rbind,t).typedef("std::vector<#{t.full_name} >")
        rbind.parser.add_type t2
    end
end

# parse type definitions
rbind.parse File.join(File.dirname(__FILE__),"pre_opencv244.txt")
rbind.parse File.join(File.dirname(__FILE__),"opencv.txt")

# using namespace cv (cv::Mat can now be addressed as Mat)
rbind.use_namespace rbind.cv

# alias a type 
rbind.cv.types_alias["string"] = rbind.cv.String

# parse headers
rbind.parse_headers

# fix some errors because of missing defines in the c++ headers
rbind.cv.putText.parameter(0).add_flag(:IO)
rbind.cv.chamerMatching.parameter(0).add_flag(:IO)
rbind.cv.chamerMatching.parameter(1).add_flag(:IO)
rbind.cv.chamerMatching.parameter(2).add_flag(:IO)

# add some more vector types
rbind.parser.find_type(rbind,"vector_Point3f")
rbind.parser.find_type(rbind,"vector_Point3d")

# generate files
rbind.generator_ruby.file_prefix = "ropencv"
rbind.generate(File.join(File.dirname(__FILE__),"src"),File.join(File.dirname(__FILE__),"..","lib","ruby","ropencv"))

file opencv.txt

...
class cv.RNG
    uint64 state
cv.RNG.RNG
cv.RNG.RNG
    uint64 state
cv.RNG.uniform int
    int a
    int b
cv.RNG.uniform float 
    float a
    float b
cv.RNG.uniform double 
    double a
    double b
cv.RNG.fill void
    Mat mat /IO
    int distType
    Mat a
    Mat b
    bool saturateRange false
cv.RNG.gaussian double
    double sigma

file opencv_test.rb

require 'ropencv'
include OpenCV

scalar = cv::Scalar.new(1,0,0,0)
scalar[2] = 2
puts scalar[0]
puts scalar[1]
puts scalar[2]
puts scalar[3]

Example2

file rbind.rb

require 'rbind'
rbind = Rbind::Rbind.new("FrameHelper")

# add pkg config dependency
rbind.pkg_config << "base-types"

# add dependency to ruby bindings for opencv
rbind.gems << "ropencv"

# add headers which shall be parsed
rbind.includes = [File.absolute_path(File.join(File.dirname(__FILE__),"..","..","src","FrameHelperTypes.h")),
                  File.absolute_path(File.join(File.dirname(__FILE__),"..","..","src","FrameHelper.h"))]

# parse additional file specifying some types
rbind.parse File.join(File.dirname(__FILE__),"types.txt")

# parse types exported by the gem ropencv
rbind.parse_extern

# using namespace cv (cv::Mat can now be addressed as Mat)
rbind.use_namespace rbind.cv

# parse headers
rbind.parse_headers

# add additional target to the linker
rbind.libs << "frame_helper"

# generate all files
rbind.generate(File.join(File.dirname(__FILE__),"src"),File.join(File.dirname(__FILE__),"lib","ruby","frame_helper"))

file types.txt

struct base.samples.frame.Frame
base.samples.frame.Frame.Frame

file FrameHelper.h

class CV_EXPORTS_W FrameHelper
{
    ...
    public:
        CV_WRAP FrameHelper();
        CV_WRAP void convert(const base::samples::frame::Frame &src,CV_OUT base::samples::frame::Frame &dst,
                int offset_x = 0, int offset_y = 0, int algo = INTER_LINEAR, bool bundistort=false);
    ...
}