You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过SWIG将Python函数赋值给C结构体的函数指针变量?

问题核心

需要将Python函数赋值给C结构体中的函数指针成员,但使用SWIG默认生成的setter函数时会触发类型不匹配错误,且无法修改原始C代码,只能通过SWIG/Python层面解决。

示例代码

C代码:

typedef void (*MyFunctionPtr)();

struct MyStruct {
    MyFunctionPtr func;
};

Python代码:

import example 
def my_python_function():
    print("Hello from Python!")

my_struct = example.MyStruct()
my_struct.func = my_python_function  # 此处抛出类型不匹配错误

已尝试的无效方案

  • 直接编写void* MyFunctionPtr的typemap:因将PyObject指针直接强转为函数指针,违背内存语义,C代码调用时会执行无效内存,导致错误。
  • 参考SWIG回调示例:未适配结构体成员赋值场景,未解决核心的函数指针与Python对象的映射问题。
  • 全局PyObject存储方案:通过构造函数传递Python函数并绑定到全局变量,属于不良设计,无法支持多实例场景。

可行解决方案

核心思路是通过C包装函数中转:C函数指针指向一个符合签名的C包装函数,该包装函数负责调用对应的Python可调用对象,同时维护Python对象的引用计数避免被GC回收。

完整SWIG .i 配置

%module example

%{
#include <Python.h>
// 引入你的C头文件路径
#include "your_header.h"

// 符合MyFunctionPtr签名的C包装函数,负责调用Python对象
static void py_callback_thunk() {
    PyObject* func = (PyObject*)SwigGetClientData();
    if (func && PyCallable_Check(func)) {
        PyObject_CallObject(func, NULL);
    }
}
%}

// 为MyStruct的func成员编写setter typemap,处理Python可调用对象
%typemap(set) MyFunctionPtr MyStruct::func %{
    // 校验输入是否为可调用对象
    if (!PyCallable_Check($input)) {
        PyErr_SetString(PyExc_TypeError, "必须传入可调用的Python对象");
        return NULL;
    }

    // 清理旧的Python对象引用(如果存在)
    if ($self->func == py_callback_thunk) {
        PyObject* old_func = (PyObject*)SwigGetClientData();
        Py_DECREF(old_func);
    }

    // 增加新对象的引用计数,防止被GC回收
    Py_INCREF($input);
    // 将Python对象绑定到包装函数的客户端数据中
    SwigSetClientData((void*)py_callback_thunk, $input);
    // 将结构体的函数指针指向包装函数
    $1 = py_callback_thunk;
%}

// 为结构体增加析构函数,释放Python对象引用
%extend struct MyStruct {
    ~MyStruct() {
        if ($self->func == py_callback_thunk) {
            PyObject* func = (PyObject*)SwigGetClientData();
            Py_DECREF(func);
        }
    }
};

// 引入结构体定义
%include "your_header.h"

方案说明

  1. 包装函数中转:py_callback_thunk是符合C函数指针签名的中间层,C代码调用结构体的func时,实际执行该包装函数,再由它调用对应的Python函数。
  2. 引用计数管理:通过Py_INCREF和Py_DECREF维护Python对象的生命周期,避免被垃圾回收器误释放。
  3. 客户端数据绑定:利用SWIG的SwigSetClientData和SwigGetClientData实现包装函数与Python对象的关联。
  4. 资源清理:通过扩展结构体的析构函数,在结构体销毁时释放Python对象的引用,避免内存泄漏。

Python端使用

配置完成后,原Python代码可以直接运行:

import example 
def my_python_function():
    print("Hello from Python!")

my_struct = example.MyStruct()
my_struct.func = my_python_function  # 赋值成功
# 当C代码调用my_struct->func()时,会自动触发Python函数执行

内容的提问来源于stack exchange,提问作者Maverickgugu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 13:55:32