Python 與 C 擴充 -將 C++ 指標傳去給 python

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

執行結果

小提醒:要確定這些檔案和執行檔放一起

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

發佈留言

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