SWIG操作法转换Wrapping overloaded operators - 小众知识

SWIG操作法转换Wrapping overloaded operators

2013年01月27日 14:18:05 苏内容
  标签: SWIG


C++ overloaded operator declarations can be wrapped. For example, consider a class like this:

class Complex {
  double rpart, ipart;
  Complex(double r = 0, double i = 0) : rpart(r), ipart(i) { }
  Complex(const Complex &c) : rpart(c.rpart), ipart(c.ipart) { }
  Complex &operator=(const Complex &c) {
    rpart = c.rpart;
    ipart = c.ipart;
    return *this;
  Complex operator+(const Complex &c) const {
    return Complex(rpart+c.rpart, ipart+c.ipart);
  Complex operator-(const Complex &c) const {
    return Complex(rpart-c.rpart, ipart-c.ipart);
  Complex operator*(const Complex &c) const {
    return Complex(rpart*c.rpart - ipart*c.ipart,
		   rpart*c.ipart + c.rpart*ipart);
  Complex operator-() const {
    return Complex(-rpart, -ipart);
  double re() const { return rpart; }
  double im() const { return ipart; }

When operator declarations appear, they are handled in exactly the same manner as regular methods. However, the names of these methods are set to strings like "operator +" or "operator -". The problem with these names is that they are illegal identifiers in most scripting languages. For instance, you can't just create a method called "operator +" in Python--there won't be any way to call it.

Some language modules already know how to automatically handle certain operators (mapping them into operators in the target language). However, the underlying implementation of this is really managed in a very general way using the %rename directive. For example, in Python a declaration similar to this is used:

%rename(__add__) Complex::operator+;

This binds the + operator to a method called __add__ (which is conveniently the same name used to implement the Python + operator). Internally, the generated wrapper code for a wrapped operator will look something like this pseudocode:

_wrap_Complex___add__(args) {
   ... get args ...

When used in the target language, it may now be possible to use the overloaded operator normally. For example:

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

It is important to realize that there is nothing magical happening here. The %rename directive really only picks a valid method name. If you wrote this:

%rename(add) operator+;

The resulting scripting interface might work like this:

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

All of the techniques described to deal with overloaded functions also apply to operators. For example:

%ignore Complex::operator=;             // Ignore = in class Complex
%ignore *::operator=;                   // Ignore = in all classes
%ignore operator=;                      // Ignore = everywhere.

%rename(__sub__) Complex::operator-; 
%rename(__neg__) Complex::operator-();  // Unary - 

The last part of this example illustrates how multiple definitions of the operator- method might be handled.

Handling operators in this manner is mostly straightforward. However, there are a few subtle issues to keep in mind:

  • In C++, it is fairly common to define different versions of the operators to account for different types. For example, a class might also include a friend function like this:

    class Complex {
      friend Complex operator+(Complex &, double);
    Complex operator+(Complex &, double);

    SWIG simply ignores all friend declarations. Furthermore, it doesn't know how to associate the associated operator+ with the class (because it's not a member of the class).

    It's still possible to make a wrapper for this operator, but you'll have to handle it like a normal function. For example:

    %rename(add_complex_double) operator+(Complex &, double);
  • Certain operators are ignored by default. For instance, new and delete operators are ignored as well as conversion operators.

  • The semantics of certain C++ operators may not match those in the target language.

© CopyRight 2010-2021, PREDREAM.ORG, Inc.All Rights Reserved. 京ICP备13045924号-1