Search code examples
pythonc++rubyembedding

Calling C++ class functions from Ruby/Python


In my specific circumstance, I have a complex class (a class of classes of classes) that I want to expose to a scripting language (aka Ruby). Rather that directly pass that complex class, someone gave me the idea of just opening up a few functions to a scripting language like Ruby, which seemed simpler. I've seen Rice but the only examples I've seen use simple functions that just multiply something, rather than interfacing with a class.

For simplicity, I have a simple class with functions I want to expose:

class Foo
{
    private:
        //Integer Vector:
        std::vector<int> fooVector;

    public:
        //Functions to expose to Ruby:
        void pushBack(const int& newInt) {fooVector.push_back(newInt);}
        int& getInt(const int& element) {return fooVector.at(element);}
};

ALSO:

I'd prefer not to just have a link to the download page of SWIG, or an article explaining how to do this with rice that was written in 2010, I would like a guide that will likely work (I haven't had much luck just yet)

ASWELL:

I'm using Linux (Ubuntu) but this is a cross-compatible program, so I must be able to compile on Windows and OS X

EDIT:

I do understand that shared libraries exist (dll and so files), but I don't know if I can have a library that depends on a .hpp file containing the class(es).


Solution

  • You can use cython or Boost.Python to call native code from python. Since you are using c++, i'd recommend looking into Boost.Python which offers a very natural way of wrapping c++ classes for python.

    As an example (close to what you provided), consider the following class definitions

    class Bar
    {
    private:
        int value;
    
    public:
        Bar() : value(42){ }
    
        //Functions to expose to Python:
        int getValue() const { return value; }
        void setValue(int newValue) { value = newValue; }
    };
    
    class Foo
    {
    private:
        //Integer Vector:
        std::vector<int> fooVector;
        Bar bar;
    
    public:
        //Functions to expose to Python:
        void pushBack(const int& newInt) { fooVector.push_back(newInt); }
        int getInt(const int& element) { return fooVector.at(element); }
        Bar& getBar() { return bar; }
    };
    
    double compute() { return 18.3; }
    

    This can be wrapped to python using Boost.Python

    #include <boost/python.hpp>
    BOOST_PYTHON_MODULE(MyLibrary) {
        using namespace boost::python;
    
        class_<Foo>("Foo", init<>())
            .def("pushBack", &Foo::pushBack, (arg("newInt")))
            .def("getInt", &Foo::getInt, (arg("element")))
            .def("getBar", &Foo::getBar, return_value_policy<reference_existing_object>())
        ;
    
        class_<Bar>("Bar", init<>())
            .def("getValue", &Bar::getValue)
            .def("setValue", &Bar::setValue, (arg("newValue")))
        ;
    
        def("compute", compute);
    }
    

    This code can be compiled to a static library MyLibrary.pyd and used like this

    import MyLibrary
    
    foo = MyLibrary.Foo()
    foo.pushBack(10);
    foo.pushBack(20);
    foo.pushBack(30);
    print(foo.getInt(0)) # 10
    print(foo.getInt(1)) # 20
    print(foo.getInt(2)) # 30
    
    bar = foo.getBar()
    print(bar.getValue()) # 42
    bar.setValue(17)
    print(foo.getBar().getValue()) #17
    
    print(MyLibrary.compute()) # 18.3