阿酷尔工作室

V1

2022/08/10阅读:31主题:默认主题

SWIG教程-使用SWIG对C++的封装

使用SWIG对C++的封装

目前支持的C++特性

  • Classes

  • Constructors and destructors

  • Virtual functions

  • Public inheritance (including multiple inheritance)

  • Static functions

  • Function and method overloading

  • Operator overloading for many standard operators

  • References

  • Templates (including specialization and member templates)

  • Pointers to members

  • Namespaces

  • Default parameters

  • Smart pointers

目前还不支持的特性

  • Overloaded versions of certain operators (new, delete, etc.)

作为一条铁律,最好不要将swig用到C++源文件上,只用swig来处理C++头文件

封装时的默认行为

  • If a C++ class does not declare any explicit constructor, SWIG will automatically generate a wrapper for one.

  • If a C++ class does not declare an explicit copy constructor, SWIG will automatically generate a wrapper for one if the %copyctor is used.

  • If a C++ class does not declare an explicit destructor, SWIG will automatically generate a wrapper for one

  • A default constructor is not created if a class already defines a constructor with arguments.

  • Default constructors are not generated for classes with pure virtual methods or for classes that inherit from an abstract class, but don't provide definitions for all of the pure methods.

  • A default constructor is not created unless all base classes support a default constructor.

  • Default constructors and implicit destructors are not created if a class defines them in a private or protected section.

  • Default constructors and implicit destructors are not created if any base class defines a non-public default constructor or destructor.

除了以上条件会影响构造函数和析枸函数的执行,还可以人为的使用指令来控制构造函数和析枸函数的封装

%nodefaultctor Foo; // Disable the default constructor for class Foo.
class Foo { // No default constructor is generated, unless one is declared
...
};
class Bar { // A default constructor is generated, if possible
...
};
%nodefaultdtor Foo; // Disable the implicit/default destructor for class Foo.
class Foo { // No destructor is generated, unless one is declared
...
};

如果嫌弃一个一个的指定麻烦还可以全局的进行限定

%nodefaultctor; // Disable creation of default constructors
class Foo { // No default constructor is generated, unless one is declared
...
};
class Bar {
public:
Bar(); // The default constructor is generated, since one is declared
};
%clearnodefaultctor; // Enable the creation of default constructors again

如下函数不会进行封装

当swig进行封装之后可能会出错时, swig将不会对其进行封装

比如说:当一个构造函数被声明为保护类型属性时,swig将不会对其进行封装

class Foo {
protected:
Foo(); // Not wrapped.
public:
...
};

只读变量

使用immutable对只读变量进行标记

class List {
public:
...
%immutable;
int length;
%mutable;
...
};

当然如果嫌麻烦可以使用更加明确的指定方式

%immutable List::length;
...
class List {
...
int length; // Immutable by above directive
...
};

使用原始值(引用)

The %naturalvar directive is a macro for, and hence equivalent to, %feature("naturalvar"). It can be used as follows:

// All List variables will use const List& typemaps
%naturalvar List;
// Only Foo::myList will use const List& typemaps
%naturalvar Foo::myList;
struct Foo {
List myList;
};
// All non-primitive types will use const reference typemaps
%naturalvar;

It is generally a good idea to use this feature globally as the reference typemaps have extra NULL checking compared to the pointer typemaps. A pointer can be NULL, whereas a reference

cannot,

The naturalvar behavior can also be turned on as a global setting via the -naturalvar commandline option or the module mode option, %module(naturalvar=1). However, any use of

%feature("naturalvar") will override the global setting.

默认参数封装方式

当有默认参数时,swig会为每个参数都新生成一个封装函数,如果需要封装的类中存在大量的默认参数,这样会导致封装之后的包非常的大,可以使用compactdefaultargs功能标记,添加之后该接口的所有默认参数处理会别合并到一起进行处理,能有效的缩小封装之后包的大小。

%feature("compactdefaultargs") Foo::bar;
class Foo {
public:
    void bar(int x, int y = 3int z = 4);
};

模板类的封装

因为模板类只有在C++编译的时候才会展开,所以仅仅依照源码swig并不能得知要封装那种类型的的模板类,因此必须指定T类型之后才能对其进行封装。这个时候就可以借助rename来指定T了,具体使用方式如下:

将模板类具体化之后再通过rename修改别名,这个时候swig就能按照具体类进行封装了

%rename(intList) List<int>; // Rename to a suitable identifier
class List<int> {
private:
    int *data;
    int nitems;
    int maxitems;
public:
    List(int max);
    ~List();
    void append(int obj);
    int length();
    int get(int n);
};

同时swig可以支持直接对模板进行操作,这时就需要借助%template指令了

需要注意这时的template有顺序要求,模板必须在指令之前才行,否则就会报错退出

template<class Tclass List { ... };
%template(intList) List<int>;
List<Integer> = List<int>
%template(intList) List<int>;
typedef int Integer;
...
void foo(List<Integer> *x);

类型支持使用typedef重定义,但是模板绝对不允许,如果使用typedef重定义模板会导致出错

typedef List<int> ListOfInt;
%template(intList) List<int>; // ok
%template(intList) ListOfInt; // illegal - Syntax error

模板函数的封装

同样可以使用template指令来处理模板函数

// Function template
template<class T> T max(T a, T b) return a > b ? a : b; }
// Make some different versions of this function
%template(maxint) max<int>;
%template(maxdouble) max<double>;
// 同样支持foo的模板重载
template<class T> void foo(T x) { };
template<class T> void foo(T x, T y) { };
%template(foo) foo<int>;

默认参数的函数模板

template <typename T, int max=100>
class ector {

};

%template(intvec) ector<int>; // OK
%template(vec1000) ector<int1000>; // OK

当函数是类的成员函数时,这时需要在类内进行模板封装

class Foo {
public:
template<class T> void bar(T x, T y) { ... };
...
%template(barint) bar<int>;
%template(bardouble) bar<double>;
};

如果类已经定义过了只能通过extend来进行扩展了

class Foo {
public:
template<class T> void bar(T x, T y) { ... };
...
};
...
%extend Foo {
%template(barint) bar<int>;
%template(bardouble) bar<double>;
};

或者使用C++的方式简化扩展的方式

class Foo {
public:
template<class T> void bar(T x, T y) { ... };
...
};
...
%template(bari) Foo::bar<int>;
%template(bard) Foo::bar<double>;

命名空间

命名空间作为C++的一项基础功能,在swig支持的很好,但是如果不使用指令控制swig遇到一些比较棘手的问题还是会直接报错,比如默认情况下两个命名空间中有相同的类时,swig将无法区分两个类,因为swig的默认处理是将所有的命名空间的种类型都完全暴露出来,因为目标语言中可能没有明明空间这个概念(go语言也是后期才添加上的)

类重命名

%feature("nspace") MyWorld::Material::Future;
// %nspace MyWorld::Wrapping::Future; // %nspace is a macro for %feature("nspace")
// 在go语言中多个命名空间类重名,目前版本只能通过重命名来进行使用
%rename("Future1") MyWorld::Wrapping::Future;
namespace MyWorld {
    namespace Material {
        class Future {
        };
    }
    namespace Wrapping {
        class Future {
        };
    }
}

模板重命名

namespace Space {
    %rename(bbb) ABC::aaa(T t); // 能够匹配但是优先级低于 ccc
    %rename(ccc) ABC<Space::XYZ>::aaa(Space::XYZ t);// 能够匹配优先级高于 bbb
    %rename(ddd) ABC<Space::XYZ>::aaa(XYZ t); // will not match
}

namespace Space {
    class XYZ {};
    template<typename T> struct ABC {
        void aaa(T t) {}
    };
}

%template(ABCXYZ) Space::ABC<Space::XYZ>;

异常处理

异常处理需要借助catches指令,该指令后面需要跟需要捕获的错误列表;

%catches(Error1, Error2, ...) Foo::bar(); 说明需要捕获所有的异常,...代表说明要捕获任何其他可能的异常

struct EBase { virtual ~EBase(); };
struct Error1 : EBase { };
struct Error2 : EBase { };
struct Error3 : EBase { };
struct Error4 : EBase { };
%catches(Error1, Error2, ...) Foo::bar();
%catches(EBase) Foo::blah();

class Foo {
public:
    ...
    void bar();
    void blah() throw(Error1, Error2, Error3, Error4);
    ...
};

回调和代理

swig起初只是为了能够在目标语言中去调用C/C++语言,代理使得在C或者C++中反向调用目标语言也成为可能

SWIG's primary goal is to make it possible to call C/C++ code from a target language, however, the director feature enables the reverse. While there isn't simple direct support for calling target

language code from C, the director feature makes this possible.

通过设置directors可以让swig实现对回调函数的封装,设置之后swig会在对应的目标语言中生成目标语言的回调函数封装

%feature("director") BinaryOp;
%inline %{
struct BinaryOp {
virtual int handle(int a, int b) 0;
virtual ~BinaryOp() {}
};
%}

目前支持的特性于限制

  • 支持初始化列表

  • 支持部分模板

  • 支持decltype类型推断,但是只是单个变量的推断,不支持对表达式的推断

  • 支持部分lambda表达式,但是并不完全

  • 支持新的函数定义形式

struct SomeStruct {
auto FuncName(int x, int y) -> int;
};
auto square(float a, float b) -> decltype(a);
  • 初始化列表

  • 增强型枚举类型

enum class MyEnum : unsigned int;

copy构造函数

功能标记是swig用来控制一些数据生成特性的标记。

比如正常情况下siwg是不会生成copy构造函数的,但是如果你想生成copy构造函数可以通过feature进行指定

%copyctor List;
class List {
    public:
    List();
};

这个功能是1.32版本之后才添加上的,因此在1.32版本之前如果想生成copy构造函数的封装只能手动进行重命名才能实现

class Foo {
public:
Foo();
%name(CopyFoo) Foo(const Foo &);
...
};

分类:

后端

标签:

后端

作者介绍

阿酷尔工作室
V1

恒生研究院