Swig:处理简化界面的最佳方式

Swig : best way to handle a simplified interface

本文关键字:最佳 方式 界面 处理 Swig      更新时间:2023-10-16

我们使用Swig向Python公开了一组C++类。我们经常在方法中添加新的参数。另一方面,我们有一组Python脚本,我们希望尽可能容易地维护这些脚本。因此,当我们在C++中更改公开方法的参数集时,我们不希望必须更改所有使用此方法的python脚本。为此,我们为每个C++类创建了一个辅助接口类(C++类也是),它拥有一个指向原始类实例的指针,并公开了一个简化的接口。

例如,假设我们有一个方法为foo:的类a

class A {
public :
void foo (int a, int b, double c, char * d, ....);
};

假设我们只对python中的a、b和c参数感到满意(将默认值传递给其他参数)。

我们创建了一个APy类

class APy {
public :
void foo(int a,int b, double c) {
A->foo(a,b,c,default,default,...);
}
protected :
A * ref;
};

其优点是可以向A::foo添加新参数,更改参数顺序和方法名称,更常见的是,对于更复杂的更改,可以在不更改现有Python脚本的情况下编写原始方法的等效代码。此外,如果脚本需要一个新的参数,可以通过APy::foo中的默认参数添加它,而不必在原始的a::foo方法中这样做。

缺点是,由于有两个对象,因此处理删除很困难。当APy被销毁时,我们应该删除A吗?这取决于情况。有时A是较大对象的内部对象,不应被破坏。但是只要APy不再被Python脚本使用,它就应该被销毁。

我想知道是否有可能通过直接映射原始类和方法,但要求swig映射并简化参数列表本身,来设计一个更简单但同样方便的体系结构。

在.i文件中,是否可以要求Swig更改参数的顺序,为未公开的参数设置默认值(而不是.h默认值)?

例如,如果我有一个函数

void foo(int a, double b, int c);

我想在脚本语言中只保留a和c的相反顺序,并将b设置为10.0

所以,当我打电话给时

foo(3, 5)

它实际上会被映射到:

foo(5,10.0,3)

不管怎样,我很想听听你的经验,你认为处理这些问题的最佳方法是什么。

我认为您可以通过SWIG的%ignore%rename%extend指令来很好地解决您的问题。有关详细信息,请查阅SWIG文档。但是对于上面的例子,您可以使用以下接口文件:

%module example
%{
#define SWIG_FILE_WITH_INIT
%}
%ignore A::foo(int a, double b, int c);
%inline %{
class A {
public :
double foo(int a, double b, int c) {
return a * b - c;
}
};
%}
%extend A {
double foo(int c, int a) {
return $self->foo(a, 10.0, c);
}
};

(请注意,如果您在头文件中声明了函数,那么您当然也可以使用%{ #include "example.h" %}%include "example.h"来代替%inline %{ ... %}。)

使用这种方法,无论何时更改C++代码,都需要更新SWIG接口文件,但您应该能够自定义暴露于Python的API。

还有一个技巧你应该知道,即你可以指示%ignore匹配更多,例如所有方法或A::foo的所有版本。但是,在使用%extend之前,您需要先"unignore"它们。因此,这里有一个稍微复杂/高级的例子来展示这个(标准SWIG)技巧:

%module example
%{
#define SWIG_FILE_WITH_INIT
%}
%ignore A::foo;
%inline %{
class A {
public :
double foo(int a, double b, int c) {
return a * b - c;
}
double foo(int a, double b) {
return a * b;
}
};
%}
%rename("%s") A::foo;
%extend A {
int foo(int c, int a) {
return $self->foo(a, 10.0, c);
}
};

同样,请查看SWIG文档以了解确切的详细信息。