【问题标题】:Translate return codes SQLITE_OK, ..., SQLITE_DONE to an enum class in C++ (without ask clients to include the C header)将返回码 SQLITE_OK、...、SQLITE_DONE 转换为 C++ 中的枚举类(不要求客户端包含 C 标头)
【发布时间】:2020-04-05 15:34:04
【问题描述】:

我有一个persistence_manager_t 类,它向用户隐藏了可用的实现。 一种实现是使用SQLite,其返回码是 C 预处理定义为

#define SQLITE_OK           0   /* Successful result */

现在我正在尝试创建一个枚举类,而无需手动编写返回代码,即

// persistence_manager_t.cpp
#include <sqlite3.h>
enum class persistence_manager_status {
    STATUS_OK = SQLITE_OK, STATUS_ROW = SQLITE_ROW, STATUS_DONE = SQLITE_DONE
};

但作为类用户尝试

auto rc = context->persistence->open(db_name);
if(rc != persistence_manager_status::STATUS_OK) {
    // TODO: gestione errori
}

事实证明,枚举类应该在标题中定义,所以我要么必须在公共标题 persistence_manager.h 中包含 sqlite3.h,要么类用户应该在他们的实现中自己包含 sqlite3.h(很奇怪,因为他们需要安装 sqlite-dev)。

根据 C++17,枚举类实现是否也有前向声明或类似的东西?我们如何使用它?

【问题讨论】:

  • sqlite3.h中的状态值是否经常变化?如果没有,您可以使用枚举类中的值,并添加注释说明这些值是从 sqlite3.h 中逐字复制的,而不是更改它们。在 persistence_manager_t.cpp 中,您可以添加一些 static_assert 以确保枚举值与 sqlite3.h 值匹配,以捕捉是否有人弄乱了这些值。
  • 这正是我所做的。但是,此解决方案也需要映射到实现中的 SQLITE_KEY。所以这里的脱钩似乎是乌托邦。
  • 值得确保您的persistence_manager 没有“泄漏抽象”,其中persistence_manager 的客户端代码必须了解SQLite-isms。这样,当你用 DB2 或 Oracle 或 Azure 或 AWS 或 GCP 或 MySQL 或 PostgreSQL 或 MongoDB 或 LiteDB 或 Firebase 或 JSON 或 YAML 或 BSON 或 XML 替换 SQLite 时......你的客户端代码也不必是重构。不幸的是,这往往很困难,直到您不得不多次修改代码以找出适当的抽象障碍。
  • persistence_manager 目前有泄漏的抽象,因为类的用户必须在他们的开发机器上安装 sqlite-dev。如果映射函数成功了,那么一切都会无缝地工作并且抽象完成。

标签: c++ sqlite class enums forward-declaration


【解决方案1】:

您可以转发声明enum class,但您似乎想“转发声明”该enum枚举数。那完全是另一回事,这不是你能做的事情。枚举器应该是编译时常量,这意味着该常量的值需要在编译时可见。这意味着枚举器名称需要始终知道常量是什么。

但是,您可以转发声明一堆全局常量变量 (extern const int name;) 并在您喜欢的任何源文件中定义它们。但是,您将失去枚举器的所有优势。这些不是常量表达式(在定义这些变量的文件之外),因此,编译器必须发出访问实际内存的代码以提取变量的值。此外,“枚举数”之间不会有任何关系;您可以切换它们,但编译器不会知道该值应该是枚举数之一。

或者,您可以定义自己的枚举器并编写一个函数,将 SQLite 的枚举器值转换为您的枚举器。如果您的代码的用户不希望直接处理 SQLite,那么使用 SQLite 枚举器的唯一原因是您的方便,而不是您的用户。也就是说,如果您要让您的用户接触 SQLite 的状态代码,那么您就是在让 SQLite 成为您库的接口的一部分,因此您的库的用户也是 SQLite 的用户直接。如果你不想这样,那么你应该在 SQLite 和用户之间提供一个不透明的层。

确实,您可以选择与 SQLite 值相同的枚举器值,并在代码中使用 static_asserts 以确保 SQLite 不会在您背后更改它们的枚举器值(仅供参考:他们不会在次要版本中执行此操作;这会破坏 ABI 兼容性,因此会破坏其库的每个 DLL/SO 用户)。

【讨论】:

  • 所以看起来解耦的唯一方法是编写一个从公共枚举到“隐藏”枚举的映射函数。看起来是我能实现的最佳解决方案。我想知道我可以使用underlying_type,但它也需要在标题中。
猜你喜欢
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多