SWIG 3 中文手冊——11. 類型映射

来源:https://www.cnblogs.com/xuruilong100/archive/2020/06/07/13062648.html
-Advertisement-
Play Games

11 類型映射 11.1 引言 Chances are, you are reading this chapter for one of two reasons; you either want to customize SWIG's behavior or you overheard someon ...


目錄

11 類型映射

11.1 引言

Chances are, you are reading this chapter for one of two reasons; you either want to customize SWIG's behavior or you overheard someone mumbling some incomprehensible drivel about "typemaps" and you asked yourself "typemaps, what are those?" That said, let's start with a short disclaimer that "typemaps" are an advanced customization feature that provide direct access to SWIG's low-level code generator. Not only that, they are an integral part of the SWIG C++ type system (a non-trivial topic of its own). Typemaps are generally not a required part of using SWIG. Therefore, you might want to re-read the earlier chapters if you have found your way to this chapter with only a vague idea of what SWIG already does by default.

你正在閱讀本章的原因可能有兩個:你想自定義 SWIG 的行為,或是無意中聽到有人抱怨“typemaps”一詞,並問自己“typemaps 是什麼?”。那麼,讓我們從一個簡短的免責聲明開始,即“typemaps”是一種高級定製功能,可以直接訪問 SWIG 的低級代碼生成器。不僅如此,它們還是 SWIG C++ 類型系統(SWIG 自身的重要內容)的組成部分。通常,不是使用 SWIG 的必需部分。因此,如果你閱讀本章時對 SWIG 預設情況下的行為認識模糊,那麼你可能想重新閱讀前面的章節。

11.1.1 類型轉換

One of the most important problems in wrapper code generation is the conversion or marshalling of datatypes between programming languages. Specifically, for every C/C++ declaration, SWIG must somehow generate wrapper code that allows values to be passed back and forth between languages. Since every programming language represents data differently, this is not a simple of matter of simply linking code together with the C linker. Instead, SWIG has to know something about how data is represented in each language and how it can be manipulated.

To illustrate, suppose you had a simple C function like this:

包裝器代碼生成中最重要的問題之一是編程語言之間數據類型的轉換或編組。具體來說,對於每個 C/C++ 聲明,SWIG 必須以某種方式生成包裝器代碼,該包裝器代碼允許在語言之間來回傳遞值。由於每種編程語言表示數據的方式都不相同,因此簡單地將代碼與 C 鏈接器鏈接在一起並不是一件容易的事。相反,SWIG 必須瞭解每種語言如何表示數據,以及如何對其進行操縱的知識。

為了說明這一點,假設你有一個簡單的 C 函數,如下所示:

int factorial(int n);

To access this function from Python, a pair of Python API functions are used to convert integer values. For example:

要從 Python 訪問此函數,要用大一對 Python API 函數轉換整數值。例如:

long PyInt_AsLong(PyObject *obj);      /* Python --> C */
PyObject *PyInt_FromLong(long x);      /* C --> Python */

The first function is used to convert the input argument from a Python integer object to C long. The second function is used to convert a value from C back into a Python integer object.

Inside the wrapper function, you might see these functions used like this:

第一個函數用於將輸入參數從 Python 整數對象轉換為 C 中的 long。第二個函數用於將值從 C 轉換回 Python 整數對象。

在包裝器函數中,你可能會看到這些函數的用法如下:

PyObject *wrap_factorial(PyObject *self, PyObject *args) {
  int       arg1;
  int       result;
  PyObject *obj1;
  PyObject *resultobj;

  if (!PyArg_ParseTuple("O:factorial", &obj1)) return NULL;
  arg1 = PyInt_AsLong(obj1);
  result = factorial(arg1);
  resultobj = PyInt_FromLong(result);
  return resultobj;
}

Every target language supported by SWIG has functions that work in a similar manner. For example, in Perl, the following functions are used:

SWIG 支持的每種目標語言都具有以類似方式工作的函數。例如,在 Perl 中,使用以下函數:

IV SvIV(SV *sv);                     /* Perl --> C */
void sv_setiv(SV *sv, IV val);       /* C --> Perl */

In Tcl:

在 Tcl 中:

int Tcl_GetLongFromObj(Tcl_Interp *interp, Tcl_Obj *obj, long *value);
Tcl_Obj *Tcl_NewIntObj(long value);

The precise details are not so important. What is important is that all of the underlying type conversion is handled by collections of utility functions and short bits of C code like this--you simply have to read the extension documentation for your favorite language to know how it works (an exercise left to the reader).

確切的細節不是那麼重要。重要的是,所有底層類型轉換都由實用程式函數和類似這樣的 C 代碼的短代碼集合處理——你只需閱讀自己喜歡的語言的擴展文檔以瞭解其工作原理(一個留給讀者的練習)。

11.1.2 類型映射

Since type handling is so central to wrapper code generation, SWIG allows it to be completely defined (or redefined) by the user. To do this, a special %typemap directive is used. For example:

由於類型處理對於包裝器代碼生成非常重要,因此 SWIG 允許用戶完全自定義(或重新定義)它。為此,使用特殊的 %typemap 指令。例如:

/* Convert from Python --> C */
%typemap(in) int {
  $1 = PyInt_AsLong($input);
}

/* Convert from C --> Python */
%typemap(out) int {
  $result = PyInt_FromLong($1);
}

At first glance, this code will look a little confusing. However, there is really not much to it. The first typemap (the "in" typemap) is used to convert a value from the target language to C. The second typemap (the "out" typemap) is used to convert in the other direction. The content of each typemap is a small fragment of code that is inserted directly into the SWIG generated wrapper functions. The code is usually C or C++ code which will be generated into the C/C++ wrapper functions. Note that this isn't always the case as some target language modules allow target language code within the typemaps which gets generated into target language specific files. Within this code, a number of special variables prefixed with a $ are expanded. These are really just placeholders for C/C++ variables that are generated in the course of creating the wrapper function. In this case, $input refers to an input object that needs to be converted to C/C++ and $result refers to an object that is going to be returned by a wrapper function. $1 refers to a C/C++ variable that has the same type as specified in the typemap declaration (an int in this example).

A short example might make this a little more clear. If you were wrapping a function like this:

乍看之下,這段代碼看起來有些混亂。但是,實際上並沒有太多。第一個類型映射(in 類型映射)用於將值從目標語言轉換為 C。第二個類型映射(out 類型映射)用於向另一個方向轉換。每個類型映射的內容都是一小段代碼,直接插入 SWIG 生成的包裝器函數中。該代碼通常是 C 或 C++ 代碼,它們將生成到 C/C++ 包裝器函數中。請註意,並非總是如此,因為某些目標語言模塊允許類型映射中的目標語言代碼生成到目標語言特定的文件中。在此代碼中,將擴展許多帶有 $ 首碼的特殊變數。這些實際上只是 C/C++ 變數的占位符,這些變數是在創建包裝器函數的過程中生成的。在這種情況下,$input 是指需要轉換為 C/C++ 的輸入對象,而 $result 是指將由包裝器函數返回的對象。$1 指的是一個 C/C++ 變數,其類型與類型映射聲明中指定的類型相同(本例中為 int)。

一個簡短的示例可能會使這一點更加清楚。如果要包裝這樣的函數:

int gcd(int x, int y);

A wrapper function would look approximately like this:

包裝器函數大致如下所示:

PyObject *wrap_gcd(PyObject *self, PyObject *args) {
  int arg1;
  int arg2;
  int result;
  PyObject *obj1;
  PyObject *obj2;
  PyObject *resultobj;

  if (!PyArg_ParseTuple("OO:gcd", &obj1, &obj2)) return NULL;

  /* "in" typemap, argument 1 */
  {
    arg1 = PyInt_AsLong(obj1);
  }

  /* "in" typemap, argument 2 */
  {
    arg2 = PyInt_AsLong(obj2);
  }

  result = gcd(arg1, arg2);

  /* "out" typemap, return value */
  {
    resultobj = PyInt_FromLong(result);
  }

  return resultobj;
}

In this code, you can see how the typemap code has been inserted into the function. You can also see how the special $ variables have been expanded to match certain variable names inside the wrapper function. This is really the whole idea behind typemaps--they simply let you insert arbitrary code into different parts of the generated wrapper functions. Because arbitrary code can be inserted, it possible to completely change the way in which values are converted.

在此代碼中,你可以看到如何將類型映射代碼插入到函數中。你還可以看到特殊的 $ 變數是如何擴展的,以匹配包裝器函數中的某些變數名稱。這實際上就是類型映射背後的全部思想,它們只是讓你將任意代碼插入生成的包裝器函數的不同部分。由於可以插入任意代碼,因此可以完全改變值轉換的方式。

11.1.3 模式匹配

As the name implies, the purpose of a typemap is to "map" C datatypes to types in the target language. Once a typemap is defined for a C datatype, it is applied to all future occurrences of that type in the input file. For example:

顧名思義,類型映射的目的是將 C 數據類型“映射”為目標語言中的類型。一旦為 C 數據類型定義類型映射,它將應用於輸入文件中出現的所有該類型。例如:

/* Convert from Perl --> C */
%typemap(in) int {
  $1 = SvIV($input);
}

...
int factorial(int n);
int gcd(int x, int y);
int count(char *s, char *t, int max);

The matching of typemaps to C datatypes is more than a simple textual match. In fact, typemaps are fully built into the underlying type system. Therefore, typemaps are unaffected by typedef, namespaces, and other declarations that might hide the underlying type. For example, you could have code like this:

類型映射與 C 數據類型的匹配不僅僅是簡單的文本匹配。實際上,類型映射完全內置在基礎類型系統中。因此,類型映射不受 typedef、命名空間和其他可能隱藏基礎類型的聲明的影響。例如,你可能具有以下代碼:

/* Convert from Ruby--> C */
%typemap(in) int {
  $1 = NUM2INT($input);
}
...
typedef int Integer;
namespace foo {
  typedef Integer Number;
};

int foo(int x);
int bar(Integer y);
int spam(foo::Number a, foo::Number b);

In this case, the typemap is still applied to the proper arguments even though typenames don't always match the text "int". This ability to track types is a critical part of SWIG--in fact, all of the target language modules work merely define a set of typemaps for the basic types. Yet, it is never necessary to write new typemaps for typenames introduced by typedef.

In addition to tracking typenames, typemaps may also be specialized to match against a specific argument name. For example, you could write a typemap like this:

在這種情況下,即使類型名並不總是與文本 int 匹配,也仍然將類型映射應用於適當的參數。這種跟蹤類型的能力是 SWIG 的重要組成部分——實際上,所有目標語言模塊都只能為基本類型定義一組類型映射。但是,從來沒有必要為 typedef 引入的類型名編寫新的類型映射。

除了跟蹤類型名稱之外,類型映射還可以專門用於與特定的參數名稱匹配。例如,你可以編寫這樣的類型映射:

%typemap(in) double nonnegative {
  $1 = PyFloat_AsDouble($input);
  if ($1 < 0) {
    PyErr_SetString(PyExc_ValueError, "argument must be nonnegative.");
    SWIG_fail;
  }
}

...
double sin(double x);
double cos(double x);
double sqrt(double nonnegative);

typedef double Real;
double log(Real nonnegative);
...

For certain tasks such as input argument conversion, typemaps can be defined for sequences of consecutive arguments. For example:

對於某些任務,例如輸入參數轉換,可以為連續參數序列定義類型映射。例如:

%typemap(in) (char *str, int len) {
  $1 = PyString_AsString($input);   /* char *str */
  $2 = PyString_Size($input);       /* int len   */
}
...
int count(char *str, int len, char c);

In this case, a single input object is expanded into a pair of C arguments. This example also provides a hint to the unusual variable naming scheme involving $1, $2, and so forth.

在這種情況下,單個輸入對象將擴展為一對 C 參數。這個例子也暗示了涉及不尋常的變數命名方案,包括 $1$2 等等。

11.1.4 復用類型映射

Typemaps are normally defined for specific type and argument name patterns. However, typemaps can also be copied and reused. One way to do this is to use assignment like this:

類型映射通常為特定的類型和參數名稱模式而定義。但是,類型映射也可以複製和重用。一種方法是使用賦值:

%typemap(in) Integer = int;
%typemap(in) (char *buffer, int size) = (char *str, int len);

A more general form of copying is found in the %apply directive like this:

%apply 指令中可以找到更通用的複製形式,如下所示:

%typemap(in) int {
  /* Convert an integer argument */
  ...
}
%typemap(out) int {
  /* Return an integer value */
  ...
}

/* Apply all of the integer typemaps to size_t */
%apply int { size_t };

%apply merely takes all of the typemaps that are defined for one type and applies them to other types. Note: you can include a comma separated set of types in the {...} part of %apply.

It should be noted that it is not necessary to copy typemaps for types that are related by typedef. For example, if you have this,

%apply 僅接受為一種類型定義的所有類型映射,並將它們應用於其他類型。註意:你可以在 %apply{...} 部分中包含一組用逗號分隔的類型。

應該註意的是,沒有必要為 typedef 相關的類型複製類型映射。例如,如果你有這個,

typedef int size_t;

then SWIG already knows that the int typemaps apply. You don't have to do anything.

那麼 SWIG 已經知道了 int 的類型映射。你不必做任何事情。

11.1.5 類型映射能幹什麼?

The primary use of typemaps is for defining wrapper generation behavior at the level of individual C/C++ datatypes. There are currently six general categories of problems that typemaps address:

類型映射的主要用途是在單一 C/C++ 數據類型級別上定義包裝器生成行為。當前,類型映射解決了六大類問題:

Argument handling

參數處理

int foo(int x, double y, char *s);
  • Input argument conversion ("in" typemap).
  • Input argument type checking for types used in overloaded methods ("typecheck" typemap).
  • Output argument handling ("argout" typemap).
  • Input argument value checking ("check" typemap).
  • Input argument initialization ("arginit" typemap).
  • Default arguments ("default" typemap).
  • Input argument resource management ("freearg" typemap).
  • 輸入參數轉換(in 類型映射)。
  • 重載方法中的輸入參數類型檢查(typecheck 類型映射)。
  • 輸出參數處理(argout 類型映射)。
  • 輸入參數值檢查(check 類型映射)。
  • 輸入參數初始化(arginit 類型映射)。
  • 預設參數(default 類型映射)。
  • 輸入參數資源管理(freearg 類型映射)。

Return value handling

返回值處理

int foo(int x, double y, char *s);
  • Function return value conversion ("out" typemap).
  • Return value resource management ("ret" typemap).
  • Resource management for newly allocated objects ("newfree" typemap).
  • 函數返回值轉換(out 類型映射)。
  • 返回值資源管理(ret 類型映射)。
  • 新分配對象的資源管理(newfree 類型映射)。

Exception handling

異常處理

int foo(int x, double y, char *s) throw(MemoryError, IndexError);
  • Handling of C++ exception specifications. ("throw" typemap).
  • 處理 C++ 異常規範。(throw 類型映射)。

Global variables

全局變數

int foo;
  • Assignment of a global variable. ("varin" typemap).
  • Reading a global variable. ("varout" typemap).
  • 分配全局變數。(varin 類型映射)。
  • 讀取全局變數。(varout 類型映射)。

Member variables

成員變數

struct Foo {
  int x[20];
};
  • Assignment of data to a class/structure member. ("memberin" typemap).
  • 將數據分配給類或結構體成員。(memberin 類型映射)。

Constant creation

創建常量

#define FOO 3
%constant int BAR = 42;
enum { ALE, LAGER, STOUT };
  • Creation of constant values. ("consttab" or "constcode" typemap).

Details of each of these typemaps will be covered shortly. Also, certain language modules may define additional typemaps that expand upon this list. For example, the Java module defines a variety of typemaps for controlling additional aspects of the Java bindings. Consult language specific documentation for further details.

  • 創建常數值。(consttabconstcode 類型映射)。

每個類型映射的詳細內容很快會提到。同樣,某些語言模塊可能會定義其他類型映射以擴展此列表。例如,Java 模塊定義了各種類型映射來控制 Java 綁定的其他方面。請查閱特定於語言的文檔以獲取更多詳細信息。

11.1.6 類型映射不能幹什麼?

Typemaps can't be used to define properties that apply to C/C++ declarations as a whole. For example, suppose you had a declaration like this,

類型映射不能用於定義整體上適用於 C/C++ 聲明的屬性。例如,假設你有一個這樣的聲明,

Foo *make_Foo(int n);

and you wanted to tell SWIG that make_Foo(int n) returned a newly allocated object (for the purposes of providing better memory management). Clearly, this property of make_Foo(int n) is not a property that would be associated with the datatype Foo * by itself. Therefore, a completely different SWIG customization mechanism (%feature) is used for this purpose. Consult the Customization Features chapter for more information about that.

Typemaps also can't be used to rearrange or transform the order of arguments. For example, if you had a function like this:

並且你想告訴 SWIG make_Foo(int n) 返回了一個新分配的對象(目的是提供更好的記憶體管理)。顯然,make_Foo(int n) 的此屬性不是本身將與數據類型 Foo * 相關聯的屬性。因此,為此目的要使用完全不同的 SWIG 定製機制(%feature)。有關更多信息,請參考自定義功能章節。

類型映射也不能用於重新排列或轉換參數的順序。例如,如果你具有如下函數:

void foo(int, char *);

you can't use typemaps to interchange the arguments, allowing you to call the function like this:

你不能使用類型映射來交換參數,進而允許你能這樣調用函數:

foo("hello", 3)          # Reversed arguments

If you want to change the calling conventions of a function, write a helper function instead. For example:

如果要更改函數的調用約定,請編寫輔助函數。例如:

%rename(foo) wrap_foo;
%inline %{
void wrap_foo(char *s, int x) {
  foo(x, s);
}
%}

11.1.7 與面向切麵編程的相似之處

SWIG has parallels to Aspect Oriented Software Development (AOP). The AOP terminology with respect to SWIG typemaps can be viewed as follows:

  • Cross-cutting concerns: The cross-cutting concerns are the modularization of the functionality that the typemaps implement, which is primarily marshalling of types from/to the target language and C/C++.
  • Advice: The typemap body contains code which is executed whenever the marshalling is required.
  • Pointcut: The pointcuts are the positions in the wrapper code that the typemap code is generated into.
  • Aspect: Aspects are the combination of the pointcut and the advice, hence each typemap is an aspect.

SWIG can also be viewed as has having a second set of aspects based around %feature. Features such as %exception are also cross-cutting concerns as they encapsulate code that can be used to add logging or exception handling to any function.

SWIG 與面向切麵的軟體開發(AOP)相似。與 SWIG 類型映射有關的 AOP 術語如下:

  • 橫切關註點:橫切關註點是類型映射所實現功能的模塊化,主要是將目標語言和 C/C++ 之間的類型進行編組。
  • 通知:類型映射主體包含在需要編組時執行的代碼。
  • 切入點:切入點是包裝器代碼中生成類型映射代碼的位置。
  • 切麵:切麵是切入點和通知的組合,因此每個類型映射都是一個切麵。

也可以將 SWIG 視為具有基於 %feature 的第二組切麵。諸如 %exception 之類的功能也是橫切關註點,因為它們封裝了可用於向任何函數添加日誌記錄或異常處理的代碼。

11.1.8 本章的剩餘部分

The rest of this chapter provides detailed information for people who want to write new typemaps. This information is of particular importance to anyone who intends to write a new SWIG target language module. Power users can also use this information to write application specific type conversion rules.

Since typemaps are strongly tied to the underlying C++ type system, subsequent sections assume that you are reasonably familiar with the basic details of values, pointers, references, arrays, type qualifiers (e.g., const), structures, namespaces, templates, and memory management in C/C++. If not, you would be well-advised to consult a copy of "The C Programming Language" by Kernighan and Ritchie or "The C++ Programming Language" by Stroustrup before going any further.

本章的剩餘部分為想要編寫新的類型映射的人提供了詳細的信息。對於打算為 SWIG 編寫新目標語言模塊的人來說,這些信息都特別重要。高級用戶還可以使用這些信息來編寫應用程式特定的類型轉換規則。

由於類型映射與底層 C++ 類型系統緊密相關,因此後續章節假定你對值、指針、引用、數組、類型限定符(例如 const)、結構體、命名空間、模板和 C/C++ 中的記憶體管理相當熟悉。如果不是這樣,建議你先閱讀 Kernighan 和 Ritchie 撰寫的《The C Programming Language》或 Stroustrup 撰寫的《The C++ Programming Language》。

11.2 類型映射詳述

This section describes the behavior of the %typemap directive itself.

本節描述了 %typemap 指令本身的行為。

11.2.1 定義一個類型映射

New typemaps are defined using the %typemap declaration. The general form of this declaration is as follows (parts enclosed in [...] are optional):

新的類型映射使用 %typemap 聲明定義。該聲明的一般形式如下([...] 中的部分是可選的):

%typemap(method [, modifiers]) typelist code ;

method is a simply a name that specifies what kind of typemap is being defined. It is usually a name like "in", "out", or "argout". The purpose of these methods is described later.

modifiers is an optional comma separated list of name="value" values. These are sometimes to attach extra information to a typemap and is often target-language dependent. They are also known as typemap attributes.

typelist is a list of the C++ type patterns that the typemap will match. The general form of this list is as follows:

method 是一個簡單的名稱,用於指定要定義的類型映射。通常,它的名稱類似於 inoutargout。這些方法的目的將在後面說明。

modifiers 是一個可選的逗號分隔列表,其中包含 name="value" 值。這些有時會在類型映射上附加額外的信息,並且通常取決於目標語言。它們也稱為類型映射屬性。

typelist 是類型映射將匹配的 C++ 類型模式的列表。此列表的一般形式如下:

typelist    :  typepattern [, typepattern, typepattern, ... ] ;

typepattern :  type [ (parms) ]
            |  type name [ (parms) ]
            |  ( typelist ) [ (parms) ]

Each type pattern is either a simple type, a simple type and argument name, or a list of types in the case of multi-argument typemaps. In addition, each type pattern can be parameterized with a list of temporary variables (parms). The purpose of these variables will be explained shortly.

code specifies the code used in the typemap. Usually this is C/C++ code, but in the statically typed target languages, such as Java and C#, this can contain target language code for certain typemaps. It can take any one of the following forms:

每個類型模式可以是簡單類型、簡單類型和參數名稱,或者在多參數類型映射下的類型列表。此外,可以使用一系列臨時變數(參數)對每個類型模式進行參數化。這些變數的目的將在稍後說明。

code 指定類型映射中使用的代碼。通常這是 C/C++ 代碼,但是在靜態類型的目標語言(例如 Java 和 C#)中,它可以包含某些類型映射的目標語言代碼。可以採用以下任何一種形式:

code       : { ... }
           | " ... "
           | %{ ... %}

Note that the preprocessor will expand code within the {} delimiters, but not in the last two styles of delimiters, see Preprocessor and Typemaps. Here are some examples of valid typemap specifications:

請註意,預處理器將在 {} 分隔符內擴展代碼,但不會在最後兩種分隔符樣式中擴展代碼,請參閱《預處理器與類型映射》章節。以下是有效類型映射規範的一些示例:

/* Simple typemap declarations */
%typemap(in) int {
  $1 = PyInt_AsLong($input);
}
%typemap(in) int "$1 = PyInt_AsLong($input);";
%typemap(in) int %{
  $1 = PyInt_AsLong($input);
%}

/* Typemap with extra argument name */
%typemap(in) int nonnegative {
  ...
}

/* Multiple types in one typemap */
%typemap(in) int, short, long {
  $1 = SvIV($input);
}

/* Typemap with modifiers */
%typemap(in, doc="integer") int "$1 = scm_to_int($input);";

/* Typemap applied to patterns of multiple arguments */
%typemap(in) (char *str, int len),
             (char *buffer, int size)
{
  $1 = PyString_AsString($input);
  $2 = PyString_Size($input);
}

/* Typemap with extra pattern parameters */
%typemap(in, numinputs=0) int *output (int temp),
                          long *output (long temp)
{
  $1 = &temp;
}

Admittedly, it's not the most readable syntax at first glance. However, the purpose of the individual pieces will become clear.

乍一看,這並不是最易讀的語法。但是,各個部分的目的將變得清楚。

11.2.2 類型映射作用範圍

Once defined, a typemap remains in effect for all of the declarations that follow. A typemap may be redefined for different sections of an input file. For example:

定義後,類型映射對於隨後出現的所有聲明都有效。可以為輸入文件的不同部分重新定義類型映射。例如:

// typemap1
%typemap(in) int {
...
}

int fact(int);                    // typemap1
int gcd(int x, int y);            // typemap1

// typemap2
%typemap(in) int {
...
}

int isprime(int);                 // typemap2

One exception to the typemap scoping rules pertains to the %extend declaration. %extend is used to attach new declarations to a class or structure definition. Because of this, all of the declarations in an %extend block are subject to the typemap rules that are in effect at the point where the class itself is defined. For example:

類型映射範圍規則的一個例外與 %extend 聲明有關。%extend 用於將新的聲明附加到類或結構體定義上。因此,%extend 塊中的所有聲明都將受到類型映射規則的約束,該規則在定義類本身時生效。例如:

class Foo {
  ...
};

%typemap(in) int {
 ...
}

%extend Foo {
  int blah(int x);    // typemap has no effect.  Declaration is attached to Foo which
                      // appears before the %typemap declaration.
};

11.2.3 複製類型映射

A typemap is copied by using assignment. For example:

使用賦值複製類型映射。例如:

%typemap(in) Integer = int;

or this:

或者這樣:

%typemap(in) Integer, Number, int32_t = int;

Types are often managed by a collection of different typemaps. For example:

類型通常由不同類型映射的集合來管理。例如:

%typemap(in)     int { ... }
%typemap(out)    int { ... }
%typemap(varin)  int { ... }
%typemap(varout) int { ... }

To copy all of these typemaps to a new type, use %apply. For example:

要將所有這些類型映射複製到一個新的類型,請使用 %apply。例如:

%apply int { Integer };            // Copy all int typemaps to Integer
%apply int { Integer, Number };    // Copy all int typemaps to both Integer and Number

The patterns for %apply follow the same rules as for %typemap. For example:

%apply 的模式遵循與 %typemap 相同的規則。例如:

%apply int *output { Integer *output };                    // Typemap with name
%apply (char *buf, int len) { (char *buffer, int size) };  // Multiple arguments

11.2.4 刪除類型映射

A typemap can be deleted by simply defining no code. For example:

不需要定義代碼即可刪除類型映射。例如:

%typemap(in) int;               // Clears typemap for int
%typemap(in) int, long, short;  // Clears typemap for int, long, short
%typemap(in) int *output;

The %clear directive clears all typemaps for a given type. For example:

%clear 指令清除給定類型的所有類型映射。例如:

%clear int;                     // Removes all types for int
%clear int *output, long *output;

Note: Since SWIG's default behavior is defined by typemaps, clearing a fundamental type like int will make that type unusable unless you also define a new set of typemaps immediately after the clear operation.

註意:由於 SWIG 的預設行為是由類型映射定義的,因此除非清除操作之後立即定義了一組新的類型映射,否則清除基本類型(如 int)將使該類型不可用。

11.2.5 類型映射的位置

Typemap declarations can be declared in the global scope, within a C++ namespace, and within a C++ class. For example:

可以在全局範圍、C++ 命名空間和 C++ 類中聲明類型映射。例如:

%typemap(in) int {
  ...
}

namespace std {
  class string;
  %typemap(in) string {
    ...
  }
}

class Bar {
public:
  typedef const int & const_reference;
  %typemap(out) const_reference {
    ...
  }
};

When a typemap appears inside a namespace or class, it stays in effect until the end of the SWIG input (just like before). However, the typemap takes the local scope into account. Therefore, this code

當類型映射出現在命名空間或類中時,它直到 SWIG 輸入文件的結束(就像之前一樣)一直有效。但是,類型映射將局部範圍考慮在內。因此,此代碼

namespace std {
  class string;
  %typemap(in) string {
    ...
  }
}

is really defining a typemap for the type std::string. You could have code like this:

確實為 std::string 類型定義了一個類型映射。你可能會有這樣的代碼:

namespace std {
  class string;
  %typemap(in) string {          /* std::string */
    ...
  }
}

namespace Foo {
  class string;
  %typemap(in) string {          /* Foo::string */
    ...
  }
}

In this case, there are two completely distinct typemaps that apply to two completely different types (std::string and Foo::string).

It should be noted that for scoping to work, SWIG has to know that string is a typename defined within a particular namespace. In this example, this is done using the forward class declaration class string.

在這種情況下,有兩個完全不同的類型映射適用於兩個完全不同的類型(std::stringFoo::string)。

應當註意,為使作用域有效,SWIG 必須知道 string 是在特定名稱空間內定義的類型名。在此示例中,這是使用正向類聲明 class string 完成的。

11.3 模式匹配規則

The section describes the pattern matching rules by which C/C++ datatypes are associated with typemaps. The matching rules can be observed in practice by using the debugging options also described.

本節描述了模式匹配規則,通過這些規則,C/C++ 數據類型與類型映射相關聯。實際中,可以通過使用調試選項來觀察匹配規則。

11.3.1 基本匹配規則

Typemaps are matched using both a type and a name (typically the name of a argument). For a given TYPE NAME pair, the following rules are applied, in order, to find a match. The first typemap found is used.

  • Typemaps that exactly match TYPE and NAME.
  • Typemaps that exactly match TYPE only.
  • If TYPE is a C++ template of type T<TPARMS>, where TPARMS are the template parameters, the type is stripped of the template parameters and the following checks are then made:
    • Typemaps that exactly match T and NAME.
    • Typemaps that exactly match T only.

If TYPE includes qualifiers (const, volatile, etc.), each qualifier is stripped one at a time to form a new stripped type and the matching rules above are repeated on the stripped type. The left-most qualifier is stripped first, resulting in the right-most (or top-level) qualifier being stripped last. For example int const*const is first stripped to int *const then int *.

If TYPE is an array. The following transformation is made:

  • Replace all dimensions to [ANY] and look for a generic array typemap.

To illustrate, suppose that you had a function like this:

使用類型和名稱(通常是參數名稱)來匹配類型映射。對於給定的 TYPE NAME 配對,將應用以下規則來查找匹配項。第一個找到的類型映射將被使用。

  • TYPENAME 完全匹配的類型映射。
  • 僅與 TYPE 完全匹配的類型映射。
  • 如果 TYPET<TPARMS> 類型的 C++ 模板,其中 TPARMS 是模板參數,則將類型的模板參數剝離,然後進行以下檢查:
    • TNAME 完全匹配的類型映射。
    • 僅與 T 完全匹配的類型映射。

如果 TYPE 包含限定符(constvolatile 等),則每次剝離一個限定符以形成新的剝離類型,併在剝離類型上重覆上述匹配規則。最左邊的限定符首先被剝離,最右邊的(或頂級)限定符最後被剝離。例如,首先將 int const * const 剝離為 int * const,然後剝離為 int *

如果 TYPE 是一個數組。進行以下轉換:

  • 將所有維度替換為 [ANY],並查找通用數組類型映射

為了說明這一點,假設你具有如下函數:

int foo(const char *s);

To find a typemap for the argument const char *s, SWIG will search for the following typemaps:

要為參數 const char *s 查找類型映射,SWIG 將搜索以下類型映射:

const char *s           Exact type and name match
const char *            Exact type match
char *s                 Type and name match (qualifier stripped)
char *                  Type match (qualifier stripped)

When more than one typemap rule might be defined, only the first match found is actually used. Here is an example that shows how some of the basic rules are applied:

當可能定義多個類型映射規則時,實際上僅使用找到的第一個匹配項。下麵是一個示例,顯示瞭如何應用一些基本規則:

%typemap(in) int *x {
  ... typemap 1
}

%typemap(in) int * {
  ... typemap 2
}

%typemap(in) const int *z {
  ... typemap 3
}

%typemap(in) int [4] {
  ... typemap 4
}

%typemap(in) int [ANY] {
  ... typemap 5
}

void A(int *x);        // int *x rule       (typemap 1)
void B(int *y);        // int * rule        (typemap 2)
void C(const int *x);  // int *x rule       (typemap 1)
void D(const int *z);  // const int *z rule (typemap 3)
void E(int x[4]);      // int [4] rule      (typemap 4)
void F(int x[1000]);   // int [ANY] rule    (typemap 5)

Compatibility note: SWIG-2.0.0 introduced stripping the qualifiers one step at a time. Prior versions stripped all qualifiers in one step.

註意相容性:SWIG-2.0.0 引入了一次刪除一個限定符。先前的版本一次就消除了所有限定符。

11.3.2 typedef 還原匹配

If no match is found using the rules in the previous section, SWIG applies a typedef reduction to the type and repeats the typemap search for the reduced type. To illustrate, suppose you had code like this:

如果使用上一節中的規則未找到匹配項,則 SWIG 將 typedef 還原,然後對還原後的類型重覆進行類型映射搜索。為了說明這一點,假設你有如下代碼:

%typemap(in) int {
  ... typemap 1
}

typedef int Integer;
void blah(Integer x);

To find the typemap for Integer x, SWIG will first search for the following typemaps:

為了找到 Integer x 的類型映射,SWIG 將首先搜索以下類型映射:

Integer x
Integer

Finding no match, it then applies a reduction Integer -> int to the type and repeats the search.

如果找不到匹配項,則對類型應用還原 Integer -> int,並重覆搜索。

int x
int      --> match: typemap 1

Even though two types might be the same via typedef, SWIG allows typemaps to be defined for each typename independently. This allows for interesting customization possibilities based solely on the typename itself. For example, you could write code like this:

即使兩個類型通過 typedef 可能是相同的,SWIG 仍允許為每個類型名分別定義類型映射。這允許僅基於類型名稱本身進行有趣的自定義。例如,你可以編寫如下代碼:

typedef double  pdouble;     // Positive double

// typemap 1
%typemap(in) double {
  ... get a double ...
}
// typemap 2
%typemap(in) pdouble {
  ... get a positive double ...
}
double sin(double x);           // typemap 1
pdouble sqrt(pdouble x);        // typemap 2

When reducing the type, only one typedef reduction is applied at a time. The search process continues to apply reductions until a match is found or until no more reductions can be made.

For complicated types, the reduction process can generate a long list of patterns. Consider the following:

還原類型時,一次僅還原一次 typedef。搜索過程將繼續應用還原直到找到匹配項,或無法再進行還原。

對於複雜類型,還原過程可以生成一長串模式。考慮以下:

typedef int Integer;
typedef Integer Row4[4];
void foo(Row4 rows[10]);

To find a match for the Row4 rows[10] argument, SWIG would check the following patterns, stopping only when it found a match:

為了找到 Row4 rows[10] 參數的匹配項,SWIG 將檢查以下模式,僅在找到匹配項時停止:

Row4 rows[10]
Row4 [10]
Row4 rows[ANY]
Row4 [ANY]

# Reduce Row4 --> Integer[4]
Integer rows[10][4]
Integer [10][4]
Integer rows[ANY][ANY]
Integer [ANY][ANY]

# Reduce Integer --> int
int rows[10][4]
int [10][4]
int rows[ANY][ANY]
int [ANY][ANY]

For parameterized types like templates, the situation is even more complicated. Suppose you had some declarations like this:

對於像模板這樣的參數化類型,情況甚至更加複雜。假設你有一些這樣的聲明:

typedef int Integer;
typedef foo<Integer, Integer> fooii;
void blah(fooii *x);

In this case, the following typemap patterns are searched for the argument fooii *x:

在這種情況下,將在以下類型映射模式中搜索參數 fooii *x

fooii *x
fooii *

# Reduce fooii --> foo<Integer, Integer>
foo<Integer, Integer> *x
foo<Integer, Integer> *

# Reduce Integer -> int
foo<int, Integer> *x
foo<int, Integer> *

# Reduce Integer -> int
foo<int, int> *x
foo<int, int> *

Typemap reductions are always applied to the left-most type that appears. Only when no reductions can be made to the left-most type are reductions made to other parts of the type. This behavior means that you could define a typemap for foo<int, Integer>, but a typemap for foo<Integer, int> would never be matched. Admittedly, this is rather esoteric--there's little practical reason to write a typemap quite like that. Of course, you could rely on this to confuse your coworkers even more.

As a point of clarification, it is worth emphasizing that typedef matching is a typedef reduction process only, that is, SWIG does not search for every single possible typedef. Given a type in a declaration, it will only reduce the type, it won't build it up looking for typedefs. For example, given the type Struct, the typemap below will not be used for the aStruct parameter, because Struct is fully reduced:

還原類型映射始終應用於出現在最左側的類型。僅當無法對最左邊的類型進行還原時,才對類型的其他部分進行還原。這種行為意味著你可以為 foo<int, Integer> 定義一個類型映射,但是 foo<Integer, int> 的類型映射不會被匹配。誠然,這是相當不常見的——幾乎沒有實際的理由來編寫類似的類型映射。當然,你可以用它使你的同事更加困惑。

需要澄清一點,值得強調的是 typedef 匹配僅是 typedef 的“還原”過程,也就是說,SWIG 不會搜索每個可能的 typedef。給定聲明中的類型,它只會還原類型,而不會在尋找 typedef 時建立它。例如,給定類型為 Struct,由於 Struct 已被完全還原,因此以下類型映射將不會用於 aStruct 參數:

struct Struct {...};
typedef Struct StructTypedef;

%typemap(in) StructTypedef {
  ...
}

void go(Struct aStruct);

11.3.3 預設類型映射匹配規則

If the basic pattern matching rules result in no match being made, even after typedef reductions, the default typemap matching rules are used to look for a suitable typemap match. These rules match a generic typemap based on the reserved SWIGTYPE base type. For example pointers will use SWIGTYPE *and references will use SWIGTYPE &. More precisely, the rules are based on the C++ class template partial specialization matching rules used by C++ compilers when looking for an appropriate partial template specialization. This means that a match is chosen from the most specialized set of generic typemap types available. For example, when looking for a match to int const *, the rules will prefer to match SWIGTYPE const * if available before matching SWIGTYPE *, before matching SWIGTYPE.

Most SWIG language modules use typemaps to define the default behavior of the C primitive types. This is entirely straightforward. For example, a set of typemaps for primitives marshalled by value or const reference are written like this:

如果即使在還原 typedef 之後基本模式匹配規則最終沒有匹配,將使用預設的類型映射匹配規則來尋找合適的匹配。這些規則匹配基於保留的 SWIGTYPE 基本類型的通用類型映射。例如,指針將使用 SWIGTYPE *,而引用將使用 SWIGTYPE &。更準確地說,這些規則基於 C++ 類模板偏特化匹配規則,這些匹配規則由 C++ 編譯器在尋找合適的模板偏特化時使用。這意味著從可用的最特定的通用類型映射類型集合中選擇一個匹配項。例如,當尋找與 int const * 的匹配項時,規則將優先匹配 SWIGTYPE const *(如果有的話),然後再匹配 SWIGTYPE *,再匹配 SWIGTYPE

大多數 SWIG 語言模塊都使用類型映射來定義 C 基本類型的預設行為。這是非常簡單的。例如,按值或常引用編組的原始類型的一組類型映射如下所示:

%typemap(in) int           "... convert to int ...";
%typemap(in) short         "... convert to short ...";
%typemap(in) float         "... convert to float ...";
...
%typemap(in) const int &   "... convert ...";
%typemap(in) const short & "... convert ...";
%typemap(in) const float & "... convert ...";
...

Since typemap matching follows all typedef declarations, any sort of type that is mapped to a primitive type by value or const reference through typedef will be picked up by one of these primitive typemaps. Most language modules also define typemaps for char pointers and char arrays to handle strings, so these non-default types will also be used in preference as the basic typemap matching rules provide a better match than the default typemap matching rules.

Below is a list of the typical default types supplied by language modules, showing what the "in" typemap would look like:

由於類型映射匹配遵循所有的 typedef 聲明,因此通過 typedef 通過值或常引用映射到原始類型的任何類型的類型都將被這些原始類型映射之一所拾取。大多數語言模塊還為 char 指針和 char 數組定義了類型映射以處理字元串,因此這些非預設類型也將優先使用,因為基本的類型映射匹配規則比預設的類型映射匹配規則提供了更好的匹配。

下麵是語言模塊提供的典型預設類型的列表,顯示了 in 類型映射的樣子:

%typemap(in) SWIGTYPE &            { ... default reference handling ...                       };
%typemap(in) SWIGTYPE *            { ... default pointer handling ...                         };
%typemap(in) SWIGTYPE *const       { ... default pointer const handling ...                   };
%typemap(in) SWIGTYPE *const&      { ... default pointer const reference handling ...         };
%typemap(in) SWIGTYPE[ANY]         { ... 1D fixed size arrays handlling ...                   };
%typemap(in) SWIGTYPE []           { ... unknown sized array handling ...                     };
%typemap(in) enum SWIGTYPE         { ... default handling for enum values ...                 };
%typemap(in) const enum SWIGTYPE & { ... default handling for const enum reference values ... };
%typemap(in) SWIGTYPE (CLASS::*)   { ... default pointer member handling ...                  };
%typemap(in) SWIGTYPE              { ... simple default handling ...                          };

If you wanted to change SWIG's default handling for simple pointers, you would simply redefine the rule for SWIGTYPE *. Note, the simple default typemap rule is used to match against simple types that don't match any other rules:

如果你想更改 SWIG 對簡單指針的預設處理,很簡單,只需為 SWIGTYPE * 重新定義規則。請註意,簡單的預設類型映射規則用於與不匹配任何其他規則的簡單類型進行匹配:

%typemap(in) SWIGTYPE              { ... simple default handling ...                          }

This typemap is important because it is the rule that gets triggered when call or return by value is used. For instance, if you have a declaration like this:

此類型映射很重要,因為使用調用或按值返回時會觸發該規則。例如,如果你有這樣的聲明:

double dot_product(Vector a, Vector b);

The Vector type will usually just get matched against SWIGTYPE. The default implementation of SWIGTYPEis to convert the value into pointers (as described in this earlier section).

By redefining SWIGTYPE it may be possible to implement other behavior. For example, if you cleared all typemaps for SWIGTYPE, SWIG simply won't wrap any unknown datatype (which might be useful for debugging). Alternatively, you might modify SWIGTYPE to marshal objects into strings instead of converting them to pointers.

Let's consider an example where the following typemaps are defined and SWIG is looking for the best match for the enum shown below:

Vector 類型通常只會與 SWIGTYPE 相匹配。SWIGTYPE 的預設實現是將值轉換為指針(如本之前的章節所述)。

通過重新定義 SWIGTYPE,可以實現其他行為。例如,如果你清除了 SWIGTYPE 的所有類型映射,則 SWIG 不會包裝任何未知的數據類型(這可能對調試很有用)。或者,你可以修改 SWIGTYPE 以將對象編組為字元串,而不是將它們轉換為指針。

讓我們考慮一個示例,其中定義了以下類型映射,並且 SWIG 正在為以下所示的枚舉尋找最佳匹配:

%typemap(in) const Hello &          { ... }
%typemap(in) const enum SWIGTYPE &  { ... }
%typemap(in) enum SWIGTYPE &        { ... }
%typemap(in) SWIGTYPE &             { ... }
%typemap(in) SWIGTYPE               { ... }

enum Hello {};
const Hello &hi;

The typemap at the top of the list will be chosen, not because it is defined first, but because it is the closest match for the type being wrapped. If any of the typemaps in the above list were not defined, then the next one on the list would have precedence.

The best way to explore the default typemaps is to look at the ones already defined for a particular language module. Typemap definitions are usually found in the SWIG library in a file such as java.swg, csharp.swg etc. However, for many of the target languages the typemaps are hidden behind complicated macros, so the best way to view the default typemaps, or any typemaps for that matter, is to look at the preprocessed output by running swig -E o

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 從零開始的前端生活-理解content(二) 應用 清除浮動 偽元素加content最常見的應用是清除浮動帶來的影響 .clear::after{ content:''; display:table; clear:both; } 字元內容的生成 content還可以插入Unicode字元(萬國 ...
  • 關於《SpringBoot-2.3容器化技術》系列 《SpringBoot-2.3容器化技術》系列,旨在和大家一起學習實踐2.3版本帶來的最新容器化技術,讓咱們的Java應用更加適應容器化環境,在雲計算時代依舊緊跟主流,保持競爭力; 全系列文章分為主題和輔助兩部分,主題部分如下: 《體驗Spring ...
  • 20.裝飾器 20.1 函數基礎知識 在Python中函數為一等公民,我們可以: 把函數賦值給變數 在函數中定義函數 在函數中返回函數 把函數傳遞給函數 20.1.1 把函數賦值給變數 在Python里,函數是對象,因此可以把它賦值給變數,如下所示: def hello(name="Surpass" ...
  • 關於《SpringBoot-2.3容器化技術》系列 《SpringBoot-2.3容器化技術》系列,旨在和大家一起學習實踐2.3版本帶來的最新容器化技術,讓咱們的Java應用更加適應容器化環境,在雲計算時代依舊緊跟主流,保持競爭力; 全系列文章分為主題和輔助兩部分,主題部分如下: 《體驗Spring ...
  • 刷到一個題腦子一下子沒有反應過來記錄一下子學習 如下: 答案就是A 這是為什麼呢 我乍一看nums1 new 了一個數組對象並把長度定為3,nums2聲明瞭一個數組,並定義了12345的值,如果 把nums2賦值給nums1它不是會越界嘛長度不一樣嘛,這是我乍一看的想法。 理解了好一會後發現這個題考 ...
  • 前端採用vue,後臺採用spring cloud微服務,進行前後端分離。公共模塊的搭建 ...
  • 題目:學習static定義靜態變數的用法。 程式分析:無。 實例: 1 #include<stdio.h> 2 int main() 3 { 4 void fun(); 5 for(int i=0;i<3;i++) 6 fun(); 7 return 0; 8 } 9 void fun() 10 { ...
  • akka-cluster對每個節點的每種狀態變化都會在系統消息隊列里發佈相關的事件。通過訂閱有關節點狀態變化的消息就可以獲取每個節點的狀態。這部分已經在之前關於akka-cluster的討論里介紹過了。由於akka-typed里採用了新的消息交流協議,而系統消息的發佈和訂閱也算是消息交換,也受交流協 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...