Plug-ins permit to easily extend a software without modifying it. For the language, Python plug-ins avoid the per platform compilation needed with c++. Actually it does not need compilation.
We'll make a basic sample to call python implemented member function on a python built c++ object.
Prerequisite
CMake will be used to manage project build. I no more able to start a project without it :).
There is multiple way to use Python, for example the official Python C API, I'll use boost 1.42 python.
Files
There will be 3 different "programs" :
- test executable : it loads the python plug-in, extract and use the corresponding c++ object. It need two arguments : plug-in interface library directory path and python plug-in path
- plugin interface library : defines the plug-in interface so that it can be inherited in python and used in the c++
- python plugin
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
#
# Setup depencies
#
# For python you can't specify the version, so it'll take the default one (for me it's 2.7)
# set PYTHON_INCLUDE_DIRS and PYTHON_LIBRARIES
find_package( PythonLibs REQUIRED )
# set PYTHON_LIBRARIES
find_package( Boost 1.42.0 COMPONENTS python )
#
# boost/python.hpp need this to compile
#
include_directories ( ${PYTHON_INCLUDE_DIRS} )
#
#c++ plug-in interface
#
add_library( plugin SHARED "Plugin.hpp" "Plugin.cpp" )
# avoid the lib prefix on unix
set_target_properties( plugin PROPERTIES PREFIX "" )
target_link_libraries( plugin ${Boost_LIBRARIES} )
#
#test executable
#
add_executable( test "main.cpp" )
# need to link with boost python and python
target_link_libraries( test ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} plugin )
Plug-in interface Library
hpp
#include <boost/python.hpp>
namespace bpy = boost::python;
namespace plugin
{
// Need to inherits from bpy::wrapper
// to easily access python implementation
// (see python_virtual definition )
class Object : public bpy::wrapper<object>
{
public:
void exported(); // will be accessible from python ( see line 32 )
void non_exported(); // won't be accessible from python
void python_virtual(); // will call the python plugin implementation
};
}
cpp
#include "Plugin.hpp"
#include <iostream>
namespace plugin
{
void Object::exported()
{
std::cout << "exported called " << std::endl;
}
void Object::non_exported()
{
}
void Object::python_virtual()
{
this->get_override("python_virtual")(); // call the inheriting python object python_virtual member function
}
}
// Define the python module that will be used
// to encapsulate all exported symbols.
// It must be compiled in a library with this
// exact name ( plugin.so/dll )
BOOST_PYTHON_MODULE(plugin)
{
//All this will be accessible from python
boost::python::class_<plugin::Object, boost::noncopyable>("Object")
.def("exported", &plugin::Object::exported );
// You can export enums with
//boost::python::enum_<EnumType>("EnumTypePython")
// .value("EnumVal0Python", EnumVal0)
// .value("EnumVal1Python", EnumVal1)
//;
}
Test executable
#include <boost/python.hpp>
#include <string>
#include <iostream>
#include "Plugin.hpp"
int main(int argc, char * argv[] )
{
if( argc == 3 ) // need two parameters
{
std::string const python_interface_path = argv[1];
std::string const python_plugin_path = argv[2];
namespace plg = plugin;
try
{
Py_Initialize(); // Initialize the python system
//Construct a context for the plugin to run in
bpy::object main_module = bpy::import( "__main__" );
bpy::object main_namespace = main_module.attr( "__dict__" );
// With sys.path setup to be able to import plugin library
bpy::object sys_module = bpy::import( "sys" );
bpy::object path = sys_module.attr( "path" );
path.attr("append")( python_interface_path );
//We execute the plugin in the previously built context
bpy::exec_file(
python_plugin_path.c_str(),
main_namespace
);
//And extract the object variable
//It must be an instance of a plugin::Object child
plg::Object & object = bpy::extract<plg::Object &>( main_namespace["object"] );
//Call a "python virtual" function
object.python_virtual();
}
catch( bpy::error_already_set const & )
{
PyErr_Print(); // For any boost::python exception the python log will be printed
}
}
}
Python plugin-in
import plugin # import the plugin library
class PluginObject ( plugin.Object ): # inherit from plugin base class
def __init__(self):
plugin.Object.__init__(self)
self.exported()
#self.non_exported() # can't be called because it have not been exported
def python_virtual(self) :
print( "python virtual called" )
# this object variable will be extracted
# in the c++ text executable
object = PluginObject()
This is just a tiny part of what you can do. So, have fun coding your plug-ins ;)