我有一些定义了两个类A和B的C++代码。B在构造时需要一个A的实例。我已经使用Boost.Python包装了A,以便Python可以创建A的实例,以及其子类。我希望对B也做同样的操作。
class A {
public:
A(long n, long x, long y) : _n(n), _x(x), _y(y) {};
long get_n() { return _n; }
long get_x() { return _x; }
long get_y() { return _y; }
private:
long _n, _x, _y;
};
class B {
public:
B(A a) : _a(a) {};
doSomething() { ... };
private:
A _a;
};
在封装B时,我需要解决如何将A的实例传递给B的构造函数。我进行了一些探索,找到了这个“转换器”类的解决方案。
struct A_from_python_A {
static void * convertible(PyObject* obj_ptr) {
// assume it is, for now...
return obj_ptr;
}
// Convert obj_ptr into an A instance
static void construct(PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data* data) {
// extract 'n':
PyObject * n_ptr = PyObject_CallMethod(obj_ptr, (char*)"get_n", (char*)"()");
long n_val = 0;
if (n_ptr == NULL) {
cout << "... an exception occurred (get_n) ..." << endl;
} else {
n_val = PyInt_AsLong(n_ptr);
Py_DECREF(n_ptr);
}
// [snip] - also do the same for x, y
// Grab pointer to memory into which to construct the new A
void* storage = (
(boost::python::converter::rvalue_from_python_storage<A>*)
data)->storage.bytes;
// in-place construct the new A using the data
// extracted from the python object
new (storage) A(n_val, x_val, y_val);
// Stash the memory chunk pointer for later use by boost.python
data->convertible = storage;
}
// register converter functions
A_from_python_A() {
boost::python::converter::registry::push_back(
&convertible,
&construct,
boost::python::type_id<A>());
}
};
然后我将其注册:
BOOST_PYTHON_MODULE(interpolation_ext)
{
// register the from-python converter for A
A_from_python_A();
class_<A>("A", init<long, long, long>())
;
class_<B>("B", init<object>())
;
}
Convertible和construct是两种方法,分别回答“是否可以转换?”和“如何转换?”的问题。我观察到construct()方法并不简单 - 它必须访问A的PyObject*,提取所有相关字段,然后重建一个C++实例,再将其传递给B的构造函数。由于A包含一些私有字段,因此必须通过公共访问机制来完成这个过程(如果是纯Python对象就不需要,对吧?)。这似乎有效。
然而,“construct”函数中的字段提取真的必要吗?它似乎很费力。如果A是一个复合对象,它可能会变得非常复杂,并且可能需要一个转换器来调用另一个转换器。如果A是一个Python类,那么我或许能理解这种要求,但如果A实例来自C++端,有没有一种方法可以确定这种情况,然后简单地获得一个句柄(例如指针)来访问这个“本地”对象,以便快速进行操作?
以下是相关的Python代码:
from my_ext import A, B
a = A(1,2,3)
b = B(a)
b.doSomething()
A
作为参数时,Python将尝试从传递给函数的PyObject
构造A
C++类型。如果Python对象是A
Python包装器的实例,则从A
Python包装器中提取A
C++类型,并可能被复制构造到C++函数参数中。如果提取失败,则Boost.Python将尝试使用注册到boost::python::type_id< A >
的构造函数构造A
Python包装器。 - Tanner Sansbury