编译时SQL数据库:如何创建含模板参数元素的std::array?
编译时SQL数据库的表集合存储问题
我正在开发一个纯编译时SQL数据库作为学习项目,当前遇到的问题是:需要存储若干模板参数不同但功能一致的Table元素,希望用简单数据结构替代工作量较大的“基于表生成数据库类”方案。
现有代码定义
Table类以CompileTimeString为模板参数(CompileTimeString是const char[N]的抽象,附带便捷特性,所有表及其名称均在编译时确定):
template<CompileTimeString TableName, CompileTimeString OtherData> class Table { public: //... 表的核心功能实现 };
我期望的Database类结构(当前写法存在问题):
class Database { public: template<CompileTimeString TableName, CompileTimeString OtherData> void add_table() { // 需要实现:构造Table实例并添加到数组 } std::array<Table, 10> tables; // 此处写法错误,Table必须指定模板参数 };
需求是通过调用add_table<CompileTimeString("TableName"), CompileTimeString("OtherData")>();在编译时完成表的添加,将实例存入tables数组。之前尝试的写法未成功:
template<CompileTimeString... TableNames, DatabaseSchemaDescriptorPair... Descriptors> std::array<Table<TableNames, Descriptors>..., 10> tables;
我的目标是学习模板元编程,想知道是否有可行的实现方案。
解决方案思路
不同模板参数的Table是完全不同的类型,而std::array要求所有元素类型一致,因此直接存储Table实例行不通。以下两种模板元编程思路可以解决问题:
方法1:基类+编译时初始化的异构容器(类型擦除)
先定义非模板基类统一接口,让所有Table特化继承它,再用std::array存储基类指针,通过模板元编程在编译时完成初始化:
// 定义统一的表基类 class TableBase { public: virtual ~TableBase() = default; virtual const CompileTimeString& get_name() const = 0; }; // 修改Table类继承基类 template<CompileTimeString TableName, CompileTimeString OtherData> class Table : public TableBase { public: const CompileTimeString& get_name() const override { return TableName; } static constexpr CompileTimeString name = TableName; //... 原有功能 }; // 用模板类接收编译时表列表 template<class... Tables> class Database { public: // 编译时初始化数组 std::array<std::unique_ptr<TableBase>, sizeof...(Tables)> tables = { std::make_unique<Tables>()... }; // 编译时按名称查找表 template<CompileTimeString TargetName> TableBase* get_table() { return find_table<TargetName>(std::make_index_sequence<sizeof...(Tables)>{}); } private: template<CompileTimeString TargetName, size_t... Idx> TableBase* find_table(std::index_sequence<Idx...>) { // 编译时遍历匹配表名 return ((std::get<Idx>(tables)->get_name() == TargetName) ? std::get<Idx>(tables).get() : nullptr)...; } };
使用方式:
Database< Table<CompileTimeString("User"), CompileTimeString("id int, name varchar")>, Table<CompileTimeString("Order"), CompileTimeString("id int, user_id int")> > db; // 编译时获取指定表 auto* user_table = db.get_table<CompileTimeString("User")>();
方法2:std::tuple存储异构实例+编译时类型查找
如果不需要运行时多态,直接用std::tuple存储不同类型的Table,通过元函数在编译时定位目标表:
template<class... Tables> class Database { public: std::tuple<Tables...> tables; Database() : tables(Tables{}...) {} // 编译时按名称获取表 template<CompileTimeString TargetName> auto& get_table() { return std::get<find_table_index<TargetName, Tables...>::value>(tables); } private: // 元函数:查找目标表在tuple中的索引 template<CompileTimeString TargetName, class First, class... Rest> struct find_table_index { static constexpr size_t value = (First::name == TargetName) ? 0 : 1 + find_table_index<TargetName, Rest...>::value; }; template<CompileTimeString TargetName> struct find_table_index<TargetName> { static_assert(false, "指定名称的表不存在"); }; }; // 给Table添加静态名称成员 template<CompileTimeString TableName, CompileTimeString OtherData> class Table { public: static constexpr CompileTimeString name = TableName; //... 原有功能 };
使用方式:
Database< Table<CompileTimeString("User"), CompileTimeString("id int, name varchar")>, Table<CompileTimeString("Order"), CompileTimeString("id int, user_id int")> > db; // 编译时获取表(类型完全匹配) auto& order_table = db.get_table<CompileTimeString("Order")>();
原写法失败原因
std::array要求元素类型统一,而Table<Name1, Data1>与Table<Name2, Data2>是不同类型,无法直接存入同一数组;- 尝试的参数包写法中,
TableNames...和Descriptors...是两个独立参数包,无法一一对应绑定到Table的两个模板参数上。
内容的提问来源于stack exchange,提问作者Eshy
相关产品推荐
相关产品推荐

