用户定义类型转换¶
在创建C++库的Python绑定过程中,大多数C++类将在Python领域中有代表它们的包装器。但可能还有其他类非常简单,并且/或者有Python类型作为直接对应物。(例如:一个表示复数的“Complex”类,在Python中有“complex”类型作为对应物。)这些类通常不会获得Python包装器,而是有从Python到C++以及从C++到Python的转换规则。
// C++ class
struct Complex {
Complex(double real, double imag);
double real() const;
double imag() const;
};
// Converting from C++ to Python using the CPython API:
PyObject* pyCpxObj = PyComplex_FromDoubles(complex.real(), complex.imag());
// Converting from Python to C++:
double real = PyComplex_RealAsDouble(pyCpxObj);
double imag = PyComplex_ImagAsDouble(pyCpxObj);
Complex cpx(real, imag);
为了使用户定义的转换代码能够插入到正确的位置,必须使用conversion-rule标签。
<primitive-type name="Complex" target-lang-api-name="PyComplex">
<include file-name="complex.h" location="global"/>
<conversion-rule>
<native-to-target>
return PyComplex_FromDoubles(%in.real(), %in.imag());
</native-to-target>
<target-to-native>
<!-- The 'check' attribute can be derived from the 'type' attribute,
it is defined here to test the CHECKTYPE type system variable. -->
<add-conversion type="PyComplex" check="%CHECKTYPE[Complex](%in)">
double real = PyComplex_RealAsDouble(%in);
double imag = PyComplex_ImagAsDouble(%in);
%out = %OUTTYPE(real, imag);
</add-conversion>
</target-to-native>
</conversion-rule>
</primitive-type>
详细信息将在稍后给出,但其要点是标签 native-to-target,它只有一个从C++到Python的转换,以及 native-to-native,它可能定义了多个Python类型到C++的“Complex”类型的转换。
Qt for Python 期望 native-to-target 的代码直接返回转换的 Python 结果,并且在 target-to-native 中添加的转换必须将 Python 到 C++ 的转换结果赋值给 %out 变量。
在最后一个例子的基础上,如果绑定开发者希望一个Python的2元组数字能够被带有“Complex”参数的封装C++函数接受,必须添加一个add-conversion标签和一个自定义检查。以下是具体做法:
<!-- Code injection at module level. -->
<inject-code class="native" position="beginning">
static bool Check2TupleOfNumbers(PyObject* pyIn) {
if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2))
return false;
Shiboken::AutoDecRef pyReal(PySequence_GetItem(pyIn, 0));
if (!PyNumber_Check(pyReal))
return false;
Shiboken::AutoDecRef pyImag(PySequence_GetItem(pyIn, 1));
if (!PyNumber_Check(pyImag))
return false;
return true;
}
</inject-code>
<primitive-type name="Complex" target-lang-api-name="PyComplex">
<include file-name="complex.h" location="global"/>
<conversion-rule>
<native-to-target>
return PyComplex_FromDoubles(%in.real(), %in.imag());
</native-to-target>
<target-to-native>
<add-conversion type="PyComplex">
double real = PyComplex_RealAsDouble(%in);
double imag = PyComplex_ImagAsDouble(%in);
%out = %OUTTYPE(real, imag);
</add-conversion>
<add-conversion type="PySequence" check="Check2TupleOfNumbers(%in)">
Shiboken::AutoDecRef pyReal(PySequence_GetItem(%in, 0));
Shiboken::AutoDecRef pyImag(PySequence_GetItem(%in, 1));
double real = %CONVERTTOCPP[double](pyReal);
double imag = %CONVERTTOCPP[double](pyImag);
%out = %OUTTYPE(real, imag);
</add-conversion>
</target-to-native>
</conversion-rule>
</primitive-type>
容器转换¶
用于container-type的转换器与其他类型的转换器基本相同,只是它们使用了类型系统变量%INTYPE_#和%OUTTYPE_#。Qt for Python将容器的转换代码与为容器定义(或自动生成)的转换结合起来。
<container-type name="std::map" type="map">
<include file-name="map" location="global"/>
<conversion-rule>
<native-to-target>
PyObject* %out = PyDict_New();
%INTYPE::const_iterator it = %in.begin();
for (; it != %in.end(); ++it) {
%INTYPE_0 key = it->first;
%INTYPE_1 value = it->second;
PyDict_SetItem(%out,
%CONVERTTOPYTHON[%INTYPE_0](key),
%CONVERTTOPYTHON[%INTYPE_1](value));
}
return %out;
</native-to-target>
<target-to-native>
<add-conversion type="PyDict">
PyObject* key;
PyObject* value;
Py_ssize_t pos = 0;
while (PyDict_Next(%in, &pos, &key, &value)) {
%OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key);
%OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value);
%out.insert(%OUTTYPE::value_type(cppKey, cppValue));
}
</add-conversion>
</target-to-native>
</conversion-rule>
</container-type>
注意
C++ 容器 std::list, std::vector,
std::pair, std::map, std::span 和 std::unordered_map 是内置的。
要指定 Opaque Containers,请使用 opaque-container 元素。
container-type 仍然可以指定以修改内置行为。
对于这种情况,提供了许多预定义的转换模板(参见 Predefined Templates)。
变量与函数¶
- %in
变量由C++输入变量替换。
- %out
变量被C++输出变量替换。需要传达Python到C++转换的结果。
- %INTYPE
用于Python到C++的转换。它被替换为正在定义转换的类型的名称。不要直接使用类型的名称。
- %INTYPE_#
替换为容器中使用的第#种类型的名称。
- %OUTTYPE
用于Python到C++的转换。它被替换为正在定义转换的类型的名称。不要直接使用类型的名称。
- %OUTTYPE_#
替换为容器中使用的第#种类型的名称。
- %CHECKTYPE[CPPTYPE]
被用于Python变量的Qt for Python类型检查函数替换。 C++类型由
CPPTYPE表示。