1. 程式人生 > 實用技巧 >SWIG 3 中文手冊——13. 約定

SWIG 3 中文手冊——13. 約定

目錄

13 約定

A common problem that arises when wrapping C libraries is that of maintaining reliability and checking for errors. The fact of the matter is that many C programs are notorious for not providing error checks. Not only that, when you expose the internals of an application as a library, it often becomes possible to crash it simply by providing bad inputs or using it in a way that wasn't intended.

This chapter describes SWIG's support for software contracts. In the context of SWIG, a contract can be viewed as a runtime constraint that is attached to a declaration. For example, you can easily attach argument checking rules, check the output values of a function and more. When one of the rules is violated by a script, a runtime exception is generated rather than having the program continue to execute.

包裝 C 庫時出現的常見問題是維護可靠性和檢查錯誤。事實是,許多 C 程式因不提供錯誤檢查而臭名昭著。不僅如此,當你將應用程式的內部結構公開為一個庫時,通常可能會因提供錯誤的輸入或以非預期的方式使用它而使其崩潰。

本章介紹 SWIG 對軟體約定的支援。在 SWIG 的上下文中,約定可以視為附加到宣告的執行時約束。例如,你可以輕鬆地附加引數檢查規則,檢查函式的輸出值等等。如果指令碼違反了其中一個規則,則會生成執行時異常,而不是讓程式繼續執行。

13.1 %contract 指令

Contracts are added to a declaration using the %contract directive. Here is a simple example:

使用 %contract 指令將約定新增到宣告中。這是一個簡單的示例:

%contract sqrt(double x) {
require:
  x >= 0;
ensure:
  sqrt >= 0;
}

...
double sqrt(double);

In this case, a contract is being added to the sqrt() function. The %contract directive must always appear before the declaration in question. Within the contract there are two sections, both of which are optional. The require: section specifies conditions that must hold before the function is called. Typically, this is used to check argument values. The ensure: section specifies conditions that must hold after the function is called. This is often used to check return values or the state of the program. In both cases, the conditions that must hold must be specified as boolean expressions.

In the above example, we're simply making sure that sqrt() returns a non-negative number (if it didn't, then it would be broken in some way).

Once a contract has been specified, it modifies the behavior of the resulting module. For example:

在這種情況下,將約定新增到 sqrt() 函式中。%contract 指令必須始終出現在相關宣告之前。約定中有兩個部分,兩者都是可選的。require: 部分指定了在呼叫函式之前必須滿足的條件。通常,它用於檢查引數值。ensure: 部分指定了在呼叫函式後必須滿足的條件。通常用於檢查返回值或程式狀態。在這兩種情況下,必須滿足的條件都必須指定為布林表示式。

在上面的示例中,我們只是確保 sqrt() 返回一個非負數(如果不返回,那麼它將以某種方式被破壞)。

指定約定後,它將修改結果模組的行為。例如:

>>> example.sqrt(2)
1.4142135623730951
>>> example.sqrt(-2)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
RuntimeError: Contract violation: require: (arg1>=0)
>>>

13.2 %contract 與類

The %contract directive can also be applied to class methods and constructors. For example:

%contract 指令也可以應用於類方法和建構函式。例如:

%contract Foo::bar(int x, int y) {
require:
  x > 0;
ensure:
  bar > 0;
}

%contract Foo::Foo(int a) {
require:
  a > 0;
}

class Foo {
public:
  Foo(int);
  int bar(int, int);
};

The way in which %contract is applied is exactly the same as the %feature directive. Thus, any contract that you specified for a base class will also be attached to inherited methods. For example:

%contract 的應用方式與 %feature 指令完全相同。因此,你為基類指定的任何約定也將附加到繼承的方法。例如:

class Spam : public Foo {
public:
  int bar(int, int);    // Gets contract defined for Foo::bar(int, int)
};

In addition to this, separate contracts can be applied to both the base class and a derived class. For example:

除此之外,可以將單獨的約定同時應用於基類和派生類。例如:

%contract Foo::bar(int x, int) {
require:
  x > 0;
}

%contract Spam::bar(int, int y) {
require:
  y > 0;
}

class Foo {
public:
  int bar(int, int);   // Gets Foo::bar contract.
};

class Spam : public Foo {
public:
  int bar(int, int);   // Gets Foo::bar and Spam::bar contract
};

When more than one contract is applied, the conditions specified in a "require:" section are combined together using a logical-AND operation. In other words conditions specified for the base class and conditions specified for the derived class all must hold. In the above example, this means that both the arguments to Spam::bar must be positive.

當應用多個約定時,使用邏輯與運算將 require: 部分中指定的條件組合在一起。換句話說,為基類指定的條件和為派生類指定的條件都必須成立。在上面的示例中,這意味著 Spam::bar 的兩個引數都必須為正。

13.3 約定整合與 %aggregate_check

Consider an interface file that contains the following code:

考慮包含以下程式碼的介面檔案:

#define  UP     1
#define  DOWN   2
#define  RIGHT  3
#define  LEFT   4

void move(SomeObject *, int direction, int distance);

One thing you might want to do is impose a constraint on the direction parameter to make sure it's one of a few accepted values. To do that, SWIG provides an easy to use macro %aggregate_check() that works like this:

你可能想做的一件事是對 direction 引數施加約束,以確保它是少數幾個可接受的值之一。為此,SWIG 提供了一個易於使用的巨集 %aggregate_check(),其工作方式如下:

%aggregate_check(int, check_direction, UP, DOWN, LEFT, RIGHT);

This merely defines a utility function of the form

只是定義一個工具函式

int check_direction(int x);

That checks the argument x to see if it is one of the values listed. This utility function can be used in contracts. For example:

這將檢查引數 x 以檢視其是否為列出的值之一。可以在約定中使用此實用程式函式。例如:

%aggregate_check(int, check_direction, UP, DOWN, RIGHT, LEFT);

%contract move(SomeObject *, int direction, in) {
require:
  check_direction(direction);
}

#define  UP     1
#define  DOWN   2
#define  RIGHT  3
#define  LEFT   4

void move(SomeObject *, int direction, int distance);

Alternatively, it can be used in typemaps and other directives. For example:

另外,它可以在型別對映和其他指令中使用。例如:

%aggregate_check(int, check_direction, UP, DOWN, RIGHT, LEFT);

%typemap(check) int direction {
  if (!check_direction($1)) SWIG_exception(SWIG_ValueError, "Bad direction");
}

#define  UP     1
#define  DOWN   2
#define  RIGHT  3
#define  LEFT   4

void move(SomeObject *, int direction, int distance);

Regrettably, there is no automatic way to perform similar checks with enums values. Maybe in a future release.

遺憾的是,沒有自動的方法可以對列舉值執行類似的檢查。也許在將來的版本中可以。

13.4 注意事項

Contract support was implemented by Songyan (Tiger) Feng and first appeared in SWIG-1.3.20.

約定支援由 Songyan(Tiger)Feng 實施,並首次出現在 SWIG-1.3.20 中。