• 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
    ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    %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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如下函数不会进行封装

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

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

    class Foo {
    protected:
    Foo(); // Not wrapped.
    public:
    ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    只读变量

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

    class List {
    public:
    ...
    %immutable;
    int length;
    %mutable;
    ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    %immutable List::length;
    ...
    class List {
    ...
    int length; // Immutable by above directive
    ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用原始值(引用)

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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 = 3, int z = 4);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    模板类的封装

    因为模板类只有在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);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

    template<class T> class List { ... };
    %template(intList) List<int>;
    List<Integer> = List<int>
    %template(intList) List<int>;
    typedef int Integer;
    ...
    void foo(List<Integer> *x);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

    模板函数的封装

    同样可以使用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>;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    默认参数的函数模板

    template <typename T, int max=100>
    class ector {
    
    };
    
    %template(intvec) ector<int>; // OK
    %template(vec1000) ector<int, 1000>; // OK
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    class Foo {
    public:
    template<class T> void bar(T x, T y) { ... };
    ...
    %template(barint) bar<int>;
    %template(bardouble) bar<double>;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    class Foo {
    public:
    template<class T> void bar(T x, T y) { ... };
    ...
    };
    ...
    %extend Foo {
    %template(barint) bar<int>;
    %template(bardouble) bar<double>;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    class Foo {
    public:
    template<class T> void bar(T x, T y) { ... };
    ...
    };
    ...
    %template(bari) Foo::bar<int>;
    %template(bard) Foo::bar<double>;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    命名空间

    命名空间作为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 {
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    模板重命名

    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>;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    异常处理

    异常处理需要借助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);
        ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    回调和代理

    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() {}
    };
    %}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    目前支持的特性于限制

    • 支持初始化列表

    • 支持部分模板

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

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

    • 支持新的函数定义形式

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

    • 增强型枚举类型

    enum class MyEnum : unsigned int;
    
    • 1

    copy构造函数

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

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

    %copyctor List;
    class List {
        public:
        List();
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    class Foo {
    public:
    Foo();
    %name(CopyFoo) Foo(const Foo &);
    ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    巡检运维的UI设计,一目了然,方便快捷是核心诉求
    ORACLE 特殊日期时间转换,计算
    break、continue、return中选择一个,我们结束掉它
    sol2 配置到centos
    [Mamba_4]LMa-UNet
    工艺路线、工序、工位、工步的定义
    在Qt设计师(Qt Designer )控件面板加入自定义控件
    sh、bash 和 dash 几种 shell 的区别是什么?
    基于webapi的websocket聊天室(番外二)
    Vue路由与nodejs下载安装及环境变量的配置
  • 原文地址:https://blog.csdn.net/andrewgithub/article/details/126261885