Python的标准版本是用C语言写成的,提供C语言的API,所以可以很容易嵌入C/C++程序中。本文介绍嵌入操作如何进行,并如何采用cmake编译。

嵌入还是扩展

嵌入(Embedding)是指在C++程序中嵌入Python的解释器。如果你用过Sublime Tex编辑器,你很可能知道Sublime Text就嵌入了Python解释器,所以Sublime Text的插件都是用Python语言编写的。

扩展(Extending)是指用其他语言(C/C++)编写Python的模块,最后导入Python使用。

嵌入和扩展这两种操作从原理上并没有很大区别,一般都要用到Python的API,以及在Python和与其交互的语言之间转换数据类型。差别在于运行的时候以谁为主,嵌入的话程序的主体由宿主语言控制,扩展的话程序的主体由Python控制。

这里的Python指的是标准版的用C语言实现的Python,俗称CPython。Python语言还有其他版本,比如基于Java平台的Jython,或者基于.Net平台的IronPython

嵌入的例子

Python3的官方文档中有一个章节Embedding Python in Another Application,专门介绍如何把Python嵌入到其他程序中。 该章节中由一个例子:

#include <Python.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
                       "print('Today is', ctime(time()))\n");
    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    PyMem_RawFree(program);
    return 0;
}

上面的代码看起来有点复杂,在C++程序中初始化了Python解释器,然后让这个解释器执行一下Python脚本:

from time import time, ctime
print('Today is', ctime(time()))

程序的结果是在命令行上输出当前的时间,比如:Today is Mon Jul 30 14:14:14 2018

使用cmake编译

那如果编译上面的例子呢?如果直接调用gcc/g++命令或者手写makefile会比较麻烦,一个简化的办法是使用cmake。

cmake需要写一个CMakeLists.txt的指令文件。对于上面的例子,对应的CMakeLists.txt的内容如下:

cmake_minimum_required(VERSION 3.12)

project(mypy)

find_package(Python3 COMPONENTS Development)

include_directories(${Python3_INCLUDE_DIRS})

add_executable(mypy main.cc)

target_link_libraries(mypy PRIVATE ${Python3_LIBRARIES})

上面的cmake指令使用了FindPython3指令包。该指令包在cmake 3.12版本才支持,所以要通过cmake_minimum_required(VERSION 3.12)指定cmake的最小版本为3.12。

find_package(Python3 COMPONENTS Development)使用FindPython3指令包来查找系统中的Python,这条指令会生成一系列的变量,比如用来指示Python头文件位置的Python3_INCLUDE_DIRS,还有用来指示Python链接库的Python3_LIBRARIES

project(mypy)告诉cmake我们的项目名为mypy,最后生成的可执行文件就叫mypy。add_executable(mypy main.cc)指定main.cc为mypy的源代码。main.cc的内容就是上个章节的那个例子。include_directories(${Python3_INCLUDE_DIRS})告诉cmake在编译mypy时要添加Python的头文件目录;target_link_libraries(mypy PRIVATE ${Python3_LIBRARIES})告诉cmake在链接的时候添加Python的库。

一起就绪,执行以下命令来生成mypy:

cmake . # CMakeLists.txt必须在同一个目录
make # 在cmake成功之后,执行make

一起顺利的话就会生成mypy,然后执行mypy,就会获得当前日期:

> ./mypy
Today is Mon Jul 30 14:14:14 2018

大功告成!

其他参考

(本篇完)