├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── README.md ├── classes ├── CMakeLists.txt ├── include │ └── classes │ │ ├── cat.h │ │ └── dog.h ├── package.xml ├── scripts │ └── pets.py └── src │ ├── cat.cpp │ └── dog.cpp ├── cpp_object_attr ├── CMakeLists.txt ├── package.xml ├── setup.py └── src │ ├── cpp_object.cpp │ ├── cpp_object_attr │ ├── __init__.py │ └── type_printer.py │ ├── example.cpp │ └── main.cpp ├── function_call ├── CMakeLists.txt ├── package.xml ├── scripts │ └── hello_world.py └── src │ └── logger.cpp ├── inheritance ├── CMakeLists.txt ├── include │ └── inheritance │ │ └── pet.h ├── package.xml ├── scripts │ └── dog.py └── src │ └── pet.cpp └── plugin ├── CMakeLists.txt ├── include └── plugin │ ├── base_plugin.h │ ├── plugin_runner.h │ └── plugin_wrapper.h ├── package.xml ├── setup.py └── src ├── base_plugin.cpp ├── plugin ├── __init__.py └── custom_plugin.py ├── plugin_runner.cpp ├── plugin_wrapper.cpp └── py_base_plugin.cpp /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | industrial_ci: 7 | strategy: 8 | matrix: 9 | env: 10 | - ROS_DISTRO: melodic 11 | - ROS_DISTRO: noetic 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: 'ros-industrial/industrial_ci@master' 16 | env: ${{matrix.env}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pybind11_examples 2 | [![CI](https://github.com/arturmiller/pybind11_examples/actions/workflows/ci.yaml/badge.svg)](https://github.com/arturmiller/pybind11_examples/actions/workflows/ci.yaml) 3 | 4 | This repository shows minimal pybind11 using ROS. This project depends on [pybind11_catkin](https://github.com/ipab-slmc/pybind11_catkin). It is a [pybind11](https://github.com/pybind/pybind11) wrapper for ROS packages. Despite its name pybind11_catkin can be used in combination with catkin as well as with colcon. 5 | 6 | ## Installation 7 | ### Install prerequisites 8 | This project depends on [pybind11_catkin](https://github.com/ipab-slmc/pybind11_catkin). You can install it with ```sudo apt-get install ros--pybind11-catkin```. Depending on the ROS distribution `````` is ```kinetic```, ```melodic``` or ```noetic```. 9 | 10 | ### Build with catkin tools 11 | 1. [Create a catkin workspace](https://catkin-tools.readthedocs.io/en/latest/quick_start.html#initializing-a-new-workspace) or use an existing workspace. 12 | 2. Clone this repository into the ```src/``` subdirectory of the workspace: ``git clone https://github.com/arturmiller/pybind11_examples.git``. 13 | 3. Compile the code ```catkin build```. 14 | 4. Source the config file: ```source path_to_workspace/devel/setup.bash```. 15 | 16 | ### Build with colcon 17 | 1. [Create a colcon workspace](https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/#create-a-workspace) or use an existing workspace. 18 | 2. Clone this repository into the ```src/``` subdirectory of the workspace: ``git clone https://github.com/arturmiller/pybind11_examples.git``. 19 | 3. Compile the code ```colcon build```. 20 | 4. Source the config file: ```source path_to_workspace/install/setup.bash```. 21 | 22 | ## Examples 23 | 24 | ### [Calling a C++ function](https://github.com/arturmiller/pybind11_examples/tree/master/function_call) 25 | The first example is calling the C++ print function from Python. 26 | You run this example with ```rosrun function_call hello_world.py```. As output you should see ```hello world!```. 27 | 28 | ### [Basic classes usage](https://github.com/arturmiller/pybind11_examples/tree/master/classes) 29 | The second example shows how to instantiate and use C++ classes in Python. 30 | 31 | Run this example with ```rosrun classes pets.py```. This should be your output: 32 | ``` 33 | 34 | Bello 35 | woof woof! 36 | woof woof! 37 | meow! 38 | ``` 39 | 40 | ### [Inheritance](https://github.com/arturmiller/pybind11_examples/tree/master/inheritance) 41 | This example shows a Python class inheriting from a C++ class. A virtual C++ function is overriden in Python and called from the C++ base class. 42 | 43 | Run it with ```rosrun inheritance dog.py```. You should see something similar to this: 44 | ``` 45 | <__main__.Dog object at 0x7f90143954d0> 46 | Fluffy dog Molly! 47 | Fluffy dog Charly! 48 | ``` 49 | 50 | ### [Plugin](https://github.com/arturmiller/pybind11_examples/tree/master/plugin) 51 | The last two examples show, how to call C++ code in Python. This is can be very handy if you want to write a Python plugin system for a C++ library, as is the case for e.g. [Tensorflow](https://www.tensorflow.org/). There are multiple advantages of this approach. For example you can rerun the plugin without recompiling and running a debugger is much simpler Python. 52 | The following classes are part of the plugin system: 53 | - [PluginRunner](https://github.com/arturmiller/pybind11_examples/blob/master/plugin/src/plugin_runner.cpp): The plugins are stored and called in this module. 54 | - [BasePlugin](https://github.com/arturmiller/pybind11_examples/blob/master/plugin/src/base_plugin.cpp): All plugins inherit from BasePlugin. 55 | - [CustomPlugin](https://github.com/arturmiller/pybind11_examples/blob/master/plugin/src/plugin/custom_plugin.py): A custom plugin implemented in Python. 56 | - [PluginWrapper](https://github.com/arturmiller/pybind11_examples/blob/master/plugin/src/plugin_wrapper.cpp): The PluginWrapper calls the Python Plugin. 57 | - [PyBasePlugin](https://github.com/arturmiller/pybind11_examples/blob/master/plugin/src/py_base_plugin.cpp): This module makes the base plugin accessible in Python. 58 | 59 | Run this example with ```rosrun plugin plugin_runner```. The Python plugin is called, with the resulting output ```hello world!```. Now you can change the string of the print statement and rerun it without recompiling the source code. 60 | -------------------------------------------------------------------------------- /classes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(classes) 3 | 4 | find_package(catkin REQUIRED COMPONENTS 5 | pybind11_catkin) 6 | 7 | catkin_package( 8 | LIBRARIES 9 | CATKIN_DEPENDS pybind11_catkin 10 | ) 11 | 12 | include_directories(${PROJECT_SOURCE_DIR}/include 13 | ${catkin_INCLUDE_DIRS}) 14 | 15 | 16 | pybind_add_module(dog MODULE src/dog.cpp src/cat.cpp) 17 | pybind_add_module(cat SHARED src/cat.cpp) 18 | 19 | catkin_install_python(PROGRAMS scripts/pets.py 20 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 21 | 22 | install(TARGETS dog 23 | cat 24 | LIBRARY DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 25 | ) 26 | -------------------------------------------------------------------------------- /classes/include/classes/cat.h: -------------------------------------------------------------------------------- 1 | #ifndef __CAT_H__ 2 | #define __CAT_H__ 3 | 4 | class Cat { 5 | public: 6 | void meow() const; 7 | }; 8 | 9 | #endif // __CAT_H__ -------------------------------------------------------------------------------- /classes/include/classes/dog.h: -------------------------------------------------------------------------------- 1 | #ifndef __DOG_H__ 2 | #define __DOG_H__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | 9 | class Dog { 10 | public: 11 | Dog(const std::string &name); 12 | 13 | void bark() const; 14 | std::string getName() const; 15 | void chase(const Cat &cat); 16 | 17 | private: 18 | std::string name; 19 | }; 20 | 21 | #endif // __DOG_H__ -------------------------------------------------------------------------------- /classes/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | classes 4 | 0.0.1 5 | The classes package 6 | 7 | amiller 8 | 9 | MIT 10 | 11 | catkin 12 | 13 | pybind11_catkin 14 | 15 | pybind11_catkin 16 | 17 | 18 | -------------------------------------------------------------------------------- /classes/scripts/pets.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from cat import Cat 3 | from dog import Dog 4 | 5 | 6 | 7 | if __name__ == '__main__': 8 | dog = Dog('Bello') 9 | print(dog) 10 | # 11 | 12 | print(dog.name) 13 | # Bello 14 | 15 | dog.bark() 16 | # woof woof! 17 | 18 | cat = Cat() 19 | dog.chase(cat) 20 | # woof woof! 21 | # meow! 22 | -------------------------------------------------------------------------------- /classes/src/cat.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | 9 | namespace py = pybind11; 10 | 11 | 12 | void Cat::meow() const {std::cout << "meow!" << std::endl; } 13 | 14 | 15 | PYBIND11_MODULE(cat, m) { 16 | py::class_(m, "Cat") 17 | .def(py::init<>()); 18 | } 19 | -------------------------------------------------------------------------------- /classes/src/dog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | 8 | namespace py = pybind11; 9 | 10 | 11 | Dog::Dog(const std::string &name) : name(name) { } 12 | 13 | void Dog::bark() const { std::cout << "woof woof!" << std::endl; } 14 | 15 | std::string Dog::getName() const { return name; } 16 | 17 | void Dog::chase(const Cat &cat) 18 | { 19 | bark(); 20 | cat.meow(); 21 | } 22 | 23 | 24 | PYBIND11_MODULE(dog, m) { 25 | py::class_(m, "Dog") 26 | .def(py::init()) 27 | .def("bark", &Dog::bark, "Let the dog bark") 28 | .def("chase", &Dog::chase, "The dog chases a cat") 29 | .def_property_readonly("name", &Dog::getName, "The dog's name") 30 | .def("__repr__", [](const Dog &dog) { 31 | return ""; 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /cpp_object_attr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(cpp_object_attr) 3 | 4 | find_package(PythonLibs REQUIRED) 5 | 6 | find_package(catkin REQUIRED COMPONENTS 7 | pybind11_catkin) 8 | 9 | catkin_python_setup() 10 | 11 | catkin_package( 12 | LIBRARIES 13 | CATKIN_DEPENDS pybind11_catkin 14 | ) 15 | 16 | include_directories(${PYTHON_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS}) 17 | 18 | pybind_add_module(cpp_object MODULE src/cpp_object.cpp) 19 | pybind_add_module(example SHARED src/example.cpp ) 20 | add_executable(main src/main.cpp) 21 | target_link_libraries(main ${PYTHON_LIBRARIES} example) 22 | 23 | install(TARGETS main 24 | cpp_object 25 | LIBRARY DESTINATION ${PYTHON_INSTALL_DIR} 26 | RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 27 | ) 28 | -------------------------------------------------------------------------------- /cpp_object_attr/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | cpp_object_attr 4 | 0.0.1 5 | The plugin package 6 | 7 | amiller 8 | 9 | MIT 10 | 11 | catkin 12 | 13 | pybind11_catkin 14 | 15 | pybind11_catkin 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /cpp_object_attr/setup.py: -------------------------------------------------------------------------------- 1 | ## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD 2 | 3 | from distutils.core import setup 4 | from catkin_pkg.python_setup import generate_distutils_setup 5 | 6 | # fetch values from package.xml 7 | setup_args = generate_distutils_setup( 8 | packages=['cpp_object_attr'], 9 | package_dir={'': 'src'}) 10 | 11 | setup(**setup_args) -------------------------------------------------------------------------------- /cpp_object_attr/src/cpp_object.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace py = pybind11; 4 | 5 | 6 | class CppObject{ 7 | public: 8 | CppObject(){} 9 | }; 10 | 11 | 12 | PYBIND11_MODULE(cpp_object, m) { 13 | py::class_(m, "CppObject") 14 | .def(py::init<>()); 15 | } 16 | -------------------------------------------------------------------------------- /cpp_object_attr/src/cpp_object_attr/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cpp_object_attr/src/cpp_object_attr/type_printer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def print_type(cpp_object): 4 | print(type(cpp_object)) 5 | -------------------------------------------------------------------------------- /cpp_object_attr/src/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cpp_object.cpp" 4 | 5 | 6 | namespace py = pybind11; 7 | 8 | void example() 9 | { 10 | py::scoped_interpreter guard{}; 11 | 12 | py::module::import("cpp_object").attr("CppObject"); 13 | py::object print_type = py::module::import("cpp_object_attr.type_printer").attr("print_type"); 14 | CppObject cpp_object; 15 | print_type(cpp_object); 16 | } 17 | -------------------------------------------------------------------------------- /cpp_object_attr/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "example.cpp" 2 | 3 | int main(int argc, char **argv) { 4 | example(); 5 | return 0; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /function_call/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(function_call) 3 | 4 | find_package(catkin REQUIRED COMPONENTS 5 | pybind11_catkin) 6 | 7 | catkin_package( 8 | LIBRARIES 9 | CATKIN_DEPENDS pybind11_catkin 10 | ) 11 | 12 | include_directories(${catkin_INCLUDE_DIRS}) 13 | 14 | pybind_add_module(logger MODULE src/logger.cpp) 15 | 16 | catkin_install_python(PROGRAMS scripts/hello_world.py 17 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 18 | 19 | install(TARGETS logger 20 | LIBRARY DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 21 | ) 22 | -------------------------------------------------------------------------------- /function_call/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | function_call 4 | 0.0.1 5 | The function_call package 6 | 7 | amiller 8 | 9 | MIT 10 | 11 | catkin 12 | 13 | pybind11_catkin 14 | 15 | pybind11_catkin 16 | 17 | 18 | -------------------------------------------------------------------------------- /function_call/scripts/hello_world.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from logger import cpp_print 4 | 5 | 6 | if __name__ == '__main__': 7 | cpp_print("hello world!") # here the C++ function is called 8 | -------------------------------------------------------------------------------- /function_call/src/logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | 7 | void cpp_print(std::string value) 8 | { 9 | std::cout << value << std::endl; 10 | } 11 | 12 | PYBIND11_MODULE(logger, m) { 13 | m.doc() = "pybind11 example plugin"; // optional module docstring 14 | 15 | m.def("cpp_print", &cpp_print, "A print function implemented in c++"); 16 | } 17 | -------------------------------------------------------------------------------- /inheritance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(inheritance) 3 | 4 | find_package(catkin REQUIRED COMPONENTS 5 | pybind11_catkin) 6 | 7 | catkin_package( 8 | LIBRARIES 9 | CATKIN_DEPENDS pybind11_catkin 10 | ) 11 | 12 | include_directories(${PROJECT_SOURCE_DIR}/include 13 | ${catkin_INCLUDE_DIRS}) 14 | 15 | pybind_add_module(pet MODULE src/pet.cpp) 16 | 17 | catkin_install_python(PROGRAMS scripts/dog.py 18 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 19 | 20 | install(TARGETS pet 21 | LIBRARY DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 22 | ) 23 | -------------------------------------------------------------------------------- /inheritance/include/inheritance/pet.h: -------------------------------------------------------------------------------- 1 | #ifndef __PET_H__ 2 | #define __PET_H__ 3 | 4 | #include 5 | 6 | 7 | class Pet { 8 | public: 9 | Pet(const std::string &name); 10 | virtual std::string decorateName(const std::string name) = 0; 11 | void setName(const std::string &name); 12 | std::string getName(); 13 | 14 | private: 15 | std::string name; 16 | }; 17 | 18 | #endif // __PET_H__ -------------------------------------------------------------------------------- /inheritance/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | inheritance 4 | 0.0.1 5 | The hello world package 6 | 7 | amiller 8 | 9 | MIT 10 | 11 | catkin 12 | 13 | pybind11_catkin 14 | 15 | pybind11_catkin 16 | 17 | 18 | -------------------------------------------------------------------------------- /inheritance/scripts/dog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pet import Pet 4 | 5 | 6 | class Dog(Pet): 7 | 8 | def __init__(self, name): 9 | Pet.__init__(self, name) 10 | 11 | def decorateName(self, name): 12 | return 'Fluffy dog ' + name + '!' 13 | 14 | 15 | if __name__ == '__main__': 16 | pet = Dog('Molly') 17 | print(pet) 18 | print(pet.getName()) 19 | pet.setName('Charly') 20 | print(pet.getName()) 21 | -------------------------------------------------------------------------------- /inheritance/src/pet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | 6 | Pet::Pet(const std::string &name) : name(name) { } 7 | 8 | void Pet::setName(const std::string &name) { this->name = name; } 9 | 10 | std::string Pet::getName() { return decorateName(name); } 11 | 12 | 13 | namespace py = pybind11; 14 | 15 | class PyPet : public Pet { 16 | public: 17 | /* Inherit the constructors */ 18 | using Pet::Pet; 19 | 20 | /* Trampoline (need one for each virtual function) */ 21 | std::string decorateName(std::string name) override { 22 | PYBIND11_OVERLOAD_PURE( 23 | std::string, /* Return type */ 24 | Pet, /* Parent class */ 25 | decorateName, /* Name of function in C++ (must match Python name) */ 26 | name /* Argument(s) */ 27 | ); 28 | } 29 | }; 30 | 31 | PYBIND11_MODULE(pet, m) { 32 | py::class_(m, "Pet") 33 | .def(py::init()) 34 | .def("setName", &Pet::setName) 35 | .def("getName", &Pet::getName) 36 | .def("decorateName", &Pet::decorateName); 37 | } 38 | -------------------------------------------------------------------------------- /plugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(plugin) 3 | add_compile_options(-Wno-attributes -std=c++11) 4 | 5 | find_package(PythonLibs REQUIRED) 6 | 7 | find_package(catkin REQUIRED COMPONENTS 8 | pybind11_catkin) 9 | 10 | catkin_python_setup() 11 | 12 | catkin_package( 13 | LIBRARIES 14 | CATKIN_DEPENDS pybind11_catkin 15 | ) 16 | 17 | include_directories(${PROJECT_SOURCE_DIR}/include 18 | ${PYTHON_INCLUDE_DIRS} 19 | ${catkin_INCLUDE_DIRS}) 20 | 21 | add_library(base_plugin SHARED src/base_plugin.cpp) 22 | add_library(plugin_wrapper SHARED src/plugin_wrapper.cpp) 23 | target_link_libraries(plugin_wrapper ${PYTHON_LIBRARIES} base_plugin) 24 | 25 | pybind_add_module(py_base_plugin MODULE src/py_base_plugin.cpp) 26 | 27 | add_executable(plugin_runner src/plugin_runner.cpp) 28 | target_link_libraries(plugin_runner ${PYTHON_LIBRARIES} base_plugin plugin_wrapper) 29 | 30 | install(TARGETS plugin_runner 31 | base_plugin 32 | plugin_wrapper 33 | LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 34 | RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 35 | ) 36 | 37 | install(TARGETS py_base_plugin 38 | LIBRARY DESTINATION ${PYTHON_INSTALL_DIR} 39 | ) 40 | -------------------------------------------------------------------------------- /plugin/include/plugin/base_plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef __BASE_PLUGIN_H__ 2 | #define __BASE_PLUGIN_H__ 3 | 4 | 5 | class BasePlugin{ 6 | public: 7 | BasePlugin(); 8 | virtual void run() = 0; 9 | }; 10 | 11 | 12 | #endif // __BASE_PLUGIN_H__ -------------------------------------------------------------------------------- /plugin/include/plugin/plugin_runner.h: -------------------------------------------------------------------------------- 1 | #ifndef __PLUGIN_RUNNER_H__ 2 | #define __PLUGIN_RUNNER_H__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | 9 | class PluginRunner{ 10 | public: 11 | PluginRunner(); 12 | 13 | void add(BasePlugin* plugin); 14 | void run(); 15 | 16 | private: 17 | std::list plugins; 18 | }; 19 | 20 | 21 | #endif // __PLUGIN_RUNNER_H__ -------------------------------------------------------------------------------- /plugin/include/plugin/plugin_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef __PLUGIN_WRAPPER_H__ 2 | #define __PLUGIN_WRAPPER_H__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | 12 | namespace py = pybind11; 13 | 14 | class PluginWrapper : public BasePlugin{ 15 | public: 16 | PluginWrapper(std::string module_name_, std::string class_name_); 17 | 18 | void run(); 19 | 20 | private: 21 | py::scoped_interpreter *guard; 22 | std::string module_name; 23 | std::string class_name; 24 | }; 25 | 26 | 27 | #endif // __PLUGIN_WRAPPER_H__ -------------------------------------------------------------------------------- /plugin/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | plugin 4 | 0.0.1 5 | The plugin package 6 | 7 | amiller 8 | 9 | MIT 10 | 11 | catkin 12 | 13 | pybind11_catkin 14 | 15 | pybind11_catkin 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /plugin/setup.py: -------------------------------------------------------------------------------- 1 | ## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD 2 | 3 | from distutils.core import setup 4 | from catkin_pkg.python_setup import generate_distutils_setup 5 | 6 | # fetch values from package.xml 7 | setup_args = generate_distutils_setup( 8 | packages=['plugin'], 9 | package_dir={'': 'src'}) 10 | 11 | setup(**setup_args) -------------------------------------------------------------------------------- /plugin/src/base_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | BasePlugin::BasePlugin() { } 5 | -------------------------------------------------------------------------------- /plugin/src/plugin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugin/src/plugin/custom_plugin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from py_base_plugin import BasePlugin 4 | 5 | 6 | class CustomPlugin(BasePlugin): 7 | 8 | def __init__(self): 9 | BasePlugin.__init__(self) 10 | 11 | def run(self): 12 | print('hello world!') 13 | -------------------------------------------------------------------------------- /plugin/src/plugin_runner.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | 7 | PluginRunner::PluginRunner() { } 8 | 9 | void PluginRunner::add(BasePlugin* plugin) 10 | { 11 | plugins.push_back(plugin); 12 | } 13 | 14 | void PluginRunner::run() 15 | { 16 | std::list::iterator plugin = plugins.begin(); 17 | while(plugin != plugins.end()) 18 | { 19 | (*plugin)->run(); 20 | plugin++; 21 | } 22 | } 23 | 24 | 25 | int main(int argc, char **argv) { 26 | PluginRunner plugin_runner; 27 | plugin_runner.add(new PluginWrapper("plugin.custom_plugin", "CustomPlugin")); 28 | plugin_runner.run(); 29 | 30 | return 0; 31 | } -------------------------------------------------------------------------------- /plugin/src/plugin_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | PluginWrapper::PluginWrapper(std::string module_name_, std::string class_name_) 5 | : module_name(module_name_), class_name(class_name_) 6 | { 7 | guard = new py::scoped_interpreter(); // start the interpreter and keep it alive 8 | } 9 | 10 | void PluginWrapper::run(){ 11 | py::object Plugin = py::module::import(module_name.c_str()).attr(class_name.c_str()); 12 | py::object plugin = Plugin(); 13 | plugin.attr("run")(); 14 | } 15 | -------------------------------------------------------------------------------- /plugin/src/py_base_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | 6 | namespace py = pybind11; 7 | 8 | class PyBasePlugin : public BasePlugin { 9 | public: 10 | /* Inherit the constructors */ 11 | using BasePlugin::BasePlugin; 12 | 13 | /* Trampoline (need one for each virtual function) */ 14 | void run() override { 15 | PYBIND11_OVERLOAD_PURE( 16 | void, /* Return type */ 17 | BasePlugin, /* Parent class */ 18 | run, /* Name of function in C++ (must match Python name) */ 19 | ); 20 | } 21 | }; 22 | 23 | PYBIND11_MODULE(py_base_plugin, m) { 24 | py::class_(m, "BasePlugin") 25 | .def(py::init<>()) 26 | .def("run", &BasePlugin::run); 27 | } 28 | --------------------------------------------------------------------------------