Python 與 C 擴充 -C++類別

Python 與 C 擴充 -C++類別

環境
windows 11 64bit
Visual Studio 2022
python 3.12.2 64bit

mytestmodule.cpp

#include <C:\Python312\include\Python.h>
#include <C:\Python312\include\structmember.h>

typedef struct {
    PyObject_HEAD
	int number;
	char name[128];
} MyTest;

static PyObject *MyTest_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    MyTest *self;

    self = (MyTest *)type->tp_alloc(type, 0);
    if (self != NULL) 
	{
		//TODO : constructor
		self->number = 0;
		strcpy(self->name, "def");
    }

    return (PyObject *)self;
}

static void MyTest_dealloc(MyTest* self)
{
	//TODO : destructor
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static int MyTest_init(MyTest *self, PyObject *args, PyObject *kwds)
{
	//TODO : member variable
    PyObject *first=NULL, *last=NULL, *tmp;

    static char *kwlist[] = {"number", "name", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|is", kwlist,
                                      &self->number, self->name))
        return -1;

    return 0;
}

static PyMemberDef MyTest_members[] = {
	//TODO : member variable
    {"number", T_INT, offsetof(MyTest, number), 0, "MyTest number"},
	{"name", T_CHAR, offsetof(MyTest, name), 0, "MyTest name"},
    {NULL}  /* Sentinel */
};

//TODO : member function
static PyObject *MyTest_TestPrint(MyTest* self, PyObject *args)
{
	char sz[1024];
	sprintf(sz, "MyTest_TestPrint");
	return Py_BuildValue("s", sz);
}
static PyObject *MyTest_Add(MyTest* self, PyObject *args)
{
	int nA;
	if (!PyArg_ParseTuple(args, "i", &nA))
		return NULL;

	self->number = self->number + nA;

	return Py_BuildValue("i", self->number);
}
static PyObject *MyTest_SetName(MyTest* self, PyObject *args)
{
	const char *command;
    if (!PyArg_ParseTuple(args, "s", &command))
		return NULL;
	
	strcpy(self->name, command);
    
	return Py_BuildValue("");
}

static PyObject *MyTest_GetName(MyTest* self, PyObject *args)
{
	return Py_BuildValue("s", self->name);
}

//TODO : member function
static PyMethodDef MyTest_methods[] = {
	{"TestPrint", (PyCFunction)MyTest_TestPrint, METH_NOARGS, "TestPrint"},
	{"Add", (PyCFunction)MyTest_Add, METH_VARARGS, "Add"},
	{"SetName", (PyCFunction)MyTest_SetName, METH_VARARGS, "SetName"},
	{"GetName", (PyCFunction)MyTest_GetName, METH_NOARGS, "GetName"},
    {NULL}  /* Sentinel */
};

static PyTypeObject MyTestType = {
    PyVarObject_HEAD_INIT(NULL, 0) /*ob_size*/
    "MyTest.MyTest",             /*tp_name*/
    sizeof(MyTest),             /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)MyTest_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    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*/
    "MyTest objects",           /* tp_doc */
    0,		               /* tp_traverse */
    0,		               /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    MyTest_methods,             /* tp_methods */
    MyTest_members,             /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)MyTest_init,      /* tp_init */
    0,                         /* tp_alloc */
    MyTest_new,                 /* tp_new */
};

static PyModuleDef MyTestmodule = {
    PyModuleDef_HEAD_INIT,
    "MyTest",
    "Example module that creates an extension type.",
    -1,
    NULL, NULL, NULL, NULL, NULL
};
#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC PyInit_mytestmodule(void) 
{
	PyObject* m;

    if (PyType_Ready(&MyTestType) < 0)
        return NULL;

    m = PyModule_Create(&MyTestmodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&MyTestType);
    PyModule_AddObject(m, "MyTest", (PyObject *)&MyTestType);
    return m;
}

compile.bat(debug版)

cl /LDd /MDd mytestmodule.cpp C:\Python312\libs\python3.lib C:\Python312\libs\python312_d.lib
copy mytestmodule.dll mytestmodule.pyd

openvc.bat

@echo off
%comspec% /k ""C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat"" x64

執行 openvc.bat,輸入 compile

編譯成功,會產生 mytestmodule.pyd

test.py

import mytestmodule
p = mytestmodule.MyTest()
print(p.number)
p.number = 3
print(p.number)
print(p.Add(10))
print(p.number)
print(p.GetName())
p.SetName("abc")
print(p.GetName())

執行 test.py 結果

回到目錄
https://husking-studio.com/extending-python-with-c

未解決問題
print(p.name)
會只印出第一個字元….

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *