使用 boost-python 将 python 变量设置为 C++ 对象指针
Set a python variable to a C++ object pointer with boost-python
我想从 C++ 设置一个 Python 变量,以便 C++ 程序可以创建一个对象 Game* game = new Game();
,以便 Python 代码能够引用此实例(和调用函数等)。我怎样才能做到这一点?
我觉得我对 Python 或 Boost-Python 的工作方式有一些核心误解。
main_module.attr("game") = game
行在try catch语句中,错误(使用PyErr_Fetch)为"No to_python (by-value) converter found for C++ type: class Game".
例如
class_<Game>("Game")
.def("add", &Game::add)
;
object main_module = import("__main__");
Game* game = new Game();
main_module.attr("game") = game; //This does not work
来自Python:
import testmodule
testmodule.game.foo(7)
在处理语言绑定时,往往要在细节上迂腐。默认情况下,当 C++ 对象越过语言边界时,Boost.Python 将创建一个副本,因为这是防止悬挂引用的最安全的操作过程。如果不应制作副本,则需要明确 C++ 对象的所有权:
- 要将对 C++ 对象的引用传递给 Python,同时保持 C++ 中的所有权,请使用
boost::python::ptr()
or boost::ref()
。 C++ 代码应保证 C++ 对象的生命周期至少与 Python 对象一样长。使用 ptr()
时,如果指针为 null,则生成的 Python 对象将为 None
.
- 要将 C++ 对象的所有权转移到 Python,可以应用
manage_new_object
ResultConverterGenerator,允许将所有权转移到 Python。一旦 Python 对象的生命周期结束,C++ 代码不应尝试访问指针。
- 对于共享所有权,需要使用 HeldType 支持共享语义的智能指针公开 class,例如
boost::shared_ptr
.
创建 Python 对象后,需要将其插入到 Python 命名空间中才能普遍访问:
在模块定义中,使用 boost::python::scope
获取当前作用域的句柄。例如,以下将插入 x
到 example
模块中:
BOOST_PYTHON_MODULE(example)
{
boost::python::scope().attr("x") = ...; // example.x
}
要插入__main__
模块,可以导入__main__
。例如,以下将插入 x
到 __main__
模块中:
boost::python::import("__main__").attr("x") = ...;
这里有一个例子demonstrating如何从 C++ 直接构造 Python 对象,将 C++ 对象的所有权转移到 Python,并构造一个 Python 对象引用 C++ 对象:
#include <iostream>
#include <boost/python.hpp>
// Mockup model.
struct spam
{
spam(int id)
: id_(id)
{
std::cout << "spam(" << id_ << "): " << this << std::endl;
}
~spam()
{
std::cout << "~spam(" << id_ << "): " << this << std::endl;
}
// Explicitly disable copying.
spam(const spam&) = delete;
spam& operator=(const spam&) = delete;
int id_;
};
/// @brief Transfer ownership to a Python object. If the transfer fails,
/// then object will be destroyed and an exception is thrown.
template <typename T>
boost::python::object transfer_to_python(T* t)
{
// Transfer ownership to a smart pointer, allowing for proper cleanup
// incase Boost.Python throws.
std::unique_ptr<T> ptr(t);
// Use the manage_new_object generator to transfer ownership to Python.
namespace python = boost::python;
typename python::manage_new_object::apply<T*>::type converter;
// Transfer ownership to the Python handler and release ownership
// from C++.
python::handle<> handle(converter(*ptr));
ptr.release();
return python::object(handle);
}
namespace {
spam* global_spam;
} // namespace
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose spam.
auto py_spam_type = python::class_<spam, boost::noncopyable>(
"Spam", python::init<int>())
.def_readonly("id", &spam::id_)
;
// Directly create an instance of Python Spam and insert it into this
// module's namespace.
python::scope().attr("spam1") = py_spam_type(1);
// Construct of an instance of Python Spam from C++ spam, transfering
// ownership to Python. The Python Spam instance will be inserted into
// this module's namespace.
python::scope().attr("spam2") = transfer_to_python(new spam(2));
// Construct an instance of Python Spam from C++, but retain ownership of
// spam in C++. The Python Spam instance will be inserted into the
// __main__ scope.
global_spam = new spam(3);
python::import("__main__").attr("spam3") = python::ptr(global_spam);
}
交互使用:
>>> import example
spam(1): 0x1884d40
spam(2): 0x1831750
spam(3): 0x183bd00
>>> assert(1 == example.spam1.id)
>>> assert(2 == example.spam2.id)
>>> assert(3 == spam3.id)
~spam(1): 0x1884d40
~spam(2): 0x1831750
在示例用法中,请注意 Python 如何在退出时不销毁 spam(3)
,因为它未被授予基础对象的所有权。
我想从 C++ 设置一个 Python 变量,以便 C++ 程序可以创建一个对象 Game* game = new Game();
,以便 Python 代码能够引用此实例(和调用函数等)。我怎样才能做到这一点?
我觉得我对 Python 或 Boost-Python 的工作方式有一些核心误解。
main_module.attr("game") = game
行在try catch语句中,错误(使用PyErr_Fetch)为"No to_python (by-value) converter found for C++ type: class Game".
例如
class_<Game>("Game")
.def("add", &Game::add)
;
object main_module = import("__main__");
Game* game = new Game();
main_module.attr("game") = game; //This does not work
来自Python:
import testmodule
testmodule.game.foo(7)
在处理语言绑定时,往往要在细节上迂腐。默认情况下,当 C++ 对象越过语言边界时,Boost.Python 将创建一个副本,因为这是防止悬挂引用的最安全的操作过程。如果不应制作副本,则需要明确 C++ 对象的所有权:
- 要将对 C++ 对象的引用传递给 Python,同时保持 C++ 中的所有权,请使用
boost::python::ptr()
orboost::ref()
。 C++ 代码应保证 C++ 对象的生命周期至少与 Python 对象一样长。使用ptr()
时,如果指针为 null,则生成的 Python 对象将为None
. - 要将 C++ 对象的所有权转移到 Python,可以应用
manage_new_object
ResultConverterGenerator,允许将所有权转移到 Python。一旦 Python 对象的生命周期结束,C++ 代码不应尝试访问指针。 - 对于共享所有权,需要使用 HeldType 支持共享语义的智能指针公开 class,例如
boost::shared_ptr
.
创建 Python 对象后,需要将其插入到 Python 命名空间中才能普遍访问:
在模块定义中,使用
boost::python::scope
获取当前作用域的句柄。例如,以下将插入x
到example
模块中:BOOST_PYTHON_MODULE(example) { boost::python::scope().attr("x") = ...; // example.x }
要插入
__main__
模块,可以导入__main__
。例如,以下将插入x
到__main__
模块中:boost::python::import("__main__").attr("x") = ...;
这里有一个例子demonstrating如何从 C++ 直接构造 Python 对象,将 C++ 对象的所有权转移到 Python,并构造一个 Python 对象引用 C++ 对象:
#include <iostream>
#include <boost/python.hpp>
// Mockup model.
struct spam
{
spam(int id)
: id_(id)
{
std::cout << "spam(" << id_ << "): " << this << std::endl;
}
~spam()
{
std::cout << "~spam(" << id_ << "): " << this << std::endl;
}
// Explicitly disable copying.
spam(const spam&) = delete;
spam& operator=(const spam&) = delete;
int id_;
};
/// @brief Transfer ownership to a Python object. If the transfer fails,
/// then object will be destroyed and an exception is thrown.
template <typename T>
boost::python::object transfer_to_python(T* t)
{
// Transfer ownership to a smart pointer, allowing for proper cleanup
// incase Boost.Python throws.
std::unique_ptr<T> ptr(t);
// Use the manage_new_object generator to transfer ownership to Python.
namespace python = boost::python;
typename python::manage_new_object::apply<T*>::type converter;
// Transfer ownership to the Python handler and release ownership
// from C++.
python::handle<> handle(converter(*ptr));
ptr.release();
return python::object(handle);
}
namespace {
spam* global_spam;
} // namespace
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose spam.
auto py_spam_type = python::class_<spam, boost::noncopyable>(
"Spam", python::init<int>())
.def_readonly("id", &spam::id_)
;
// Directly create an instance of Python Spam and insert it into this
// module's namespace.
python::scope().attr("spam1") = py_spam_type(1);
// Construct of an instance of Python Spam from C++ spam, transfering
// ownership to Python. The Python Spam instance will be inserted into
// this module's namespace.
python::scope().attr("spam2") = transfer_to_python(new spam(2));
// Construct an instance of Python Spam from C++, but retain ownership of
// spam in C++. The Python Spam instance will be inserted into the
// __main__ scope.
global_spam = new spam(3);
python::import("__main__").attr("spam3") = python::ptr(global_spam);
}
交互使用:
>>> import example
spam(1): 0x1884d40
spam(2): 0x1831750
spam(3): 0x183bd00
>>> assert(1 == example.spam1.id)
>>> assert(2 == example.spam2.id)
>>> assert(3 == spam3.id)
~spam(1): 0x1884d40
~spam(2): 0x1831750
在示例用法中,请注意 Python 如何在退出时不销毁 spam(3)
,因为它未被授予基础对象的所有权。