boost :: python:将C++类暴露给嵌入在C++应用程序中的python脚本

Ste*_*mer 6 c++ python boost boost-python

我成功地能够加载python脚本文件并boost::python在C++应用程序中调用函数.

在boost python EmbeddingPython wiki中,有一个关于如何加载python模块的提示.

namespace bp = boost::python;

bp::object import(const std::string& module, const std::string& path, bp::object& globals)
{
    bp::dict locals;
    locals["module_name"] = module;
    locals["path"]        = path;

    bp::exec("import imp\n"
             "new_module = imp.load_module(module_name, open(path), path, ('py', 'U', imp.PY_SOURCE))\n",
             globals,
             locals);

    return locals["new_module"];
}
Run Code Online (Sandbox Code Playgroud)

我可以成功使用它来导入python模块(test.py)

int main()
{
    Py_Initialize();

    bp::object main    = bp::import("__main__");
    bp::object globals = main.attr("__dict__");
    bp::object module  = import("test", "test.py", globals);
    bp::object run     = module.attr("run");

    run();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

使用hello-world test.py脚本运行上面的代码可以正常工作:

test.py:

def run():
    print "hello world"
Run Code Online (Sandbox Code Playgroud)

输出:

hello world
Run Code Online (Sandbox Code Playgroud)

将C++类暴露给python:

但是,我现在想要将C++类公开给该脚本.

struct Foo
{
    void f() {}
};
Run Code Online (Sandbox Code Playgroud)

根据boost::python文档,我公开这个类如下:

BOOST_PYTHON_MODULE(FooModule)
{
    bp::class_<Foo>("Foo")
        .def("f", &Foo::f)
        ;
}
Run Code Online (Sandbox Code Playgroud)

根据上面链接的wiki中的说明,我可以导入我的FooModule,并将其存储在我的globals:

PyImport_AppendInittab("FooModule", &initFooModule); 

...

bp::object Foo = bp::import("FooModule");
globals["Foo"] = Foo;
Run Code Online (Sandbox Code Playgroud)

这个导入是导入我的test.py脚本之前完成的,这个globals对象是bp::exec导入我的脚本时传递给的对象(即:Foo 应该bp::exec在导入时暴露给我的脚本的全局字典中).

但是,出于某种原因,我的Foo模块可见test.py

题:

如何将我的Foo类暴露给test.py我正在加载的python脚本?


完整的工作示例:

test.py:

def run():
    foo = Foo()
    foo.f()
Run Code Online (Sandbox Code Playgroud)

main.cpp:

#include <iostream>
#include <boost/python.hpp>

namespace bp = boost::python;

bp::object import(const std::string& module, const std::string& path, bp::object& globals)
{
    bp::dict locals;
    locals["module_name"] = module;
    locals["path"]        = path;

    bp::exec("import imp\n"
             "new_module = imp.load_module(module_name, open(path), path, ('py', 'U', imp.PY_SOURCE))\n",
             globals,
             locals);
    return locals["new_module"];
}

struct Foo
{
    void f() {}
};

BOOST_PYTHON_MODULE(FooModule)
{
    bp::class_<Foo>("Foo")
        .def("f", &Foo::f)
        ;
}

int main()
try
{
    PyImport_AppendInittab("FooModule", &initFooModule);
    Py_Initialize();

    // get a handle to the globals dict    
    bp::object main = bp::import("__main__");
    bp::object globals = main.attr("__dict__");

    // import FooModule, and store it in the globals dict
    bp::object Foo = bp::import("FooModule");
    globals["Foo"] = Foo;

    // import the test script, passing the populated globals dict
    bp::object module = import("test", "test.py", globals);
    bp::object run = module.attr("run");

    // run the script
    run();

    return 0;
}
catch(const bp::error_already_set&)
{
    std::cerr << ">>> Error! Uncaught exception:\n";
    PyErr_Print();
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

Output:

>>> Error! Uncaught exception:
Traceback (most recent call last):
  File "test.py", line 2, in run
    foo = Foo()
NameError: global name 'Foo' is not defined
Run Code Online (Sandbox Code Playgroud)

use*_*ica 4

不要尝试从 C++ 端注入Python 脚本,只需从 C++ 端FooModule注册模块,然后从 Python 端注册模块:PyImport_AppendInittabimport

import FooModule

def run():
    foo = FooModule.Foo()
    foo.f()
Run Code Online (Sandbox Code Playgroud)