通过Boost.Python嵌入,Python 3无法将C ++类识别为模块

时间:2014-11-06 19:19:32

标签: python c++ boost-python msvc12

Boost.Python v1.56中的以下示例显示了如何将Python 3.4.2解释器嵌入到您自己的应用程序中。不幸的是,在Windows 8.1下使用MSVC2013进行配置时,该示例无法正常使用。我还没有找到一个关于嵌入的完整示例,至少没有一个比10年左右更年轻。

运行它时收到以下错误: ImportError:' embedded_hello'不是内置模块

代码在这里:http://pastebin.com/shTtdxT8

任何暗示我可以做些什么来让它运行?一般来说如何在Python中公开c ++类,反之亦然?

1 个答案:

答案 0 :(得分:6)

代码正在使用Python 2标头配置进行编译。使用Python 3标头配置进行编译时,boost/python/module_init.hpp会将embedded_hello模块的初始化函数声明为PyInit_embedded_hello而不是initembedded_hello。我强烈建议验证正确的头配置,并执行Boost.Python的干净构建,因为Boost.Python和使用库构建的模块需要使用相同的头配置。

此外,在将模块添加到内置表时,PyImport_AppendInittab()调用需要在Py_Initialize()之前进行。 PyImport_AppendInittab()文档明确指出:

  

将单个模块添加到现有内置模块表中。 ......这应该在Py_Initialize()之前调用。

Boost.Python使用BOOST_PYTHON_MODULE宏来定义Python模块。在模块的主体内,当前scope是模块本身。因此,当C ++类型通过类型包装器公开时,例如当C ++类通过boost::python::class_向Python公开时,生成的Python类将位于BOOST_PYTHON_MODULE定义的模块中。

另一方面,在Python中声明的用户定义类型是第一类对象。从C ++的角度来看,它们可以被视为工厂函数。因此,要在C ++中使用Python定义的类,需要获取类对象的句柄,然后通过调用类对象来实例化类的实例。


这是一个完整的最小示例,演示如何嵌入Python 3解释器:

  • 导入直接构建到二进制文件中的模块(example),并将基本C ++类(spam_wrap)公开给具有虚函数/调度的Python(example.Spam)默认。
  • 演示使用公开的Python类(example.Spam)。
  • 从Python(example.Spam)中公开的Python类(example.PySpam)派生并使用生成的类。
#include <iostream>
#include <boost/python.hpp>

/// @brief Mockup Spam model.
struct spam
  : boost::noncopyable
{
  virtual ~spam() {};
  virtual std::string hello() { return "Hello from C++"; }
};

//@ brief Mockup Spam wrapper.
struct spam_wrap
  : spam,
    boost::python::wrapper<spam>
{
  virtual std::string hello()
  {
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
    return boost::python::call<std::string>(
      this->get_override("hello").ptr());
#else
    return this->get_override("hello")();
#endif
  }

  std::string default_hello() { return this->spam::hello(); }
};

/// @brief Python example module.
BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  // Expose C++ spam_wrap as Python Spam class.
  python::class_<spam_wrap, boost::noncopyable>("Spam")
    .def("hello", &spam::hello, &spam_wrap::default_hello)
    ;
}   

int main()
{
  // Add example to built-in.
  PyImport_AppendInittab("example", &PyInit_example);

  // Start the interpreter.
  Py_Initialize();

  namespace python = boost::python;
  try
  {
    python::object main = python::import("__main__");
    python::object global = main.attr("__dict__");

    // Execute Python code, using the example module.
    exec(
      "from example import Spam          \n"
      "spam = Spam()                     \n"
      "                                  \n"
      "class PySpam(Spam):               \n"
      "    def hello(self):              \n"
      "        return 'Hello from Python'\n",     
      global, global);

    /// Check the instance of the Python object using the C++ class.
    // >>> spam_object = spam
    python::object spam_object = global["spam"];
    assert(python::extract<spam>(spam_object).check());
    // >>> result = spam_object.hello()
    python::object result = spam_object.attr("hello")();
    // >>> print(result)
    std::cout << python::extract<std::string>(result)() << std::endl;
    // >>> assert("Hello from C++" == result)
    assert("Hello from C++" == python::extract<std::string>(result)());

    /// Create an instance using PySpam class.  It too is a Python object.
    // >>> py_spam_type = PySpam
    python::object py_spam_type = global["PySpam"];
    // >>> py_spam_object = py_spam_type()
    python::object py_spam_object = py_spam_type();
    // >>> result = py_spam_object()
    result = py_spam_object.attr("hello")();
    // >>> print(result)
    std::cout << python::extract<std::string>(result)() << std::endl;
    // >>> assert("Hello from Python" == result)
    assert("Hello from Python" == python::extract<std::string>(result)());
  }
  catch (const python::error_already_set&)
  {
    PyErr_Print();
  }
}

程序运行完毕且没有错误,导致以下输出:

Hello from C++
Hello from Python