У меня есть код C++, который определяет два класса, A и B. B принимает экземпляр A во время построения. Я обернул A с помощью Boost.Python, чтобы Python мог создавать экземпляры A, а также подклассы. Я хочу сделать то же самое с Б.
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*)
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());
}
};
Затем я регистрирую это с помощью:
BOOST_PYTHON_MODULE(interpolation_ext)
{
// register the from-python converter for A
A_from_python_A();
class_("A", init())
;
class_("B", init
Конвертируемый и конструктивный методы — это методы, отвечающие на вопрос «это конвертируемый?» и "как преобразовать?" вопросы соответственно. Я заметил, что метод построения ()не является -тривиальным -, он должен обращаться к PyObject A *, извлекать все соответствующие поля, затем перестраивать экземпляр C++, который затем передается конструктору B. Поскольку A содержит некоторые частные поля, он должен делать это через механизмы общего доступа (, тогда как с чистым объектом Python этого не требуется, верно? ). Кажется, это работает.
Однако,Действительно ли необходимо извлечение поля в функции «конструировать»? Это кажется трудоемким. Если A является составным объектом, это может стать очень сложным и, возможно, потребовать, чтобы один преобразователь вызывал другой. Возможно, я понимаю требование, если A является классом Python, но если экземпляр A создан на стороне C++, есть ли способ определить, что это так, а затем просто получить дескриптор (, например. указатель )на этот «родной» объект в качестве ярлыка?
Вот соответствующий код Python:
from my_ext import A, B
a = A(1,2,3)
b = B(a)
b.doSomething()