C中嵌入Python解释器

讨论C/C++中嵌入Python计算器的方法。

环境

本文以VS2010、Python27为例进行说明。

在vs中创建了project之后,增加python的include和lib目录到对应位置。

参考资料

  • Python27安装后的doc:Python » Python 2.7.11 documentation » Extending and Embedding the Python Interpreter »
  • PyRun_SimpleFile() crashes on Windows but not on Unix; why?

第一个例子

python27_d.lib

创建一个C++控制台应用程序,只包含python头文件:

#include 
#include 

int main(int argc, char* argv[])
{
    printf("Hello, Python!\n");
    return 0;
}

链接出错:

>LINK : fatal error LNK1104: 无法打开文件“python27_d.lib”

此时,我们并没有在.cpp中或项目属性中提到python27_d.lib这个库,为什么会有这个提示呢?一种可能就是Python.h自动做了这个事情。为此,在头文件中以lib为关键字进行搜索,找到如下的内容:

/* For an MSVC DLL, we can nominate the .lib files used by extensions */
#ifdef MS_COREDLL
#   ifndef Py_BUILD_CORE /* not building the core - must be an ext */
#       if defined(_MSC_VER)
            /* So MSVC users need not specify the .lib file in
            their Makefile (other compilers are generally
            taken care of by distutils.) */
#           ifdef _DEBUG
#               pragma comment(lib,"python27_d.lib")
#           else
#               pragma comment(lib,"python27.lib")
#           endif /* _DEBUG */
#       endif /* _MSC_VER */
#   endif /* Py_BUILD_CORE */
#endif /* MS_COREDLL */

这就解释了为什么会自动去找python lib库。

接下来,就去Python安装目录下的libs中看看是否有所要求的lib文件。结果就是:只有python27.lib,而没有python27_d.lib文件。——如果将VS缺省的Debug改成Release配置,就无链接错误。

两种解决方法:

  • 拷贝一份python27.lib,重命名为python27_d.lib
  • 像网上有提到的去找一份python27_d.lib文件。

这里采用第一种方法。

事实上,还有一种方法,就是从源代码编译出一份。——这在后面会提到。

环境变量

代码增强:用doc的第一个例子继续验证。

#include 
#include 

int main(int argc, char* argv[])
{
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
                     "print 'Today is',ctime(time())\n");
    Py_Finalize();

    printf("Hello, Python!\n");
    return 0;
}

运行时出现错误:

ImportError: No module named site
请按任意键继续. . .

有两种解决办法:

  • 在代码最前面加上一句: Py_SetPythonHome(“C:/Python27”);
  • 设置环境变量:PYTHONHOME=”C:\Python27”。——设置环境变量之后,VS关闭重新打开才能生效。

这里采用第一种方法,即:

#include 
#include 

int main(int argc, char* argv[])
{
    Py_SetPythonHome("C:/Python27");
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
                     "print 'Today is',ctime(time())\n");
    Py_Finalize();

    printf("Hello, Python!\n");
    return 0;
}

可以运行成功。

const char* 和char*

在一开始的时候,main第二个参数用的是const char*,但编译出现错误。所以改成了上面看到的char*。

PyRun_SimpleFile();

exe已停止工作

继续按照python chm中的内容往下走,使用PyRun_SimpleFile()。代码如下:

#include 
#include 

int main(int argc, char* argv[])
{
    Py_SetPythonHome("C:/Python27");
    Py_Initialize();

    char* filename = "test.py";
    FILE* fp = fopen(filename, "r");

    PyRun_SimpleFile(fp, filename);
    Py_Finalize();

    fclose(fp);

    return 0;
}

这个时候,代码一运行就挂掉:

C中嵌入Python解释器_第1张图片

不管换成debug或release,不管是MD、MT、MTd等等,都不行。网上找到原因说是当前使用的VC环境和python安装的lib生成环境不一致。为此,就只能用当前的VS从Python源码编译一套lib出来。

编译python lib

用VS2010打开Python-2.7.7\PCbuild\pcbuild.sln,工程会自动升级。然后开始编译(为了省事,全部生成),最后会生成python27_d.lib,python27_d.dll等文件。——可能会有某些项目生成失败,但通常不会影响当前的python使用场景。

用这个生成的lib放到(/替换)(比如)C:/python27/libs/目录下,重新编译&链接上面的示例代码,就可以正常运行了。不过对于release版本,会使用python27.dll,而这个dll则会在机器的环境变量的目录中存在。如果不更改这些已在环境变量目录中的这些dll,即便把自己生成的dll放在exe目录下,仍然不会去调用它。

为此,我们转了一圈,还是回到PyRun_SimpleString+execfile这条更省事的路上来。

官方解释

参考:https://docs.python.org/2/faq/windows.html#how-can-i-embed-python-into-a-windows-application

拷贝如下:

There are two problems with Python’s C API which will become apparent if you use a compiler other than MSVC, the compiler used to build pythonNN.dll.

Problem 1: The so-called “Very High Level” functions that take FILE * arguments will not work in a multi-compiler environment because each compiler’s notion of a struct FILE will be different. From an implementation standpoint these are very _low_ level functions.

PyRun_SimpleString+execfile

因为lib和dll版本的问题,所以使用PyRun_SimpleString+execfile这种方法。代码如下:

#include 
#include 

int main(int argc, char* argv[])
{
    Py_SetPythonHome("C:/Python27");
    Py_Initialize();
    PyRun_SimpleString("execfile(\"test.py\")");
    Py_Finalize();

    return 0;
}

源码分析

源码下载位置:http://download.csdn.net/detail/u013344915/7478759

PYTHONHOME

pydoc部分

  • void Py_SetPythonHome(char *home)

Set the default “home” directory, that is, the location of the standard Python libraries. See PYTHONHOME for the meaning of the argument string.

The argument should point to a zero-terminated character string in static storage whose contents will not change for the duration of the program’s execution. No code in the Python interpreter will change the contents of this storage.

  • char* Py_GetPythonHome()

Return the default “home”, that is, the value set by a previous call to Py_SetPythonHome(), or the value of the PYTHONHOME environment variable if it is set.

  • PYTHONHOME

Change the location of the standard Python libraries. By default, the libraries are searched in prefix/lib/pythonversion and exec_prefix/lib/pythonversion, where prefix and exec_prefix are installation-dependent directories, both defaulting to /usr/local.

When PYTHONHOME is set to a single directory, its value replaces both prefix and exec_prefix. To specify different values for these, set PYTHONHOME to prefix:exec_prefix.

源码

对应的源码:

static char *default_home = NULL;

void
Py_SetPythonHome(char *home)
{
    default_home = home;
}

char *
Py_GetPythonHome(void)
{
    char *home = default_home;
    if (home == NULL && !Py_IgnoreEnvironmentFlag)
        home = Py_GETENV("PYTHONHOME");
    return home;
}

解读:

  • pydoc要求在Py_SetPythonHome()的时候,入参要求是static型。如果是栈临时变量 char home[] = “C:/Python27”; 这种,后续default_home指向的内容就不是预期的了。
  • 如果没有设置过default_home变量(即没有调用Py_SetPythonHome()),那么就会尝试从环境变量PYTHONHOME中取。
  • 如果没有调用API设置,也没有对应的环境变量,那么Py_GetPythonHome()就返回NULL了。
  • Python解释器在初始化的时候要用到这个home路径,如果设置不对,就无法按照预期执行python脚本。

你可能感兴趣的:(Python)