如何通过引用 Python 和 boost::python 来传递非常量 std::vector<double>?
How to pass non-const std::vector<double> by reference to Python with boost::python?
我希望能够提供一个空的 std::vector<double>&
作为 Python 函数的参数来填充它。类似的东西:
Python 文件 Foo.py :
class Foo :
def bar(self, x) :
x.push_back(3.14)
foo = Foo()
C++ 代码:
Py_Initialize();
boost::python::object pyobj_main = boost::python::import("__main__");
boost::python::object glob = pyobj_main.attr("__dict__");
glob["std_vector_double"] = boost::python::class_< std::vector<double> >("std_vector_double").def(boost::python::vector_indexing_suite< std::vector<double> >());
boost::python::exec_file("Foo.py", glob, glob);
boost::python::object foo = glob["foo"];
std::vector<double> x;
foo.attr("bar")(x);
// Now x.size() == 1 and x[0] == 3.14
我知道这段代码不起作用,这只是我想做的。
最好的方法是什么?
我的第一个想法是将我的 x
封装为另一个 class VectorWrapper
中的指针,但它看起来像一个丑陋的坏主意...
默认情况下,Boost.Python 将创建一个副本,因为这是防止悬挂引用的最安全的做法。但是,可以通过使用 boost::python::ptr()
or boost::ref()
将对 C++ 对象的引用传递给 Python,同时保持 C++ 中的所有权。 C++ 代码应保证 C++ 对象的生命周期至少与 Python 对象一样长。
foo.attr("bar")(boost::ref(x));
foo.attr("bar")(boost::python::ptr(&x));
以上代码将构造一个 std_vector_double
Python 对象,该对象引用 x
。使用 ptr()
时,如果指针为 null,则生成的 Python 对象将为 None
.
这是一个完整的示例,它基于 demonstrates 使用 boost::ref()
传递对 Python 的引用的原始问题。
#include <cmath> // std::abs
#include <limits> // std::numeric_limits::epsilon
#include <vector>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
int main()
{
// Initialize Python.
setenv("PYTHONPATH", ".", 1);
Py_Initialize();
namespace python = boost::python;
try
{
// Create the __main__ module.
python::object main_module = python::import("__main__");
python::object main_namespace = main_module.attr("__dict__");
boost::python::class_<std::vector<double>>("std_vector_double")
.def(python::vector_indexing_suite<std::vector<double>>())
;
// Run the foo.py file within the main namespace.
python::exec_file("foo.py", main_namespace, main_namespace);
std::vector<double> x;
// Pass a reference (C++ maintains ownership) of 'x' to foo.bar().
main_namespace["foo"].attr("bar")(boost::ref(x));
// Verify 'x' was modified.
assert(x.size() == 1);
assert(std::abs(x[0] - 3.14) <= std::numeric_limits<double>::epsilon());
}
catch (const python::error_already_set&)
{
PyErr_Print();
return 1;
}
// Do not call Py_Finalize() with Boost.Python.
}
foo.py
的内容是:
class Foo:
def bar(self, x):
x.append(3.14)
foo = Foo()
我希望能够提供一个空的 std::vector<double>&
作为 Python 函数的参数来填充它。类似的东西:
Python 文件 Foo.py :
class Foo :
def bar(self, x) :
x.push_back(3.14)
foo = Foo()
C++ 代码:
Py_Initialize();
boost::python::object pyobj_main = boost::python::import("__main__");
boost::python::object glob = pyobj_main.attr("__dict__");
glob["std_vector_double"] = boost::python::class_< std::vector<double> >("std_vector_double").def(boost::python::vector_indexing_suite< std::vector<double> >());
boost::python::exec_file("Foo.py", glob, glob);
boost::python::object foo = glob["foo"];
std::vector<double> x;
foo.attr("bar")(x);
// Now x.size() == 1 and x[0] == 3.14
我知道这段代码不起作用,这只是我想做的。
最好的方法是什么?
我的第一个想法是将我的 x
封装为另一个 class VectorWrapper
中的指针,但它看起来像一个丑陋的坏主意...
默认情况下,Boost.Python 将创建一个副本,因为这是防止悬挂引用的最安全的做法。但是,可以通过使用 boost::python::ptr()
or boost::ref()
将对 C++ 对象的引用传递给 Python,同时保持 C++ 中的所有权。 C++ 代码应保证 C++ 对象的生命周期至少与 Python 对象一样长。
foo.attr("bar")(boost::ref(x));
foo.attr("bar")(boost::python::ptr(&x));
以上代码将构造一个 std_vector_double
Python 对象,该对象引用 x
。使用 ptr()
时,如果指针为 null,则生成的 Python 对象将为 None
.
这是一个完整的示例,它基于 demonstrates 使用 boost::ref()
传递对 Python 的引用的原始问题。
#include <cmath> // std::abs
#include <limits> // std::numeric_limits::epsilon
#include <vector>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
int main()
{
// Initialize Python.
setenv("PYTHONPATH", ".", 1);
Py_Initialize();
namespace python = boost::python;
try
{
// Create the __main__ module.
python::object main_module = python::import("__main__");
python::object main_namespace = main_module.attr("__dict__");
boost::python::class_<std::vector<double>>("std_vector_double")
.def(python::vector_indexing_suite<std::vector<double>>())
;
// Run the foo.py file within the main namespace.
python::exec_file("foo.py", main_namespace, main_namespace);
std::vector<double> x;
// Pass a reference (C++ maintains ownership) of 'x' to foo.bar().
main_namespace["foo"].attr("bar")(boost::ref(x));
// Verify 'x' was modified.
assert(x.size() == 1);
assert(std::abs(x[0] - 3.14) <= std::numeric_limits<double>::epsilon());
}
catch (const python::error_already_set&)
{
PyErr_Print();
return 1;
}
// Do not call Py_Finalize() with Boost.Python.
}
foo.py
的内容是:
class Foo:
def bar(self, x):
x.append(3.14)
foo = Foo()