Docs 1.0 Help

Extending

Extending in VENOM means a 2-way binding - an object is defined in C++, extended in Python, and then used in C++ again.

implements
C++ class
Python binding
Python module
C++ wrapper class

Before you start

This chapter assumes you are familiar with the pybind11 library. If not, please read the official documentation. We'll be focusing mostly on classes so make sure you understand this section: Classes in pybind11

How extending works?

  1. C++ to Python binding

    1. A (virtual) base class ClassX is defined in C++,

    2. A Python binding ClassX is created for it,

    3. (in Python module ABC) Python class PyClassX that inherits from ClassX is created. Here it can implement / override the methods of ClassX in Python.

  2. Python to C++ binding:

    1. PyClassX derived form ClassX is created in C++. It includes a pointer to an embedded Python interpreter.

    2. It imports the ABC module and creates an instance of PyClassX using the embedded interpreter.

    3. It implements all the methods of ClassX by calling the corresponding methods of PyClassX from the Python module. It can also call the methods of PyClassX from the C++ code.

Example

This example uses pybind11 to handle the binding and embedding of the Python interpreter.

#ifndef MY_CLASS class MyClass { public: MyClass() {} virtual ~MyClass() {} int add(int a, int b) { return a + b; }; int sub(int a, int b) { return a - b; }; }; #endif // !MY_CLASS
#include "MyClass.h" #include <pybind11/pybind11.h> namespace py = pybind11; PYBIND11_MODULE(example, m) { py::class_<MyClass>(m, "MyClass") .def(py::init()) .def("add", &MyClass::add) .def("sub", &MyClass::sub); }
from example import MyClass class MyClassAltered(MyClass): def one(self): return 1 def add(self, a, b): return a + b + self.one() if __name__ == "__main__": cl = MyClassAltered() print(cl.add(1, 2)) print(cl.sub(1, 2))
#include "MyClass.h" #include <pybind11/embed.h> // everything needed for embedding namespace py = pybind11; class MyClassAltered : public MyClass { private: py::module_ altered_module; py::object altered_class_instance; public: MyClassAltered() { this->altered_module = py::module_::import("my_altered_class"); this->altered_class_instance = this->altered_module.attr("MyClassAltered")(); } int add(int a, int b) { return this->altered_class_instance.attr("add")(a, b).cast<int>(); }; };
#include "MyClassAltered.h" #include <iostream> #include <pybind11/embed.h> // everything needed for embedding namespace py = pybind11; int main() { py::scoped_interpreter guard{}; // start the interpreter and keep it alive auto my_altered = MyClassAltered(); std::cout << my_altered.add(1, 2) << std::endl; std::cout << my_altered.sub(1, 2) << std::endl; }
cmake_minimum_required(VERSION 3.5...3.26) project(example_binding) find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)` pybind11_add_module(example MyClass.cpp) target_compile_definitions(example PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO}) add_executable(example_binding main.cpp) target_link_libraries(example_binding PRIVATE pybind11::embed)
Last modified: 20 March 2024