I recently wrote an extension to Python 3 in C++, but I encountered some trouble when I called C++ in python, and I don't plan to use a third-party library.
I'm used Python binding C++ virtual member function cannot be called, but removing the virtual keyword is all right.
It crashed when it ran to return PyObject_CallObject(pFunction, args);, but I didn't find the reason.
Here is my code:
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;
}
 
    