如何让SWIG自动将模拟的“this”指针包装到C结构体?

时间:2021-09-27 21:23:48

I've got a simple C "class" I have implemented, using function pointers in a struct to implement the member functions, and passing a pointer to the struct as the first argument to each function, similar to the implicit "this" pointer in C++.

我实现了一个简单的C“类”,使用struct中的函数指针来实现成员函数,并将指向struct的指针作为每个函数的第一个参数传递,类似于c++中的隐式“this”指针。

%module mytest
%{
typedef struct mytest mytest;

struct mytest {
  int data;
  int (*func1)(mytest *,int);
  void (*func2)(mytest *,int);
};

int f1(mytest *me,int n) { return me->data + n; }
void f2(mytest *me,int n) { me->data += n; }

mytest *mytestNew(int n) {
  mytest *me = (mytest*) malloc(sizeof(mytest));
  me->data = n;
  me->func1 = f1;
  me->func2 = f2;
  return me;
}

%}

typedef struct mytest mytest;

struct mytest {
  int data;
  int func1(mytest *,int);
  void func2(mytest *,int);
};

extern mytest *mytestNew(int n);

Now my problem is, when the interface is created to whatever language I choose in the front end, I wind up having to explicitly pass the "this" pointer to the object, even though the language itself supports hiding this.

现在我的问题是,当接口被创建到我在前端选择的任何语言时,我最终不得不显式地将“this”指针传递给对象,即使语言本身支持隐藏它。

For instance, suppose I choose Python. I have to do something like this:

例如,假设我选择Python。我必须这样做:

from mytest import *
m = mytestNew(1)
m.func1(m,0)

Where what I really want is to do it like this:

我真正想做的是:

from mytest import *
m = mytestNew(1)
m.func1(0)

I know I could just write some wrapping code, but for my actual project I have a lot of functions in a lot of objects of existing C code, and multiplying this by every language that I want to support, this is just too much work! Is there some way to get SWIG to do this automatically?

我知道我可以只写一些包装代码,但是对于我的实际项目来说,我在现有的C代码的很多对象中有很多函数,并且把它乘以我想要支持的每一种语言,这是太多的工作了!有什么方法可以让SWIG自动完成这项任务吗?

1 个解决方案

#1


2  

You can do this in a language neutral way in SWIG with just two typemaps provided you name the parameter something consistent in the SWIG interface as well as the definitions to allow the typemaps to be applied selectively. (Unless you wanted all pointers to mytest to become "this" pointers by default of course)

您可以在SWIG中以一种与语言无关的方式实现这一点,只需提供两个类型ap,前提是您将参数命名为SWIG接口中一致的内容,以及允许有选择地应用类型ap的定义。(当然,除非您希望所有指向mytest的指针都默认为“this”指针)

The typemaps you need are:

您需要的类型是:

// Make sure the wraqpped function doesn't expect an input for this:
%typemap(in,numinputs=0) mytest *me "$1=NULL;"
// Slightly abuse check typemap, but it needs to happen after the rest of the arguments have been set:
%typemap(check) mytest *me {
  $1 = arg1;
}

The check typemap isn't really intended for use like this, but it's the easiest way to get the code to be injected after the arguments have been extracted from the target language and before the actual call is made.

check typemap实际上并不是这样使用的,但它是在从目标语言中提取参数并在实际调用之前将代码注入的最简单方法。

You can also simplify the module with the help of a macro to avoid having to write and keep in sync the mapping between the function pointers and the members trick. I ended up with test.h as:

您还可以使用一个宏来简化模块,以避免编写函数指针和成员之间的映射保持同步。最后我做了测试。h:

#ifdef SWIG
#define MEMBER(name, args) name args
#else
#define MEMBER(name, args) (*name) args
#endif

typedef struct mytest mytest;

struct mytest {
  int data;
  int  MEMBER(func1,(mytest *me,int));
  void MEMBER(func2,(mytest *me,int));
};

And the corresponding interface file (test.i):

以及相应的接口文件(test.i):

%module test

%{
#include "test.h"

static int f1(mytest *me,int n) { return me->data + n; }
static void f2(mytest *me,int n) { me->data += n; }
%}

%extend mytest {
  mytest(int n) {
    $self->data = n;
    $self->func1 = f1;
    $self->func2 = f2;
  }
}

%typemap(in,numinputs=0) mytest *me "$1=NULL;"
%typemap(check) mytest *me {
  $1 = arg1;
}

%include "test.h"

(This interface file provides a constructor that "creates" the "object" exactly how a Java programmer would expect - you can call new and it sets the function pointers behind the scenes)

(这个接口文件提供了一个构造函数,该构造函数“创建”Java程序员所期望的“对象”——您可以调用new,它在后台设置函数指针)

#1


2  

You can do this in a language neutral way in SWIG with just two typemaps provided you name the parameter something consistent in the SWIG interface as well as the definitions to allow the typemaps to be applied selectively. (Unless you wanted all pointers to mytest to become "this" pointers by default of course)

您可以在SWIG中以一种与语言无关的方式实现这一点,只需提供两个类型ap,前提是您将参数命名为SWIG接口中一致的内容,以及允许有选择地应用类型ap的定义。(当然,除非您希望所有指向mytest的指针都默认为“this”指针)

The typemaps you need are:

您需要的类型是:

// Make sure the wraqpped function doesn't expect an input for this:
%typemap(in,numinputs=0) mytest *me "$1=NULL;"
// Slightly abuse check typemap, but it needs to happen after the rest of the arguments have been set:
%typemap(check) mytest *me {
  $1 = arg1;
}

The check typemap isn't really intended for use like this, but it's the easiest way to get the code to be injected after the arguments have been extracted from the target language and before the actual call is made.

check typemap实际上并不是这样使用的,但它是在从目标语言中提取参数并在实际调用之前将代码注入的最简单方法。

You can also simplify the module with the help of a macro to avoid having to write and keep in sync the mapping between the function pointers and the members trick. I ended up with test.h as:

您还可以使用一个宏来简化模块,以避免编写函数指针和成员之间的映射保持同步。最后我做了测试。h:

#ifdef SWIG
#define MEMBER(name, args) name args
#else
#define MEMBER(name, args) (*name) args
#endif

typedef struct mytest mytest;

struct mytest {
  int data;
  int  MEMBER(func1,(mytest *me,int));
  void MEMBER(func2,(mytest *me,int));
};

And the corresponding interface file (test.i):

以及相应的接口文件(test.i):

%module test

%{
#include "test.h"

static int f1(mytest *me,int n) { return me->data + n; }
static void f2(mytest *me,int n) { me->data += n; }
%}

%extend mytest {
  mytest(int n) {
    $self->data = n;
    $self->func1 = f1;
    $self->func2 = f2;
  }
}

%typemap(in,numinputs=0) mytest *me "$1=NULL;"
%typemap(check) mytest *me {
  $1 = arg1;
}

%include "test.h"

(This interface file provides a constructor that "creates" the "object" exactly how a Java programmer would expect - you can call new and it sets the function pointers behind the scenes)

(这个接口文件提供了一个构造函数,该构造函数“创建”Java程序员所期望的“对象”——您可以调用new,它在后台设置函数指针)