不透明容器¶
通常情况下,当调用接受相应C++容器的C++函数时,会传递Python容器,如list或dict(参见container-type)。
这意味着每次调用时,整个Python容器都会被转换为C++容器,这在例如从点列表创建图表时可能效率低下。
为了解决这个问题,可以生成特殊的不透明容器,这些容器直接包装底层的C++容器(目前已经为list类型实现)。它们实现了序列协议,并且可以传递给函数,而不是Python列表。像添加或删除元素这样的操作可以直接使用C++容器函数对它们进行操作。
这是通过在container-type的opaque-containers属性中指定名称和实例化类型,或者使用opaque-container元素来实现现有容器类型的。
第二个用例是容器类型的公共字段。在正常情况下,它们在读取访问时会被转换为Python容器。通过字段修改(参见modify-field),可以获得一个不透明的容器,这样可以避免转换并允许直接修改元素。
返回引用的Getters也可以修改为返回不透明容器。 这是通过将返回类型修改为不透明容器的名称来实现的 (参见replace-type)。
下表列出了除了序列协议(通过索引和len()访问元素)之外,不透明序列容器支持的功能。同时支持STL和Qt命名约定(类似于Python的):
函数 |
描述 |
|
将value追加到序列中。 |
|
将value添加到序列的前面。 |
|
清除序列。 |
|
移除最后一个元素。 |
|
移除第一个元素。 |
|
对于支持它的容器
( |
|
对于支持它的容器
( |
|
对于支持它的容器
( |
|
对于支持它的容器
( |
注意
std::span,作为一个非拥有容器,目前被std::vector替代用于参数传递。这意味着从函数中获得的封装std::span的不透明容器在传递给接受std::span的函数时,将通过序列转换转换为std::vector。封装std::vector的不透明容器可以直接传递而无需转换。这目前是实验性的,可能会有所变化。
以下是一个示例,展示了如何从std::vectorIntVector的不透明容器,并在Python中使用它。
我们将考虑三个独立的使用案例。
案例 1 - 当Python列表作为不透明容器传递给C++函数
TestOpaqueContainer.getVectorSum(const std::vector
class TestOpaqueContainer
{
public:
static int getVectorSum(const std::vector<int>& intVector)
{
return std::accumulate(intVector.begin(), intVector.end(), 0);
}
};
案例 2 - 当我们有一个名为 TestOpaqueContainer 的 C++ 类,其中包含一个 std::vector 公共变量
class TestOpaqueContainer
{
public:
std::vector<int> intVector;
};
案例 3 - 当我们有一个名为 TestOpaqueContainer 的 C++ 类,其中包含一个 std::vector 作为私有变量,并且该变量通过 getter 以引用形式返回。
class TestOpaqueContainer
{
public:
std::vector<int>& getIntVector()
{
return this->intVector;
}
private:
std::vector<int> intVector;
};
注意
案例2和案例3通常被认为是C++中不良的类设计。然而,这些示例的目的更多是为了展示在Shiboken中使用不透明容器的不同可能性,而不是类设计。
在所有这三种情况下,我们都希望通过不透明容器在Python中使用intVector。首先要做的是在类型系统文件中创建相应的属性,使Shiboken知道IntVector。
<container-type name="std::vector" type="vector" opaque-containers="int:IntVector">
<include file-name="vector" location="global"/>
<conversion-rule>
<native-to-target>
<insert-template name="shiboken_conversion_cppsequence_to_pylist"/>
</native-to-target>
<target-to-native>
<add-conversion type="PySequence">
<insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer"/>
</add-conversion>
</target-to-native>
</conversion-rule>
</container-type>
对于其余的步骤,我们分别考虑这三种情况。
案例 1 - 当将 Python 列表传递给 C++ 函数时
作为下一步,我们为类 TestOpaqueContainer 创建一个类型系统条目。
<value-type name="TestOpaqueContainer" />
在这种情况下,类型系统条目很简单,函数
getVectorSum(const std::vector 接受 IntVector 作为参数。这是因为本质上 IntVector 与 std::vector 是相同的。
现在,构建代码以创建我们将导入到Python中的*_wrapper.cpp和*.so文件。
验证在Python中的使用
>>> vector = IntVector()
>>> vector.push_back(2)
>>> vector.push_back(3)
>>> len(vector)
2
>>> TestOpaqueContainer.getVectorSum(vector)
vector sum is 5
案例 2 - 当变量是公共的
我们为类TestOpaqueContainer创建一个类型系统条目。
<value-type name="TestOpaqueContainer">
<modify-field name="intVector" opaque-container="yes"/>
</value-type>
在中注意opaque-container="yes"。由于intVector的类型是std::vector,它选择了IntVector不透明容器。
构建代码以创建我们导入到Python中的*_wrapper.cpp和*.so文件。
验证在Python中的使用
>>> test = TestOpaqueContainer()
>>> test
<Universe.TestOpaqueContainer object at 0x7fe17ef30c30>
>>> test.intVector.push_back(1)
>>> test.intVector.append(2)
>>> len(test.intVector)
2
>>> test.intVector[1]
2
>>> test.intVector.removeLast()
>>> len(test.intVector)
1
案例 3 - 当变量是私有的并通过getter以引用方式返回时
与之前的情况类似,我们为类 TestOpaqueContainer 创建了一个类型系统条目。
<value-type name="TestOpaqueContainer">
<modify-function signature="getIntVector()">
<modify-argument index="return">
<replace-type modified-type="IntVector" />
</modify-argument>
</modify-function>
</value-type>
在这种情况下,我们在字段中指定了不透明容器IntVector的名称。
构建代码以创建我们导入到Python中的*_wrapper.cpp和*.so文件。
验证在Python中的使用
>>> test = TestOpaqueContainer()
>>> test
<Universe.TestOpaqueContainer object at 0x7f62b9094c30>
>>> vector = test.getIntVector()
>>> vector
<Universe.IntVector object at 0x7f62b91f7d00>
>>> vector.push_back(1)
>>> vector.push_back(2)
>>> len(vector)
2
>>> vector[1]
2
>>> vector.removeLast()
>>> len(vector)
1
在所有这三种情况下,如果我们查看模块对应的包装类,我们将看到以下行
static PyMethodDef IntVector_methods[] = {
{"push_back", reinterpret_cast<PyCFunction>(
ShibokenSequenceContainerPrivate<std::vector<int >>::push_back),METH_O, "push_back"},
{"append", reinterpret_cast<PyCFunction>(
ShibokenSequenceContainerPrivate<std::vector<int >>::push_back),METH_O, "append"},
{"clear", reinterpret_cast<PyCFunction>(
ShibokenSequenceContainerPrivate<std::vector<int >>::clear), METH_NOARGS, "clear"},
{"pop_back", reinterpret_cast<PyCFunction>(
ShibokenSequenceContainerPrivate<std::vector<int >>::pop_back), METH_NOARGS,
"pop_back"},
{"removeLast", reinterpret_cast<PyCFunction>(
ShibokenSequenceContainerPrivate<std::vector<int >>::pop_back), METH_NOARGS,
"removeLast"},
{nullptr, nullptr, 0, nullptr} // Sentinel
};
这意味着,上述方法可以在Python中使用IntVector不透明容器。
注意
Plot example
演示了一个使用不透明容器 QPointList 的示例,该容器包装了一个 C++
QList