У меня есть класс, обрабатывающий запросы SQL (он использует функции Qt, но я думаю, что это не имеет значения). Все запросы, которые записывают данные, имеют точно такой же базовый фрейм, который выглядит следующим образом:
bool Database::someSqlFunction(some variables)
{
if (! startTransaction()) {
return false;
}
QSqlQuery query(m_db);
try {
... Code using the passed variables and query ...
commitTransaction();
} catch (...) {
rollbackTransaction(query);
return false;
}
return true;
}
Можно ли повторно использовать этот код, чтобы его не нужно было определять для каждой функции? Я думал об использовании функции, которая будет вызываться с указателем на функцию, содержащую изменяющийся код, но сигнатура для каждой функции различна, и поэтому мне пришлось бы определять перегруженную для каждого случая;Я также думал об использовании препроцессора для генерации функции, но это та же проблема с другим числом и типом аргументов.
Может ли блок кода, который должен быть выполнен, передаваться другой функции? Или это можно сделать с помощью шаблона функции?
Редактировать: Вот как это можно реализовать:
В заголовке:
template<typename SqlFunction>
bool writeHelper(SqlFunction sqlFunction)
{
if (! startTransaction()) {
return false;
}
QSqlQuery query(m_db);
try {
sqlFunction(query);
commitTransaction();
} catch (...) {
rollbackTransaction(query);
return false;
}
return true;
}
И пример функции, использующей его:
bool Database::registerPlayers(const QString &name, int markerId)
{
return writeHelper([&](QSqlQuery &query) {
queryPrepare(query, QStringLiteral("INSERT INTO players(id, name, marker) "
"VALUES(NULL, ?, ?)"));
query.bindValue(0, name);
query.bindValue(1, markerId != 0 ? markerId : SQLITE_NULL);
queryExec(query);
});
}
Редактировать 2: То же самое может быть достигнуто без шаблонов:
Используя std::function
, лямбда, определенная вФактическая функция может быть просто передана без использования шаблонов. Тогда реализация вспомогательной функции выглядит следующим образом:
bool Database::writeHelper(std::function<void(QSqlQuery &query)> sqlFunction)
{
if (! startTransaction()) {
return false;
}
QSqlQuery query(m_db);
try {
sqlFunction(query);
commitTransaction();
} catch (...) {
rollbackTransaction(query);
return false;
}
return true;
}
В любом случае, очевидно, лучше использовать шаблонный подход, так как в этом случае компилятор сгенерирует необходимые функции во время сборки и сможет оптимизировать,тогда как он не знает, что на самом деле будет сделано с использованием подхода std :: function, так как вызовы происходят во время выполнения.