Python 與 C 擴充 -將 C++ 指標傳去給 python
環境
windows 11 64bit
Visual Studio 2022
python 3.12.2 64bit
Visual Studio 2022 環境設置
1. 準備一個簡單的類別
car.h
#ifndef __CAR_H
#define __CAR_H
class CCar
{
private:
int m_nSize;
char m_szName[128];
public:
CCar();
~CCar();
public:
#ifdef _INPYTHON
void __declspec(dllexport) SetSize(int nSize);
int __declspec(dllexport) GetSize();
void __declspec(dllexport) SetName(char* szName);
char __declspec(dllexport)* GetName();
#else
void SetSize(int nSize);
int GetSize();
void SetName(char* szName);
char* GetName();
#endif
};
#endif
car.cpp
#include "car.h"
#include <string.h>
CCar::CCar()
{
m_nSize = 0;
strcpy_s(m_szName, "");
}
CCar::~CCar()
{
}
void CCar::SetSize(int nSize)
{
m_nSize = nSize;
}
int CCar::GetSize()
{
return m_nSize;
}
void CCar::SetName(char* szName)
{
strcpy_s(m_szName, szName);
}
char* CCar::GetName()
{
return m_szName;
}
然後幫 car 做一個要給 python 用的連結檔案
pythoncar.cpp
#include <C:\Python312\include\Python.h>
#include <C:\Python312\include\structmember.h>
//[include-start]
#include "car.h"
#pragma comment (lib, "car.lib")
//[include-end]
//[global-start]
CCar *g_p = 0;
//[global-end]
typedef struct {
PyObject_HEAD
//TODO :
//[variable-start]
//[variable-end]
} pycar;
static PyObject *pycar_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
pycar *self;
self = (pycar *)type->tp_alloc(type, 0);
if (self != NULL)
{
//TODO : constructor
//[constructor-start]
//[constructor-end]
}
return (PyObject *)self;
}
static void pycar_dealloc(pycar* self)
{
//TODO : destructor
//[destructor-start]
//[destructor-end]
Py_TYPE(self)->tp_free((PyObject*)self);
}
static int pycar_init(pycar *self, PyObject *args, PyObject *kwds)
{
PyObject *first=NULL, *last=NULL;
static char *kwlist[] = {NULL};
if (! PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist))
return -1;
return 0;
}
//TODO : function
//[function-body-start]
static PyObject *pycar_TestPrint(pycar* self, PyObject *args)
{
char sz[1024];
sprintf(sz, "pycar_TestPrint");
return Py_BuildValue("s", sz);
}
static PyObject *pycar_SetPoint(pycar* self, PyObject *args)
{
PyObject *Pymatin;
CCar *b;
if (!PyArg_ParseTuple(args, "O", &Pymatin))
return Py_BuildValue("s", "error");
b = (CCar *)PyCapsule_GetContext(Pymatin);
g_p = b;
return Py_BuildValue("");
}
static PyObject *pycar_GetSize(pycar* self, PyObject *args)
{
int nNumber = g_p->GetSize();
return Py_BuildValue("i", nNumber);
}
static PyObject *pycar_SetSize(pycar* self, PyObject *args)
{
int nValue;
PyArg_ParseTuple(args, "i", &nValue);
g_p->SetSize(nValue);
return Py_BuildValue("");
}
static PyObject *pycar_GetName(pycar* self, PyObject *args)
{
char sz[1024] = "";
sprintf(sz, "%s", g_p->GetName());
return Py_BuildValue("s", sz);
}
static PyObject *pycar_SetName(pycar* self, PyObject *args)
{
const char *command;
PyArg_ParseTuple(args, "s", &command);
char sz[128] = "";
sprintf(sz, "%s", command);
g_p->SetName(sz);
return Py_BuildValue("");
}
//[function-body-end]
static PyMethodDef pycar_methods[] = {
{"TestPrint", (PyCFunction)pycar_TestPrint, METH_NOARGS, "TestPrint"},
//[function-define-start]
{"SetPoint", (PyCFunction)pycar_SetPoint, METH_VARARGS, "SetPoint"},
{"GetSize", (PyCFunction)pycar_GetSize, METH_NOARGS, "GetSize"},
{"SetSize", (PyCFunction)pycar_SetSize, METH_VARARGS, "SetSize"},
{"GetName", (PyCFunction)pycar_GetName, METH_NOARGS, "GetName"},
{"SetName", (PyCFunction)pycar_SetName, METH_VARARGS, "SetName"},
//[function-define-end]
{NULL} /* Sentinel */
};
static PyTypeObject pycarType = {
PyVarObject_HEAD_INIT(NULL, 0) /*ob_size*/
"pycar.pycar", /*tp_name*/
sizeof(pycar), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pycar_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*/
"pycar objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pycar_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)pycar_init, /* tp_init */
0, /* tp_alloc */
pycar_new, /* tp_new */
};
static PyMethodDef module_methods[] = {
{NULL} /* Sentinel */
};
static PyModuleDef pycarmodule = {
PyModuleDef_HEAD_INIT,
"pycar",
"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_pythoncar(void)
{
PyObject* m;
if (PyType_Ready(&pycarType) < 0)
return NULL;
m = PyModule_Create(&pycarmodule);
if (m == NULL)
return NULL;
Py_INCREF(&pycarType);
PyModule_AddObject(m, "pycar", (PyObject *)&pycarType);
return m;
}
2. 編譯
2. 寫一個 compile.bat
cl /EHsc /LD /D_INPYTHON /I/C:\Python312\include car.cpp C:\Python312\libs\python3.lib C:\Python312\libs\python312.lib
cl /EHsc /LD /I/C:\Python312\include pythoncar.cpp C:\Python312\libs\python3.lib C:\Python312\libs\python312.lib
寫一個打開 Visual Studio 的 batch 命令視窗檔案 02_VC.bat
@echo off
%comspec% /k ""C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat"" x64
執行 02_VC.bat,輸入 compile
成功之後會產生 car.dll 和 pythoncar.dll
將 pythoncar.dll 複製改名為 pythoncar.pyd
3. 再來回到 Visual Studio
main.cpp
#include <iostream>
#include <Python.h>
#include "car.h"
using namespace std;
void Test01();
void TTest01(CCar* pCar);
void TTest02(CCar* pCar);
int main()
{
Py_Initialize();
/////////////////////////////////////////////////////////////
Test01();
/////////////////////////////////////////////////////////////
Py_Finalize();
system("pause");
return 0;
}
void Test01()
{
char sz[128] = "Set In C++";
CCar* pCar = new CCar();
pCar->SetSize(20);
pCar->SetName(sz);
TTest02(pCar);
delete pCar;
}
void TTest01(CCar* pCar)
{
PyObject* pModule, * pFunc;
PyObject* pArgs, * pValue;
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
/* import */
pModule = PyImport_Import(PyUnicode_FromString("test01"));
/* great_module.great_function */
pFunc = PyObject_GetAttrString(pModule, "Test01");
pArgs = PyTuple_New(1);
PyObject* p = PyCapsule_New((void*)pCar, "", 0);
PyCapsule_SetContext(p, (void*)pCar);
PyTuple_SetItem(pArgs, 0, p);
pValue = PyObject_CallObject(pFunc, pArgs);
if (pValue)
{
CCar* pPyCar = (CCar*)PyCapsule_GetContext(pValue);
printf("--after python pValue--\n");
printf("GetName = %s\n", pPyCar->GetName());
printf("GetSize = %d\n", pPyCar->GetSize());
}
}
void TTest02(CCar* pCar)
{
PyObject* pModule, * pFunc;
PyObject* pArgs, * pValue;
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
/* import */
pModule = PyImport_Import(PyUnicode_FromString("test01"));
/* great_module.great_function */
pFunc = PyObject_GetAttrString(pModule, "Test01");
pArgs = PyTuple_New(1);
PyObject* p = PyCapsule_New((void*)pCar, "", 0);
PyCapsule_SetContext(p, (void*)pCar);
PyTuple_SetItem(pArgs, 0, p);
pValue = PyObject_CallObject(pFunc, pArgs);
if (pValue != 0)
{
printf("--after python pCar--\n");
printf("GetName = %s\n", pCar->GetName());
printf("GetSize = %d\n", pCar->GetSize());
}
}
test01.py
import pythoncar
def Test01(x):
print ("Test01")
p = pythoncar.pycar()
print(p.TestPrint())
p.SetPoint(x)
print("--value from C++ --")
print(p.GetName())
print(p.GetSize())
print("--set from python --")
p.SetName("Set In python")
p.SetSize(200)
return x
執行結果
小提醒:要確定這些檔案和執行檔放一起