编程语言
949
你将学到什么
- 如何在Python中调用C++代码
- 如何在C++中调用Python代码
在Python中调用C++代码
首先定义一个动物类(include/animal.h)
#pragma once #include <string> class Animal { public: Animal(std::string name); virtual ~Animal(); virtual void call(); virtual void move(); void eat(std::string food); protected: std::string name_; };
其实现代码如下(src/animal.cpp)
#include <iostream> #include "animal.h" Animal::Animal(std::string name):name_(name) { } Animal::~Animal() { } void Animal::call() { std::cout << name_ << ": call" << std::endl; } void Animal::move() { std::cout << name_ << ": moving" << std::endl; } void Animal::eat(std::string food) { std::cout << name_ << ": eat " << food << std::endl; }
接着编写其导出代码(include/boost_wrapper.h)
#pragma once #include <boost/python/wrapper.hpp> #include "animal.h" class AnimalWrap: public Animal, public boost::python::wrapper<Animal> { public: AnimalWrap(std::string name); virtual ~AnimalWrap(); void call(); void move(); };
其导出实现如下(src/boost_wrapper.cpp)
#include <boost/python/module.hpp> #include <boost/python/class.hpp> #include "boost_wrapper.h" using namespace boost::python; using namespace boost::python::detail; AnimalWrap::AnimalWrap(std::string name):Animal(name) { } AnimalWrap::~AnimalWrap() { } void AnimalWrap::call() { if (override func = this->get_override("call")) func(); Animal::call(); } void AnimalWrap::move() { if (override func = this->get_override("move")) func(); Animal::move(); } BOOST_PYTHON_MODULE_INIT(boost) { class_<AnimalWrap, boost::noncopyable>("Animal", init<std::string>()) .def("call", &Animal::call) .def("move", &Animal::move) .def("eat", &Animal::eat); }
最后编写CMakeLists.txt
cmake_minimum_required(VERSION 2.8) project(boost) set(CMAKE_CXX_FLAGS "-Wall -g") ### 此处的动态库名必须和BOOST_PYTHON_MODULE()中定义的保持一致,即最后生成的库必须名为boost.so file(GLOB SRC "src/*.cpp") add_library(boost SHARED ${SRC}) set_target_properties(boost PROPERTIES PREFIX "") #dependencies INCLUDE(FindPkgConfig) pkg_check_modules(PYTHON REQUIRED python) include_directories(include /usr/local/include ${PYTHON_INCLUDE_DIRS}) link_directories(/usr/local/lib ${PYTHON_LIBRARY_DIRS}) target_link_libraries(boost boost_python)
项目最终目录结构
# tree . . ├── build ├── CMakeLists.txt ├── include │ ├── animal.h │ └── boost_wrapper.h └── src ├── animal.cpp └── boost_wrapper.cpp 3 directories, 5 files
编译
# cd build # cmake .. # make
编写测试文件(build/zoo.py)
import boost def show(): wolf = boost.Animal("wolf") wolf.eat("beef") goat = boost.Animal("gota") goat.eat("grass") if __name__ == '__main__': show()
执行测试
# cd build # python zoo.py wolf: eat beef gota: eat grass
在C++中调用Python代码
在上个项目的根目录添加源文件(main.cpp)
#include <iostream> #include <boost/python.hpp> using namespace boost::python; int main() { Py_Initialize(); if (!Py_IsInitialized()) { std::cout << "Initialize failed" << std::endl; return -1; } try { object sys_module = import("sys"); str module_directory("."); sys_module.attr("path").attr("insert")(1, module_directory); object module = import("zoo"); module.attr("show")(); } catch (const error_already_set&) { PyErr_Print(); } Py_Finalize(); return 0; }
修改CMakeLists.txt
cmake_minimum_required(VERSION 2.8) project(boost) set(CMAKE_CXX_FLAGS "-Wall -g") ### 此处的动态库名必须和BOOST_PYTHON_MODULE()中定义的保持一致,即最后生成的库必须名为boost.so file(GLOB SRC "src/*.cpp") add_library(boost SHARED ${SRC}) set_target_properties(boost PROPERTIES PREFIX "") add_executable(core main.cpp) #dependencies INCLUDE(FindPkgConfig) pkg_check_modules(PYTHON REQUIRED python) include_directories(include /usr/local/include ${PYTHON_INCLUDE_DIRS}) link_directories(/usr/local/lib ${PYTHON_LIBRARY_DIRS}) target_link_libraries(boost boost_python) target_link_libraries(core boost ${PYTHON_LIBRARIES})
编译并执行测试
# cd build # cmake .. # make # ./core wolf: eat beef gota: eat grass
总结
考虑这样一个需求,我们要展示一个动物园中的动物,但是动物的种类和个数都不固定,这就导致我们动物园的show方法需要经常变动,有什么办法可以避免程序的反复编译呢?一种方式就是使用配置文件,将需要展示的动物写入配置文件,然后动物园的show方法通过解析配置文件来生成需要展示的内容;另一种方式就是通过Python脚本来实现,因为Python脚本不需要编译,相比于配置文件的方式,Python脚本的方式不需要设计配置文件格式,也不需要实现复杂的解析逻辑,使用起来更加灵活。
在上面的例子中,我们使用Python脚本实现了原本应该在main.cpp中实现的show方法,然后在main.cpp中调用它,后面如果有改动我们直接修改Python脚本即可,无需重编程序。