6.16 包装重载操作符

SWIG可以包装C++重载操作符。例如,考虑如下的类:

  1. class Complex {
  2. private:
  3. double rpart, ipart;
  4. public:
  5. Complex(double r = 0, double i = 0) : rpart(r), ipart(i){ }
  6. Complex(const Complex &c) : rpart(c.rpart), ipart(c.ipart) { }
  7. Complex &operator=(const Complex &c) {
  8. rpart = c.rpart;
  9. ipart = c.ipart;
  10. return *this;
  11. }
  12. Complex operator+(const Complex &c) const {
  13. return Complex(rpart+c.rpart, ipart+c.ipart);
  14. }
  15. Complex operator-(const Complex &c) const {
  16. return Complex(rpart-c.rpart, ipart-c.ipart);
  17. }
  18. Complex operator*(const Complex &c) const {
  19. return Complex(rpart*c.rpart - ipart*c.ipart,rpart*c.ipart + c.rpart*ipart);
  20. }
  21. Complex operator-() const {
  22. return Complex(-rpart, -ipart);
  23. }
  24. double re() const { return rpart; }
  25. double im() const { return ipart; }
  26. };

当出现操作符时,处理他们的方式与处理常规方法一致。但,这些方法的名字被设置成类似operator +operator -的形式。这些名字的问题是,在脚本语言中不合法。比如,不能再Python中调用operator +

有些语言模块知道如何自动处理某些操作符(将它们映射到目标语言的操作符)。但是,底层的实现使用非常通用的方法,利用%rename指令。例如,在Python中:

  1. %rename(__add__) Complex::operator+;

将+操作符绑定到__add__方法(Python中实现+操作符的惯用方法)。在内部,包装操作符的包装代码使用如下的类似代码:

  1. _wrap_Complex___add__(args) {
  2. ... get args ...
  3. obj->operator+(args);
  4. ...
  5. }

当在目标语言中使用时,可以像常规方式一样使用重载操作符。比如:

  1. >>> a = Complex(3,4)
  2. >>> b = Complex(5,2)
  3. >>> c = a + b # Invokes __add__ method

这里没有奇迹发生。只是使用%rename指令,选择了一个有效的名字而已。如果你写成这样:

  1. %rename(add) operator+;

在脚本中就需要这样调用接口:

  1. a = Complex(3,4)
  2. b = Complex(5,2)
  3. c = a.add(b) # Call a.operator+(b)

前面介绍的用于重载函数的技术同样适用于操作符。例如:

  1. %ignore Complex::operator=; // Ignore = in class Complex
  2. %ignore *::operator=; // Ignore = in all classes
  3. %ignore operator=; // Ignore = everywhere.
  4. %rename(__sub__) Complex::operator-;
  5. %rename(__neg__) Complex::operator-(); // Unary -

这个例子的后面演示了如何处理对operator-方法的多个定义。

使用这种方式处理重载操作符多数情况下都很直接。但是,有一些小问题需要记住:

  • 在C++中,为不同类型定义不同版本的操作符是很普遍的。例如,类可能还包含下面这样的友元函数:

    1. class Complex {
    2. public:
    3. friend Complex operator+(Complex &, double);
    4. };
    5. Complex operator+(Complex &, double);

    SWIG会简单忽略所有的友元声明。因此,就不知道如何在类中关联operator+了(因为它不是类的成员)。

    任然可能对这个操作符进行包装,但是不得不把它当做普通函数进行处理。例如:

    1. %rename(add_complex_double) operator+(Complex &, double);
  • 某些操作符默认情况下是被忽略的。比如,newdelete操作符都被忽略了。

  • 某些操作符的语义在目标语言中并没有匹配的表示方法。