Hap*_*tus 7 c++ variadic-templates c++11 c++14
我正在开发一个处理非类型C函数(SQLite)的库,我想强力输入它.
我们的想法是拥有一个FieldDef强类型,允许用户将原始类型(如int,double和std :: string)绑定到弱db类型.我的问题是库的语义很重,我想添加一些自动类型推导.
所以我有一堆"基本类型":
namespace FieldType {
struct Integer { using rawtype = int; };
struct Real{ using rawtype = double; };
struct Text{ using rawtype = std::string; };
struct Blob{ using rawtype = std::vector<uint8_t>; };
}
Run Code Online (Sandbox Code Playgroud)
我还有一个insert和一个query函数,允许插入和查询表而不使用SQL语句.查询将是纯选择.无论如何.预期用途是:
FieldDef<FieldType::Integer> mId = makeFieldDef("id", FieldType::Integer()).primaryKey().autoincrement();
FieldDef<FieldType::Text> mName = makeFieldDef("name", FieldType::Text());
FieldDef<FieldType::Integer> mValue = makeFieldDef("value", FieldType::Integer());
SQLiteTable::insert(std::make_tuple(mName, mValue), std::make_tuple(record.name, record.value));
std::vector<Record> r;
SQLiteTable::query
(std::make_tuple(mName, mValue), [&r](std::tuple<std::string, int> res) {
r.push_back(Record{std::get<0>(res), std::get<1>(res)});
});
Run Code Online (Sandbox Code Playgroud)
我用这种方式实现了插入:
template <typename ...Ts, typename ...Us>
bool insert (std::tuple<Ts...> def, std::tuple<Us...> values) {
std::ostringstream ss;
ss << "INSERT INTO " << mName << "("
<< buildSqlInsertFieldList<0>(def)
<< ") VALUES ("
<< buildSqlInsertValuesListPlaceholder<0>(values)
<< ");";
auto stmt = newStatement(ss.str());
bindAllValues<0>(stmt.get(), values);
return execute(stmt.get());
}
Run Code Online (Sandbox Code Playgroud)
这很好用,问题来自查询:
template <typename ...Ts, typename ...Us>
void query(std::tuple<Ts...> def, std::function<void(std::tuple<Us...>)> resultFeedbackFunc) {
...
}
Run Code Online (Sandbox Code Playgroud)
在调用它时,编译器无法正确推断出类型,所以我猜它需要一个迂腐的构造:
SQLiteTable::query<FieldType::Text, FieldType::Integer, /* whatever */> (...)
Run Code Online (Sandbox Code Playgroud)
这是不切实际和冗长的.
是否可以简化查询功能?由于我们对使用有约束,那就是Us包只能是某种类型的兼容FieldType::*:rawtype,我问是否可以使用一些解包并应用方法的构造.如果是insert,可以用以下内容简化:
template<typename Ts...>
bool insert (std::tuple<Ts...> def, std::tuple<Ts::rawtype ...> values)
Run Code Online (Sandbox Code Playgroud)使用Variadic Packs而不是使用元组?我没有测试它,但我担心使用类似的东西
template<typename Ts..., typename Us....>
bool insert (Ts... def, Us ... values)
Run Code Online (Sandbox Code Playgroud)会混淆编译器,会使事情变得更糟.你怎么看?
以下是有关代码的一些细节,以解释:
使用以下伪代码实现查询功能:
template <typename ...Ts, typename ...Us>
void query(std::tuple<Ts...> def, std::function<void(std::tuple<Us...>)> resultFeedbackFunc) {
std::ostringstream ss;
ss << "SELECT " << buildSqlInsertFieldList<0>(def) << " FROM " << mName <<";";
auto stmt = newStatement(ss.str());
auto r = execute(stmt.get());
SQLiteException::throwIfNotOk(r, db()->handle());
while (hasData(stmt.get())) {
auto nColumns = columnCount(stmt.get());
if (nColumns != sizeof...(Ts))
throw std::runtime_error("Column count differs from data size");
std::tuple<Us...> res;
getAllValues<0>(stmt.get(), res);
resultFeedbackFunc(res);
}
};
Run Code Online (Sandbox Code Playgroud)
Statement是隐藏的不透明类型sqlite语句结构,因为这样做在所使用的其他功能query的方法newStatement,execute和columnsCount.该函数getAllValues使用递归来填充tuple.因此,resultFeedbackFunc()将为数据库的每一行调用仿函数.因此,客户端代码可以例如填充容器(如矢量).
更新:
我关注了@ bolov的解决方案,并添加了@ massimiliano-jones的改进.
这是对反馈函数的内部调用的正确实现:
resultFeedbackFunc(getValueR<decltype (std::get<Is>(def).rawType())>
(stmt.get(), Is)...);
Run Code Online (Sandbox Code Playgroud)
getValueR进行内部调用sqlite_column_xxx(sqlite3_stmt *, int index).如果我理解正确,解包是有效的,因为参数列表是解包的有效上下文.如果我希望在参数之外进行调用,我必须进行折叠(或者因为我使用的是c ++ 11).
由于您的帖子中缺少重要的代码部分,因此很难提供具体的帮助.
不过这是我的2美分.请注意我的想象力充满了代码中缺少的部分.
首先,你需要摆脱这个std::function论点.使用std::function仅当您需要的类型擦除它提供.在您的情况下(至少从您显示的代码中)您不需要它.所以我们用一个简单的template <class F>参数替换它.这解决了演绎问题.
现在,当您传递一个无效的函数对象时,您将在query实现的深层中获得编译错误.如果您不想这样,并且想要快速失败,那么有一些选择.我选择向您展示SFINAE方法decltype.
namespace FieldType
{
struct Integer { using rawtype = int; };
struct Real{ using rawtype = double; };
struct Text{ using rawtype = std::string; };
};
template <class FT>
struct FieldDef
{
using Type = FT;
using RawTye = typename Type::rawtype;
auto getRaw() -> RawTye { return {}; }
};
template <class... Args, class F, std::size_t... Is>
auto query_impl(std::tuple<Args...> def, F f, std::index_sequence<Is...>)
-> decltype(f(std::get<Is>(def).getRaw()...), std::declval<void>())
{
f(std::get<Is>(def).getRaw()...);
}
template <class... Args, class F>
auto query(std::tuple<Args...> def, F f)
-> decltype(query_impl(def, f, std::make_index_sequence<sizeof...(Args)>{}))
{
query_impl(def, f, std::make_index_sequence<sizeof...(Args)>{});
}
Run Code Online (Sandbox Code Playgroud)
auto test()
{
FieldDef<FieldType::Text> mName = {};
FieldDef<FieldType::Integer> mValue = {};
query(std::make_tuple(mName, mValue), [](std::string, int) {}); // OK
query(std::make_tuple(mName, mValue), [](std::string, int, int) {}); // Error
query(std::make_tuple(mName, mValue), [](int, int) {}); // Error
}
Run Code Online (Sandbox Code Playgroud)
无效调用失败,并显示类似于以下内容的消息:
Run Code Online (Sandbox Code Playgroud)error: no matching function for call to 'query' ... note: candidate template ignored: substitution failure [with Args = ...]: no matching function for call to 'query_impl' ...
关于你的观点2.那是不可扣除的.即使是这样,您也希望将参数分组以便于阅读.即你想要insert({a, b}, {c, d})而不是insert(a, b, c, d).
我不明白你的观点3.
| 归档时间: |
|
| 查看次数: |
243 次 |
| 最近记录: |