Extending in VENOM means a 2-way binding - an object is defined in C++, extended in Python, and then used in C++ again.
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?
C++ to Python binding
A (virtual) base class ClassX is defined in C++,
A Python binding ClassX is created for it,
(in Python module ABC) Python class PyClassX that inherits from ClassX is created. Here it can implement / override the methods of ClassX in Python.
Python to C++ binding:
PyClassX derived form ClassX is created in C++. It includes a pointer to an embedded Python interpreter.
It imports the ABC module and creates an instance of PyClassX using the embedded interpreter.
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
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;
}