【发布时间】:2018-11-07 01:19:11
【问题描述】:
最近用C++写了一个Python 3的扩展,但是在python中调用C++的时候遇到了一些麻烦,不打算用第三方库。
我用的是Python绑定C++虚成员函数不能调用,但是去掉virtual关键字就可以了。
跑到return PyObject_CallObject(pFunction, args);时崩溃了,但是我没找到原因。
这是我的代码:
class A
{
PyObject_HEAD
public:
A()
{
std::cout << "A::A()" << std::endl;
}
~A()
{
std::cout << "A::~A()" << std::endl;
}
virtual void test()
{
std::cout << "A::test()" << std::endl;
}
};
class B : public A
{
public:
B()
{
std::cout << "B::B()" << std::endl;
}
~B()
{
std::cout << "B::~B()" << std::endl;
}
static PyObject *py(B *self) {
self->test();
return PyLong_FromLong((long)123456);
}
};
static void B_dealloc(B *self)
{
self->~B();
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *B_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
B *self = (B*)type->tp_alloc(type, 0);
new (self)B;
return (PyObject*)self;
}
static PyMethodDef B_methods[] = {
{"test", (PyCFunction)(B::py), METH_NOARGS, nullptr},
{nullptr}
};
static struct PyModuleDef example_definition = {
PyModuleDef_HEAD_INIT,
"example",
"example",
-1,
B_methods
};
static PyTypeObject ClassyType = {
PyVarObject_HEAD_INIT(NULL, 0) "example.B", /* tp_name */
sizeof(B), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)B_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
"B objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
B_methods, /* tp_methods */
nullptr, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
nullptr, /* tp_init */
0, /* tp_alloc */
B_new, /* tp_new */
};
PyMODINIT_FUNC PyInit_example(void)
{
PyObject *m = PyModule_Create(&example_definition);
if (PyType_Ready(&ClassyType) < 0)
return NULL;
Py_INCREF(&ClassyType);
PyModule_AddObject(m, "B", (PyObject*)&ClassyType);
return m;
}
PyObject* importModule(std::string name)
{
PyObject* pModule = PyImport_ImportModule(name.c_str()); // module name
if (pModule == nullptr)
{
std::cout << "load module error!" << std::endl;
return nullptr;
}
return pModule;
}
PyObject* callFunction(PyObject* pModule, std::string name, PyObject* args = nullptr)
{
PyObject* pFunction = PyObject_GetAttrString(pModule, name.c_str()); // function name
if (pFunction == nullptr)
{
std::cout << "call function error!" << std::endl;
return nullptr;
}
return PyObject_CallObject(pFunction, args);
}
int main()
{
// add module
PyImport_AppendInittab("example", PyInit_example);
// init python
Py_Initialize();
{
PyRun_SimpleString("import sys");
PyRun_SimpleString("import os");
PyRun_SimpleString("sys.path.append(os.getcwd() + '\\script')"); // add script path
}
// import module
PyImport_ImportModule("example");
PyObject* pModule = importModule("Test");
if (pModule != nullptr)
{
PyObject* pReturn = callFunction(pModule, "main");
}
PyErr_Print();
Py_Finalize();
system("pause");
return 0;
}
【问题讨论】:
-
PyObject_CallObject 定义在哪里?
-
@DiegoContreras
PyObject_CallObject是python的API函数。 -
看起来你使用 CPython(我们也这样做)。 AFAIK,CPython 是用 C 编写的——至少它的 API 是 C API。所以,我不建议在 C++ 中进行扩展(又名 Wrapper “类”)。我最近遇到了一个烦人的错误,因为我忘记包含
PyObject_HEAD。 (这是继承的“C 风格”。Python 期望在每个PyObject中都有PyObject_HEAD的内容。)使用虚成员函数创建一个“继承”类,VMT(虚方法表)可能被放置在偏移处Python 期望PyObject_HEAD的地方 - 非常危险。 -
此外,Python 调用 C 函数。成员函数不适合 C 函数指针(静态成员函数除外)。 TLDR 可以在 Python 中绑定 C++ 对象,但不要使用 C++ 进行包装——这可能会崩溃。
-
另一个提示:可以将 C++ 对象作为 CPython 包装器
structs 的成员。这样做时,我意识到我必须在tp_alloc(type, 0);之后使用放置new构造这些成员(并注意通过显式析构函数调用来销毁它们)。我看到您在暴露的代码中使用整个包装器尝试了类似的东西。所以,我想你已经意识到了这一点。
标签: python c++ python-3.x python-c-api