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

如何用SWIG实现Numpy数组与自定义模板类的对接?

问题

我尝试用SWIG实现Python与C++代码的对接,遇到了转换函数未生效的问题,简化后的场景如下:

背景代码

fxp类型定义

template<int W, int I, bool S, ac_q_mode Q, ac_o_mode O, bool force_float>
using fxp = std::conditional_t<force_float, double, ac_fixed<W, I, S, Q, O>>;

该类型借助AC types库处理定点数,通过force_float参数可强制转为double。

node模板类

#ifndef NODE_H
#define NODE_H

#include <deque>
#include <algorithm>
#include "ac_types.h"

template<typename T>
class node {
  public:
    node() {}
    ~node() {}

    T pop() {
      T sample = fifo.front();
      fifo.pop_front();
      return sample;
    }

    void push(const T &sample){
      fifo.push_back(sample);
      data.push_back(sample);
    }

    bool empty() {
      return fifo.empty();
    }

    size_t size() {
      return data.size();
    }

   
    template<typename U> std::deque<T> get();

    std::deque<double> get(){
      std::deque<double> rval(data.size());
      std::transform( data.cbegin(), data.cend(), rval.begin(), [](T arg) -> double { return (double) arg; });
      return rval;
    }

    std::deque<T> fifo;
    std::deque<T> data;
};
#endif

希望在Python中访问node类的fifo和data成员,但不想显式实例化所有定点类型模板。

当前SWIG配置

%begin %{
#define SWIG_PYTHON_CAST_MODE
%}

%ignore log;
%ignore fx_cntxt;

%module libdsp 
  %{
  #include "ac_types.h"
  #include "node.h"
  #include <deque>
  #include <numpy/ndarrayobject.h>
  
  %}

%include "numpy.i"
%include "std_deque.i"
%include "std_vector.i"
%include "std_string.i"

// Initialize NumPy API
%init %{
import_array();
%}

// Generic typemap for std::deque<T> to NumPy array conversion
%typemap(out) std::deque<T> {
    $result = deque_to_numpy($1);
}

// Generic typemap for NumPy array to std::deque<T> conversion
%typemap(in) std::deque<T> (PyObject* obj) {
    $1 = numpy_to_deque<T>(obj);
}

namespace std {
  %template(vectori) vector<int>;
  %template(vectord) vector<double>;
  %template(dequei)  deque<int>;
  %template(dequed)  deque<double>;
};

%include "ac_types.h"
%include "node.h"

%template(node1) node<fxp<16, 1, AC_RND, AC_SAT, false>>;
%template(node2) node<fxp<8, 1, AC_RND, AC_SAT, true>>;

转换函数实现

template<typename T>
PyObject* deque_to_numpy(const std::deque<T>& deque) {
    npy_intp size = deque.size();
    
    PyObject* numpy_array = PyArray_SimpleNew(1, &size, NPY_DOUBLE);
    
    double* data = static_cast<double*>(PyArray_DATA((PyArrayObject*)numpy_array));
    if (std::is_same<T, double>::value) {
        for (npy_intp i = 0; i < size; ++i) {
            data[i] = deque[i];
        }
    } else {
        for (npy_intp i = 0; i < size; ++i) {
            data[i] = deque[i].to_double();
        }
    }
    
    return numpy_array;
}

template<typename T>
std::deque<T> numpy_to_deque(PyObject* numpy_array) {
    std::deque<T> deque;
    
    if (!PyArray_Check(numpy_array)) {
        throw std::invalid_argument("Input is not a valid NumPy array");
    }
    
    PyArrayObject* array = (PyArrayObject*)numpy_array;
    npy_intp size = PyArray_SIZE(array);
    
    double* data = static_cast<double*>(PyArray_DATA(array));
    if (std::is_same<T, double>::value) {
        for (npy_intp i = 0; i < size; ++i) {
            deque.push_back(data[i]);
        }
    } else {
        for (npy_intp i = 0; i < size; ++i) {
            deque.push_back(T(data[i]));
        }
    }
    
    return deque;
}

问题现象

生成的SWIG包装代码中完全没有转换函数的痕迹,无法实现Python与node实例内容的对接。


解决方案

问题根源

  1. 泛型typemap未匹配具体化类型:SWIG的std::deque<T>泛型typemap不会自动应用到std::deque<fxp<...>>这类具体化模板类型,需显式关联。
  2. 转换函数未暴露给SWIG:deque_to_numpy和numpy_to_deque仅在C++中定义,SWIG无法识别这些函数,无法生成调用逻辑。
  3. 默认包装逻辑覆盖自定义typemap:%include "std_deque.i"会生成std::deque的默认包装,可能覆盖自定义typemap。

修复步骤

1. 让SWIG识别并实例化转换函数

在SWIG接口文件的%{ ... %}块中添加转换函数的声明和显式实例化,确保SWIG能生成对应调用代码:

%{
// 声明转换函数
template<typename T>
PyObject* deque_to_numpy(const std::deque<T>& deque);

template<typename T>
std::deque<T> numpy_to_deque(PyObject* numpy_array);

// 实例化对应fxp和double类型的版本
template PyObject* deque_to_numpy(const std::deque<fxp<16, 1, AC_RND, AC_SAT, false>>&);
template PyObject* deque_to_numpy(const std::deque<fxp<8, 1, AC_RND, AC_SAT, true>>&);
template PyObject* deque_to_numpy(const std::deque<double>&);

template std::deque<fxp<16, 1, AC_RND, AC_SAT, false>> numpy_to_deque(PyObject*);
template std::deque<fxp<8, 1, AC_RND, AC_SAT, true>> numpy_to_deque(PyObject*);
template std::deque<double> numpy_to_deque(PyObject*);
%}

2. 调整typemap优先级

将自定义typemap放在%include "std_deque.i"之前,或者先清除默认typemap再定义自己的:

// 清除std::deque的默认out/in typemap
%clear std::deque<T>;
%typemap(out) std::deque<T> {
    $result = deque_to_numpy($1);
}

%clear std::deque<T>;
%typemap(in) std::deque<T> (PyObject* obj) {
    $1 = numpy_to_deque<T>(obj);
}

// 之后再包含std_deque.i
%include "std_deque.i"

3. 为node成员变量单独设置typemap

如果泛型typemap不生效,可直接针对node<T>的成员变量定义typemap:

%typemap(out) node<T>::fifo, node<T>::data {
    $result = deque_to_numpy($1);
}

%typemap(in) node<T>::fifo, node<T>::data (PyObject* obj) {
    $1 = numpy_to_deque<T>(obj);
}

4. 简化模板实例化(可选)

若不想手动实例化所有fxp类型,可通过宏批量生成:

#define DEFINE_NODE(W, I, S, Q, O, FF) \
%template(node_##W##_##I##_##S##_##Q##_##O##_##FF) node<fxp<W, I, S, Q, O, FF>>;

// 调用宏生成需要的实例
DEFINE_NODE(16, 1, AC_RND, AC_SAT, false);
DEFINE_NODE(8, 1, AC_RND, AC_SAT, true);

验证要点

  • 检查生成的libdsp_wrap.cxx文件,确认是否存在调用deque_to_numpy和numpy_to_deque的代码。
  • 在Python中测试访问node.fifo/node.data是否返回NumPy数组,以及赋值操作是否正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 09:07:02