【QGIS源码阅读】QgsDataProvider::ReadFlags


一、代码及使用

1. 源码

代码如下(示例):

enum ReadFlag
{
  FlagTrustDataSource = 1 << 0, //!< Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load time by skipping expensive checks like primary key unicity, geometry type and srid and by using estimated metadata on data load. Since QGIS 3.16
  SkipFeatureCount = 1 << 1, //!< Make featureCount() return -1 to indicate unknown, and subLayers() to return a unknown feature count as well. Since QGIS 3.18. Only implemented by OGR provider at time of writing.
  FlagLoadDefaultStyle = 1 << 2, //!< Reset the layer's style to the default for the datasource
  SkipGetExtent = 1 << 3, //!< Skip the extent from provider
  SkipFullScan = 1 << 4, //!< Skip expensive full scan on files (i.e. on delimited text) (since QGIS 3.24)
  ForceReadOnly = 1 << 5, //!< Open layer in a read-only mode (since QGIS 3.28)
  SkipCredentialsRequest =  1 << 6, //! Skip credentials if the provided one are not valid, let the provider be invalid, avoiding to block the thread creating the provider if it is not the main thread (since QGIS 3.32).
  ParallelThreadLoading = 1 << 7, //! Provider is created in a parallel thread than the one where it will live (since QGIS 3.32.1).
};
Q_DECLARE_FLAGS( ReadFlags, ReadFlag )

2. 解释

这个 ReadFlag 枚举类定义了数据提供程序(QgsDataProvider)在读取数据时的一系列行为标志,用于精细控制数据加载过程中的特定逻辑。每个标志都是一个位运算值(使用 1 << n 定义),支持按位组合(通过 | 操作符),从而实现多标志的同时生效。

各枚举值的含义如下:

  1. FlagTrustDataSource = 1 << 0

    • 信任数据源的配置信息(如主键唯一性、几何类型、空间参考系等)。
    • 作用:跳过代价高昂的验证检查(如主键唯一性校验、几何类型和空间参考系确认),并在数据加载时使用估算的元数据,从而提高数据提供程序的加载速度。
    • 引入版本:QGIS 3.16。
  2. SkipFeatureCount = 1 << 1

    • 使 featureCount() 方法返回 -1(表示未知要素数量),同时 subLayers() 也返回未知的要素计数。
    • 作用:避免计算要素总数(可能耗时),适用于只需要快速加载图层结构而不关心具体要素数量的场景。
    • 引入版本:QGIS 3.18,最初仅在 OGR 提供程序中实现。
  3. FlagLoadDefaultStyle = 1 << 2

    • 将图层样式重置为数据源的默认样式。
    • 作用:加载图层时自动应用数据源内置的默认样式,而非保留之前的自定义样式。
  4. SkipGetExtent = 1 << 3

    • 跳过从提供程序获取空间范围(extent)。
    • 作用:避免计算图层的空间范围(可能耗时,尤其对大型数据集),适用于不需要空间范围信息的场景。
  5. SkipFullScan = 1 << 4

    • 跳过对文件的全量扫描(如分隔文本文件)。
    • 作用:避免完整读取文件内容以获取元数据(如字段类型),加快加载速度(可能牺牲部分元数据准确性)。
    • 引入版本:QGIS 3.24。
  6. ForceReadOnly = 1 << 5

    • 强制以只读模式打开图层。
    • 作用:即使数据源支持写入,也限制为只读访问,防止误修改。
    • 引入版本:QGIS 3.28。
  7. SkipCredentialsRequest = 1 << 6

    • 当提供的凭据无效时,跳过重新请求凭据,直接让提供程序处于无效状态。
    • 作用:避免在非主线程创建提供程序时因凭据请求而阻塞线程。
    • 引入版本:QGIS 3.32。
  8. ParallelThreadLoading = 1 << 7

    • 指示提供程序在与最终运行线程不同的并行线程中创建。
    • 作用:用于多线程环境下的加载优化,确保线程安全。
    • 引入版本:QGIS 3.32.1。

3. 问题

1. Q_DECLARE_FLAGS(ReadFlags, ReadFlag)

Q_DECLARE_FLAGS(ReadFlags, ReadFlag) 是 Qt 框架中用于处理枚举标志(enumeration flags)的宏定义,其作用是为枚举类型 ReadFlag 创建一个对应的“标志集合类型” ReadFlags,以便支持位运算组合多个枚举值

具体来说,它的功能包括:

  1. 定义标志集合类型
    声明 ReadFlags 作为 ReadFlag 枚举值的集合类型,允许将多个 ReadFlag 枚举值通过位运算符(|&^ 等)组合起来。例如:

    ReadFlags flags = ReadFlag::FlagTrustDataSource | ReadFlag::SkipFeatureCount;
    

    这样就创建了一个同时包含两个标志的集合。

  2. 支持位运算操作
    该宏会自动生成必要的运算符重载(如 |&~ 等),使得 ReadFlags 类型可以像整数一样进行位运算,方便对标志进行添加、移除、检查等操作。例如:

    // 添加标志
    flags |= ReadFlag::SkipGetExtent;
    
    // 检查是否包含某个标志
    if (flags & ReadFlag::FlagTrustDataSource) {
        // 执行相应逻辑
    }
    
    // 移除标志
    flags &= ~ReadFlag::SkipFeatureCount;
    
  3. 与 Qt 元对象系统兼容
    该宏会生成相关代码,使 ReadFlags 类型能够被 Qt 的元对象系统(QMetaObject)识别,支持在 Qt 信号槽、属性系统等场景中使用。

简单说,Q_DECLARE_FLAGS(ReadFlags, ReadFlag) 的核心目的是让枚举值能够像“开关”一样被组合使用,从而灵活地控制程序行为(在 QGIS 中就是控制数据提供程序的读取逻辑)。

通常,在使用这个宏之后,还会配合 Q_DECLARE_OPERATORS_FOR_FLAGS 宏来完成全局运算符的重载,例如:

Q_DECLARE_OPERATORS_FOR_FLAGS(ReadFlags)

这样就可以在全局范围内使用位运算符处理 ReadFlags 类型了。

2. ReadFlags 和 ReadFlag

ReadFlagsReadFlag 是两种不同的类型:

  • ReadFlag 是单个枚举值的类型(如 FlagTrustDataSource 本身);
  • ReadFlags 是通过 Q_DECLARE_FLAGS 定义的集合类型,用于存储多个 ReadFlag 的组合。

关于赋值的规则:

  1. 允许直接用单个 ReadFlag 赋值给 ReadFlags
    例如:

    ReadFlags flags = ReadFlag::FlagTrustDataSource; // 完全合法
    

    这是因为 Q_DECLARE_FLAGS 会自动生成从 ReadFlagReadFlags 的隐式转换,单个枚举值可以被视作只包含自身的“集合”。

  2. | 运算符的作用是组合多个 ReadFlag,生成 ReadFlags
    | 连接多个 ReadFlag 时,结果会自动转换为 ReadFlags 类型。例如:

    // 单个枚举值 → 转换为 ReadFlags
    ReadFlags flags1 = ReadFlag::FlagTrustDataSource; 
    
    // 多个枚举值通过 | 组合 → 结果为 ReadFlags
    ReadFlags flags2 = ReadFlag::FlagTrustDataSource | ReadFlag::SkipFeatureCount;
    
  3. ReadFlags 不能直接赋值给 ReadFlag
    因为 ReadFlags 是多个枚举值的集合,而 ReadFlag 是单个值,类型不兼容(除非显式转换,但不推荐,可能导致数据丢失)。

总结:

  • ReadFlags 作为集合类型,既可以接收单个 ReadFlag(隐式转换为只包含该值的集合),也可以接收多个 ReadFlag 通过 | 组合的结果。
  • 不需要“至少两个 ReadFlag”,单个 ReadFlag 赋值给 ReadFlags 是完全合法且常见的用法(表示集合中只包含这一个标志)。

3. 位移枚举值

在枚举类 ReadFlag 中,每个枚举值都是通过位运算 1 << n 定义的,这是一种常见的标志位定义方式,每个值对应二进制中不同的位,方便通过位运算组合使用。

各个枚举值的具体数值如下:

  • FlagTrustDataSource = 1 << 0
    计算:1 << 0 表示 1 左移 0 位,结果为 1(二进制:00000001

  • SkipFeatureCount = 1 << 1
    计算:1 << 1 表示 1 左移 1 位,结果为 2(二进制:00000010

  • FlagLoadDefaultStyle = 1 << 2
    计算:1 << 2 表示 1 左移 2 位,结果为 4(二进制:00000100

  • SkipGetExtent = 1 << 3
    计算:1 << 3 表示 1 左移 3 位,结果为 8(二进制:00001000

  • SkipFullScan = 1 << 4
    计算:1 << 4 表示 1 左移 4 位,结果为 16(二进制:00010000

  • ForceReadOnly = 1 << 5
    计算:1 << 5 表示 1 左移 5 位,结果为 32(二进制:00100000

  • SkipCredentialsRequest = 1 << 6
    计算:1 << 6 表示 1 左移 6 位,结果为 64(二进制:01000000

  • ParallelThreadLoading = 1 << 7
    计算:1 << 7 表示 1 左移 7 位,结果为 128(二进制:10000000

这种定义方式的优势是:可以通过位运算 | 组合多个标志,例如 FlagTrustDataSource | SkipFeatureCount 的结果为 1 | 2 = 3(二进制 00000011),表示同时启用这两个标志;通过 & 运算可以检查是否包含某个标志。

4. QgsDataProvider::ReadFlags mReadFlags = QgsDataProvider::ReadFlags();

  • QgsDataProvider::ReadFlags 是通过 Q_DECLARE_FLAGS(ReadFlags, ReadFlag) 定义的标志集合类型,用于存储多个 ReadFlag 的组合(本质是位掩码)。
  • QgsDataProvider::ReadFlags() 是默认构造函数,创建的对象 mReadFlags 初始值为 0(即不包含任何 ReadFlag 标志)。

这意味着初始状态下,mReadFlags 不启用任何特殊读取行为,数据提供程序将使用默认的加载逻辑(如执行完整的验证检查、计算要素数量和空间范围等)。

如果 ReadFlag 枚举中存在值为 0 的成员(例如假设存在 ReadFlag::None = 0),那么通过 QgsDataProvider::ReadFlags() 默认构造的 mReadFlags 对象,其底层值为 0在数值上与该枚举成员相等

但需要注意两者的语义区别

  • ReadFlag::None(假设值为 0)是枚举中明确定义的成员,代表“无任何标志”的合法状态,是开发者显式指定的逻辑。
  • ReadFlags() 默认构造的对象,其值为 0类型默认初始化的结果(底层值为 0),虽然数值上与 ReadFlag::None 一致,但它本身并不直接等同于该枚举成员,而是“空集合”的自然状态(即不包含任何 ReadFlag 标志)。

实际使用中的表现:

当用 & 运算符检查 mReadFlags 是否包含 ReadFlag::None 时,结果会为 true(因为两者底层值都是 0)。例如:

// 假设存在 ReadFlag::None = 0
ReadFlags flags = ReadFlags(); // 底层值 0
if (flags & ReadFlag::None) { 
    // 条件成立(数值上 0 & 0 = 0,但实际逻辑中通常 None 代表“无标志”,可能不会这样检查)
}

但从设计意图上,ReadFlag::None 通常用于表示“不启用任何标志”,而 ReadFlags() 默认构造的对象本身就代表“空集合”,两者在功能上是等价的(都表示没有任何标志)。因此,即使存在值为 0 的枚举成员,这种数值上的巧合也不会导致逻辑错误,反而会让默认状态与“无标志”的语义自然统一。

二、拓展基础知识

1. 位运算符

在 C++ 等编程语言中,位运算符用于对整数的二进制位进行操作,常用于标志位组合、权限控制、数据压缩等场景。以下是常用的位运算符及其使用说明:

1. 按位与(&

  • 作用:对两个数的二进制位进行“与”操作,只有当对应位都为 1 时,结果位才为 1,否则为 0。
  • 示例
    int a = 5;    // 二进制:0101
    int b = 3;    // 二进制:0011
    int c = a & b; // 结果:0001(十进制 1)
    
  • 应用
    • 检查某个位是否为 1(如判断标志集合中是否包含某个标志):
      if (flags & ReadFlag::FlagTrustDataSource) { 
          // 包含该标志(对应位为 1)
      }
      
    • 清除特定的位(配合 ~ 使用)。

2. 按位或(|

  • 作用:对两个数的二进制位进行“或”操作,只要对应位中有一个为 1,结果位就为 1,否则为 0。
  • 示例
    int a = 5;    // 0101
    int b = 3;    // 0011
    int c = a | b; // 结果:0111(十进制 7)
    
  • 应用
    • 组合多个标志位(如给 ReadFlags 集合添加多个标志):
      ReadFlags flags = ReadFlag::FlagTrustDataSource | ReadFlag::SkipFeatureCount;
      

3. 按位异或(^

  • 作用:对两个数的二进制位进行“异或”操作,当对应位不同时结果为 1,相同时为 0
  • 示例
    int a = 5;    // 0101
    int b = 3;    // 0011
    int c = a ^ b; // 结果:0110(十进制 6)
    
  • 应用
    • 切换位的状态(0 变 1,1 变 0):
      flags = flags ^ ReadFlag::SkipFeatureCount; // 若有该标志则移除,若无则添加
      
    • 简单的加密/解密(与同一个数异或两次可还原原值)。

4. 按位非(~

  • 作用:对一个数的二进制位进行“取反”操作,0 变 1,1 变 0(单目运算符,只有一个操作数)。
  • 示例
    int a = 5;    // 二进制(4位表示):0101
    int b = ~a;   // 结果(4位表示):1010(实际中根据类型位数取反,如32位/64位)
    
  • 应用
    • 配合 & 清除特定位:
      flags = flags & ~ReadFlag::SkipFeatureCount; // 移除该标志
      

5. 左移(<<

  • 作用:将一个数的二进制位向左移动指定的位数,右侧空出的位用 0 填充。
  • 语法a << n(将 a 的二进制位左移 n 位)。
  • 示例
    int a = 1;    // 0001
    int b = a << 2; // 左移2位 → 0100(十进制 4)
    
  • 应用
    • 生成标志位(如 ReadFlag 中用 1 << n 定义不同的标志)。
    • 快速计算乘以 2 的幂(a << n 等价于 a * 2ⁿ,效率更高)。

6. 右移(>>

  • 作用:将一个数的二进制位向右移动指定的位数,左侧空出的位根据符号位填充(正数补 0,负数补 1,取决于编译器)。
  • 语法a >> n(将 a 的二进制位右移 n 位)。
  • 示例
    int a = 8;    // 1000
    int b = a >> 2; // 右移2位 → 0010(十进制 2)
    
  • 应用
    • 快速计算除以 2 的幂(a >> n 等价于 a / 2ⁿ,效率更高)。
    • 提取特定的位(如从二进制数中截取某几位)。

总结

位运算符的核心用途是直接操作二进制位,在处理标志位、权限控制、底层数据处理等场景中非常高效。例如在 QGIS 的 ReadFlags 中,通过 | 组合标志、& 检查标志、&~ 移除标志,正是位运算的典型应用。

三、待处理

  1. 强类型枚举与普通枚举
  2. 枚举与整型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值