使用 Variadic 模板(或类似机制)将类型列表传递给函数
Use Variadic templates (or a similar mechanism) to pass list of types to a function
我想为数据库实现一个 class 包装器。
目前,我正在研究 createTable 函数。
我试图让它工作的方式是,用户指定类型
作为模板参数,列名作为初始化列表,
这是函数的模板:
template <typename ... Ts>
bool createTable(const std::string & tableName, const std::initializer_list<std::string> & columnNames);
这是方法的主体:
template<typename ... Ts>
bool DatabaseConnection::createTable(const std::string &tableName, const std::initializer_list<std::string> & columnNames)
{
constexpr size_t num_cols = sizeof...(Ts);
assert(num_cols == columnNames.size());
auto typetuple = std::tuple<Ts...>();
std::vector<std::tuple<std::string, std::string>> columnNameAndType(num_cols);
auto columnNameIterator = columnNames.begin();
for(unsigned it = 0; it++ < columnNames.size(); it++){
typedef std::tuple_element<it, typetuple>::type c; // non-type template argument is not a constant expression
if(is_same<c, int> ...) //pseudocode
std::string dbtype = "INTEGER"; //pseudocode
}
}
遗憾的是,tuple_element 行不起作用,因为它不是真正的
常量表达式。
现在,有人可能会问,为什么我要这样称呼它:
createTable<int, std::string>("Users", {"ID", "Name"});
而不是只传递两个初始化列表?
好吧,我只想让用户远离界面——如果我能够确定
it-h 类型我可以只使用 decltype 或 is_same 之类的东西来确定数据库创建查询中使用的类型——用户只需说 he/she 想要什么类型和数据库 class
确定匹配用户请求的最佳数据库类型。
现在,它仍然可以用初始化列表来制作,但它不会是编译时间,并且
我只是想知道是否可以在完整的时间完成。
希望我对问题的解释足够了。
当然这多半是理论上的问题,但我想很多人
对这样的语法很感兴趣,我还没有在互联网上找到任何解决方案。
这个接口当然可以
一个for
循环是做不到的,因为一个statement/variable/expression/etc。 for
子语句的不同评估不能有不同的类型。循环将需要通过包扩展来代替。
一个或多个私有助手成员函数可以帮助解决这个问题。使用通用 lambda 可以在一个函数定义中获得所有内容,但有点不愉快。
// private static
template <typename T>
std::string DatabaseConnection::dbTypeName()
{
if constexpr (std::is_same_v<T, int>)
return "INTEGER";
// ...
else
static_assert(!std::is_same_v<T,T>, "Unsupported type argument");
}
template<typename ... Ts>
bool DatabaseConnection::createTable(
const std::string &tableName,
std::initializer_list<std::string> columnNames)
{
constexpr size_t num_cols = sizeof...(Ts);
assert(num_cols == columnNames.size());
std::vector<std::tuple<std::string, std::string>> columnNameAndType;
auto columnNameIterator = columnNames.begin();
(columnNameAndType.emplace_back(*columnNameIterator++, dbTypeName<Ts>()), ...);
// ...
}
我想为数据库实现一个 class 包装器。
目前,我正在研究 createTable 函数。
我试图让它工作的方式是,用户指定类型
作为模板参数,列名作为初始化列表,
这是函数的模板:
template <typename ... Ts>
bool createTable(const std::string & tableName, const std::initializer_list<std::string> & columnNames);
这是方法的主体:
template<typename ... Ts>
bool DatabaseConnection::createTable(const std::string &tableName, const std::initializer_list<std::string> & columnNames)
{
constexpr size_t num_cols = sizeof...(Ts);
assert(num_cols == columnNames.size());
auto typetuple = std::tuple<Ts...>();
std::vector<std::tuple<std::string, std::string>> columnNameAndType(num_cols);
auto columnNameIterator = columnNames.begin();
for(unsigned it = 0; it++ < columnNames.size(); it++){
typedef std::tuple_element<it, typetuple>::type c; // non-type template argument is not a constant expression
if(is_same<c, int> ...) //pseudocode
std::string dbtype = "INTEGER"; //pseudocode
}
}
遗憾的是,tuple_element 行不起作用,因为它不是真正的
常量表达式。
现在,有人可能会问,为什么我要这样称呼它:
createTable<int, std::string>("Users", {"ID", "Name"});
而不是只传递两个初始化列表?
好吧,我只想让用户远离界面——如果我能够确定 it-h 类型我可以只使用 decltype 或 is_same 之类的东西来确定数据库创建查询中使用的类型——用户只需说 he/she 想要什么类型和数据库 class 确定匹配用户请求的最佳数据库类型。
现在,它仍然可以用初始化列表来制作,但它不会是编译时间,并且 我只是想知道是否可以在完整的时间完成。
希望我对问题的解释足够了。
当然这多半是理论上的问题,但我想很多人
对这样的语法很感兴趣,我还没有在互联网上找到任何解决方案。
这个接口当然可以
一个for
循环是做不到的,因为一个statement/variable/expression/etc。 for
子语句的不同评估不能有不同的类型。循环将需要通过包扩展来代替。
一个或多个私有助手成员函数可以帮助解决这个问题。使用通用 lambda 可以在一个函数定义中获得所有内容,但有点不愉快。
// private static
template <typename T>
std::string DatabaseConnection::dbTypeName()
{
if constexpr (std::is_same_v<T, int>)
return "INTEGER";
// ...
else
static_assert(!std::is_same_v<T,T>, "Unsupported type argument");
}
template<typename ... Ts>
bool DatabaseConnection::createTable(
const std::string &tableName,
std::initializer_list<std::string> columnNames)
{
constexpr size_t num_cols = sizeof...(Ts);
assert(num_cols == columnNames.size());
std::vector<std::tuple<std::string, std::string>> columnNameAndType;
auto columnNameIterator = columnNames.begin();
(columnNameAndType.emplace_back(*columnNameIterator++, dbTypeName<Ts>()), ...);
// ...
}