SWIG中的%rename和%inline,错误检查

2 投票
1 回答
2549 浏览
提问于 2025-04-17 10:09

我在使用SWIG和Numpy。为了快速处理数据数组,我定义了一个叫做inplace()的C函数,并且我想添加一些错误检查(比如检查两个数组的维度是否相同)。

.i文件中,我使用了%rename%inline。我理解的是,rename应该是把函数名映射过来,所以每次有人使用inplace的时候,实际上应该是运行safe_inplace,这样就能检查错误了。

但是这并没有成功 :( 。我注意到,safe_inplace并没有被执行,Python直接运行inplace,根本没有调用安全版本的函数。

# .i

%include "inplace.h"
%rename (inplace) safe_inplace;

%inline %{
    void safe_inplace(int* datain, int in_dx, int in_dy, int in_dz,
                      int* dataout, int out_dx, int out_dy)
    {
        if ((in_dx != out_dx) || (in_dy != out_dy)) { 
            PyErr_Format(PyExc_ValueError, /*... messgage*/) 
            return;
        } 

        inplace( /* .. pass the arguments to original function*/ );
    }

头文件:

# .h 

void inplace(int* datain, int in_dx, int in_dy, int in_dz, int* dataout, int out_dx, int out_dy);

Python代码:

#.py
inplace.inplace(a,b)

我修改的原始示例可以在这里找到。

1 个回答

5

你最开始的方法差不多,但你可能想让SWIG完全不包装原始的函数。

我整理了一个稍微简单一点的例子,来说明这怎么实现。假设有一个头文件:

void foo();

我们想要在调用真正的函数之前,先调用一个稍微修改过的版本。

最简单的方法就是根本不让SWIG看到这个头文件进行包装,只在编译包装的时候用到,比如:

%module test

%{
#include "test.h"
#include <iostream>
%}

%rename (foo) foo_safe;
%inline %{
  void foo_safe() {
    std::cout << "Hello world" << std::endl;
    foo(); // Calls the foo() from test.h, as you'd hope
  }
%}

如果你不想去掉%include(比如说你还关心这个头文件里的其他内容),你可以这样做:

%module test

%{
#include "test.h"
#include <iostream>
%}

%rename (unsafe_foo) foo;    
%include "test.h"

%rename (foo) foo_safe;    
%inline %{
  void foo_safe() {
    std::cout << "Hello world" << std::endl;
    foo();
  }
%}

这样可以把真正的实现foo暴露为unsafe_foo

或者如果没有必要让Python用户调用unsafe_ignore,你可以使用%ignore

%module test

%{
#include "test.h"
#include <iostream>
%}

%rename (foo) foo_safe;    
%inline %{
  void foo_safe() {
    std::cout << "Hello world" << std::endl;
    foo();
  }
%}

%ignore foo;    
%include "test.h"

最后,看起来你的目标其实只是想在调用真正的C函数之前先运行一些代码。如果是这样,你也有几种方法可以做到,比如你可以在真正的函数调用之前加上Python代码,使用pythonprepend

%feature("pythonprepend") foo() %{
   print "hello world"
   # check args and raise possibly 
%}

或者你也可以使用%exception功能,像这样(未经测试):

%exception inplace {
   // Check args and possibly throw
   $action
}

$action会自动替换为真正的调用。

撰写回答