C++通用成员变量实现与SQL数据库接口代码复用方案咨询
实现方向与代码方案
核心思路:用CRTP+模板元编程消除重复代码
针对你的场景,推荐通过**奇异递归模板模式(CRTP)**封装通用逻辑,结合编译期类型安全的额外字段处理,既保留静态类型检查,又彻底消除重复的query_getTable、流操作符等代码。
步骤1:封装通用基类(处理共同字段)
先定义基类存储id/name/description三个通用字段,并实现通用的读取逻辑:
#include <string> #include <vector> #include <tuple> #include <sql.h> // 适配SQL Server的API(如ODBC) class BaseForeignTable { protected: int id; std::string name; std::string description; // 读取通用字段的核心逻辑 void readBaseFields(SQLHSTMT stmt) { // 绑定并读取通用列(假设SQL列顺序为id、name、description) SQLBindCol(stmt, 1, SQL_C_SLONG, &id, sizeof(id), nullptr); SQLCHAR name_buf[256]; SQLBindCol(stmt, 2, SQL_C_CHAR, name_buf, sizeof(name_buf), nullptr); SQLCHAR desc_buf[1024]; SQLBindCol(stmt, 3, SQL_C_CHAR, desc_buf, sizeof(desc_buf), nullptr); // 执行读取(实际需处理SQL返回码,此处简化) if (SQLFetch(stmt) == SQL_SUCCESS) { name = reinterpret_cast<char*>(name_buf); description = reinterpret_cast<char*>(desc_buf); } } public: // 通用Getter int getId() const { return id; } const std::string& getName() const { return name; } const std::string& getDescription() const { return description; } };
步骤2:用CRTP实现通用流操作符与查询逻辑
通过CRTP模板类,让派生类自动继承通用的>>流操作符和query_getTable函数,同时允许派生类自定义额外字段的处理:
template <typename Derived> class TableCRTP : public BaseForeignTable { public: // 通用>>流操作符:自动调用基类+派生类的读取逻辑 friend std::istream& operator>>(std::istream& is, TableCRTP<Derived>& table) { // 从流中获取SQL语句句柄(需根据你的数据库接口调整) SQLHSTMT stmt = ...; // 读取通用字段 table.readBaseFields(stmt); // 调用派生类的额外字段读取逻辑 static_cast<Derived*>(&table)->readExtraFields(stmt); return is; } // 通用查询函数:返回指定类型的表数据容器 template <typename Container = std::vector<Derived>> static Container query_getTable(Database& db, const std::string& tableName) { Container result; // 拼接SQL:通用列 + 派生类提供的额外列 std::string extraCols = static_cast<Derived*>(nullptr)->getExtraColumns(); std::string query = "SELECT id, name, description" + (extraCols.empty() ? "" : ", " + extraCols) + " FROM " + tableName; auto stmt = db.executeQuery(query); while (SQLFetch(stmt) == SQL_SUCCESS) { Derived item; item.readBaseFields(stmt); item.readExtraFields(stmt); result.push_back(std::move(item)); } db.closeStmt(stmt); return result; } };
步骤3:派生类实现自定义逻辑
针对每个表,仅需实现额外字段的读取和列名声明即可,无需重复编写通用逻辑:
// 示例:带price、stock字段的产品表 class ProductTable : public TableCRTP<ProductTable> { private: float price; int stock; public: // 返回额外列的SQL字符串 std::string getExtraColumns() const { return "price, stock"; } // 读取额外字段的逻辑 void readExtraFields(SQLHSTMT stmt) { SQLBindCol(stmt, 4, SQL_C_FLOAT, &price, sizeof(price), nullptr); SQLBindCol(stmt, 5, SQL_C_SLONG, &stock, sizeof(stock), nullptr); // 注:基类已执行SQLFetch,此处直接取值即可 } // 自定义Getter float getPrice() const { return price; } int getStock() const { return stock; } }; // 示例:带email、is_active字段的用户表 class UserTable : public TableCRTP<UserTable> { private: std::string email; bool is_active; public: std::string getExtraColumns() const { return "email, is_active"; } void readExtraFields(SQLHSTMT stmt) { SQLCHAR email_buf[256]; SQLBindCol(stmt, 4, SQL_C_CHAR, email_buf, sizeof(email_buf), nullptr); SQLINTEGER active_val; SQLBindCol(stmt, 5, SQL_C_BIT, &active_val, sizeof(active_val), nullptr); email = reinterpret_cast<char*>(email_buf); is_active = (active_val == 1); } const std::string& getEmail() const { return email; } bool isActive() const { return is_active; } };
步骤4:更通用的额外字段处理(可选)
如果希望额外字段的定义更灵活(类似Python字典的键值对但保留类型安全),可以用std::tuple存储额外字段,配合模板元编程自动绑定SQL列:
template <typename ExtraFieldsTuple> class TableWithTupleExtra : public BaseForeignTable { protected: ExtraFieldsTuple extra_fields; // 递归绑定tuple中的每个字段 template <std::size_t... I> void bindExtraCols(SQLHSTMT stmt, int startCol, std::index_sequence<I...>) { (bindSingleCol(stmt, startCol + I, std::get<I>(extra_fields)), ...); } // 针对不同类型的绑定重载 void bindSingleCol(SQLHSTMT stmt, int col, int& val) { SQLBindCol(stmt, col, SQL_C_SLONG, &val, sizeof(val), nullptr); } void bindSingleCol(SQLHSTMT stmt, int col, float& val) { SQLBindCol(stmt, col, SQL_C_FLOAT, &val, sizeof(val), nullptr); } void bindSingleCol(SQLHSTMT stmt, int col, std::string& val) { SQLCHAR buf[256]; SQLBindCol(stmt, col, SQL_C_CHAR, buf, sizeof(buf), nullptr); val = reinterpret_cast<char*>(buf); } public: void readAllFields(SQLHSTMT stmt) { readBaseFields(stmt); bindExtraCols(stmt, 4, std::make_index_sequence<std::tuple_size_v<ExtraFieldsTuple>>{}); } // 访问额外字段 template <std::size_t I> auto& getExtra() { return std::get<I>(extra_fields); } }; // 使用示例:产品表的额外字段为(float, int) using ProductTable = TableWithTupleExtra<std::tuple<float, int>>;
调用示例
Database db; // 你的数据库实例 // 查询产品表 auto products = ProductTable::query_getTable(db, "products"); // 查询用户表 auto users = UserTable::query_getTable(db, "users"); // 流操作符使用 ProductTable product; std::cin >> product; // 假设你的流已绑定SQL语句句柄
内容的提问来源于stack exchange,提问作者IjonTichy
相关产品推荐
相关产品推荐

