%typemap and %exception for error codes from C functions for SWIG, Python

拜拜、爱过 提交于 2019-12-06 14:39:49

The trick is not to use %exception but to define %typemap(argout). Also don't refer to your temporary variable directly. The %typemap(in) suppresses the argument in the target language and provides a local temporary variable, but you should still refer to the argument itself in %typemap(argout). Here's a modified version of your original .i file. I've also added more generic exception throwing, so it should work for other languages also:

%module x
%feature("autodoc","1");

// Disable some Windows warnings on the generated code
%begin %{
#pragma warning(disable:4100 4127 4211 4706)
%}

%{
int add(int a, int b, int *err){
    if(a < 0)*err = 1;
    if(b < 0)*err = 2;
    return a+b;
}

char *err_string(int err){
    switch(err){
    case 1:return "first argument was less than 0";
    case 2:return "second argument was less than 0";
    default:return "unknown error";
    }
}
%}

%include <exception.i>

%typemap(in,numinputs=0) int *err (int myerr = 0) {
    $1 = &myerr;
}

%typemap(argout) int* err {
    if(*$1 != 0) {
        SWIG_exception(SWIG_ValueError,err_string(*$1));
    }
}

int add(int a, int b, int *err);

And here is the result:

Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import x
>>> x.add(1,1)
2
>>> x.add(3,4)
7
>>> x.add(-1,4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "x.py", line 73, in add
    return _x.add(*args)
RuntimeError: first argument was less than 0
>>> x.add(3,-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "x.py", line 73, in add
    return _x.add(*args)
RuntimeError: second argument was less than 0

From Karl Wette, via the swig-user mailing list:

You could modify your "in" typemap by moving the declaration of "myerr" inside the typemap:

%typemap(in,numinputs=0, noblock=1) int *err {
   int myerr = 0;
   $1 = &myerr;
};

So long as there's only one "int *err" argument in each function, this should be fine. You can then use "myerr" directly without the argument number.

This seems to be exactly the right solution, no kludges required. Thanks Karl!

If you're willing to accept that it won't be re-entrant you can use a global instead of myerr3, e.g.:

%{
static int myerr = 0;
%}

%typemap(in,numinputs=0) int *err {
    $1 = &myerr;
};

%exception{
    $action
    if(myerr != 0){
        PyErr_SetString(PyExc_RuntimeError,err_string(myerr));
        return NULL;
    }
};

The other alternative is to slightly abuse the freearg typemap, instead of the %exception:

// "" makes sure we don't go inside {}, which means using alloca is sane
%typemap(in,numinputs=0) int *err "*($1=alloca(sizeof(int)))=0;"

%typemap(freearg) int *err {
    if (*$1 != 0) {
       PyErr_SetString(PyExc_RuntimeError,err_string($1));
       SWIG_fail;
    }
}

Or if you can't use alloca:

%typemap(in,numinputs=0) int *err {
    $1=malloc(sizeof(int));
    *$1=0;
}

%typemap(freearg) int *err {
    if ($1 && *$1 != 0) {
       PyErr_SetString(PyExc_RuntimeError,err_string($1));
       // Don't leak even if we error
       free($1);
       $1=NULL; // Slightly ugly - we need to avoid a possible double free
       SWIG_fail;
    }
    free($1);
    $1=NULL; // even here another arg may fail
}

There's a third possible (bodge) approach you might use:

%{
static const int myerr1 = 0;
static const int myerr2 = 0;
static const int myerr3 = 0;
static const int myerr4 = 0;
static const int myerr5 = 0;
//...
%}

%typemap(in,numinputs=0) int *err (int myerr = 0){
    $1 = &myerr;
}

%exception{
    $action
    // Trick: The local myerrN from the typemap "masks" the const global one!
    if(myerr1 != 0 || myerr2 != 0 || myerr3 != 0 || myerr4 != 0 || myerr5 != 0) {
        PyErr_SetString(PyExc_RuntimeError,err_string(myerr1|myerr2|myerr3|myerr4|myerr5));
        return NULL;
    }
}

The trick is that the specific myerrN from the typemap masks the static const global ones - the if statement is always referring only to one, local constant which is the only one that can be non-zero

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!