私は非型C関数(SQLite)を扱うライブラリを開発しており、それを強くタイプしたいと思います。控除を使用したC++可変展開
考え方はint、double、std :: stringなどの生の型を弱いDB型に結びつけることを可能にする強い型のFieldDef
を持つことです。 私の問題は、ライブラリのセマンティックが非常に重いため、いくつかの自動タイプの控除を追加したいと思います。
だから私は、「基本タイプ」の束を持っている:
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>; };
}
私もinsert
を持っているし、SQLステートメントを使用せずにテーブルを挿入し、照会できるようquery
機能。クエリはプレーンセレクトです。とにかく。意図された使用は、次のとおりです。
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)});
});
私は、インサートをこのように実装:
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());
}
これは正常に動作し、問題は、クエリが付属しています:
template <typename ...Ts, typename ...Us>
void query(std::tuple<Ts...> def, std::function<void(std::tuple<Us...>)> resultFeedbackFunc) {
...
}
それを呼び出す場合、コンパイラはできませんタイプを正確に推論するために、それはペダンティックな構造が必要であると推測します:
SQLiteTable::query<FieldType::Text, FieldType::Integer, /* whatever */> (...)
実用的で冗長です。
クエリ機能を簡略化することは可能でしょうか?
Us
パックは、FieldType::*:rawtype
と互換性のあるタイプにしか使えないので、使用方法に制約があるため、解凍してメソッドを適用するいくつかのコンストラクトを使用できるかどうかを尋ねています。insert
の場合は、のようなもので簡単にすることができます。代わりにタプルを使用してのtemplate<typename Ts...> bool insert (std::tuple<Ts...> def, std::tuple<Ts::rawtype ...> values)
、どのような可変引数パックを使用してはどうですか?私はそれをテストしていませんが、私は、コンパイラを混乱させる
template<typename Ts..., typename Us....> bool insert (Ts... def, Us ... values)
ようなものを使用して、物事を悪化させるだろうと恐れています。 あなたはどう思いますか?
- 実際のクエリの実装を使用することができれば、使用法をより表現力豊かにするための回避策は何でしょうか?ここで
コードについてのいくつかの詳細を説明するために、以下のとおりです。
をクエリ機能は次の擬似コード使用して実装されています。
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);
}
};
Statement
するsqlite
を隠す不透明タイプでありますステートメント構造体であり、query
メソッドで使用される他の関数と同様に、newStatement
,execute
およびcolumnsCount
です。 getAllValues
関数は、再帰を使用してtuple
を入力します。したがって、ファンクタresultFeedbackFunc()
は、データベースのすべての行に対して呼び出されます。したがって、クライアントコードは、例えば、コンテナ(ベクトルのような)を満たすことができます。
更新:
私はbolovのソリューション@続き、@のマッシミリアーノ・ジョーンズの改善点を追加しました。
resultFeedbackFunc(getValueR<decltype (std::get<Is>(def).rawType())>
(stmt.get(), Is)...);
getValueR
sqlite_column_xxx(sqlite3_stmt *, int index)
内部呼び出しを行う:
これは、フィードバック機能に対する内側呼び出しの正しい実装です。私が正しく理解すれば、アンパックは、引数リストがアンパックのための有効なコンテキストであるために機能します。議論の外に呼び出しをしたい場合は、折りたたみ(または私はC++ 11を使用しているので回避策)をしなければなりませんでした。
も、プロジェクトはここで公開されていますhttps://github.com/studiofuga/mSqliteCpp – HappyCactus