темплейт функции с произвольным числом аргуметнов
Есть следующая задача - сделать в С++ функцию-член класса, споcобную принимать произвольное количество аргументов (не более некоторого небольшого числа, в примере под катом - 4) разных типов. Нужно для привязывания значений к SQLite statement'ам, (но на SQLite можно не обращать внимания, вопрос чисто по C++, просто в абстрактный пример не хочется переделывать). Вот пока получается такой набросок:
Ключевой момент - ф-я bindvalues_impl
Оптимизатор понимает, что если тип аргумента T2 (или T3, T4) - NullType, то после следующего условия все, что есть в функции можно выкинуть:
Проверял сгенерированный код на VC7.1 - в Release всё хорошо инлайнится, if'ы с константными условиями выкидываются, все как надо. Но что-то мне тут не нравится, наверное не хочется зависеть сильно от оптимизатора. Как этот пример можно улучшить?
#include <string>
#include <sqlite3.h>
class Cursor
{
private:
class NullType
{
public:
NullType() { }
};
template <typename T>
struct ArgTraits
{
enum { stop_arg = 0 };
};
template<>
struct ArgTraits<NullType>
{
enum { stop_arg = 1 };
};
int bind(sqlite3_stmt *stmt, int narg, NullType arg)
{
return 0;
}
int bind(sqlite3_stmt *stmt, int narg, int arg)
{
return sqlite3_bind_int(stmt, narg, arg);
}
int bind(sqlite3_stmt *stmt, int narg, double arg)
{
return sqlite3_bind_double(stmt, narg, arg);
}
int bind(sqlite3_stmt *stmt, int narg, __int64 arg)
{
return sqlite3_bind_int64(stmt, narg, arg);
}
int bind(sqlite3_stmt *stmt, int narg, const std::string& arg)
{
return sqlite3_bind_text(stmt, narg, arg.c_str(), -1, SQLITE_TRANSIENT);
}
int bind(sqlite3_stmt* stmt, int narg, const char *arg)
{
return sqlite3_bind_text(stmt, narg, arg, -1, SQLITE_TRANSIENT);
}
template<
typename T1,
typename T2,
typename T3,
typename T4
>
void bind_values_impl(sqlite3_stmt* stmt, T1 a1, T2 a2, T3 a3, T4 a4)
{
if (ArgTraits<T1>::stop_arg)
return;
if (bind(stmt, 1, a1) != SQLITE_OK)
return;
if (ArgTraits<T2>::stop_arg)
return;
if (bind(stmt, 2, a2) != SQLITE_OK)
return;
if (ArgTraits<T3>::stop_arg)
return;
if (bind(stmt, 3, a3) != SQLITE_OK)
return;
if (ArgTraits<T4>::stop_arg)
return;
if (bind(stmt, 4, a4) != SQLITE_OK)
return;
}
public:
template <typename T1>
void bindvalues(sqlite3_stmt* stmt, T1 a1)
{
bind_values_impl(stmt, a1, NullType(), NullType(), NullType());
}
template <typename T1, typename T2>
void bindvalues(sqlite3_stmt* stmt, T1 a1, T2 a2)
{
bind_values_impl(stmt, a1, a2, NullType(), NullType());
}
template <typename T1, typename T2, typename T3>
void bindvalues(sqlite3_stmt* stmt, T1 a1, T2 a2, T3 a3)
{
bind_values_impl(stmt, a1, a2, a3, NullType());
}
template <typename T1, typename T2, typename T3, typename T4>
void bindvalues(sqlite3_stmt* stmt, T1 a1, T2 a2, T3 a3, T4 a4)
{
bind_values_impl(stmt, a1, a2, a3, a4);
}
void f()
{
sqlite3_stmt* stmt = 0;
bindvalues(stmt, 1);
bindvalues(stmt, 0.0);
bindvalues(stmt, 1, 0.0, "hello");
}
};
void a()
{
Cursor c;
c.f();
}
Ключевой момент - ф-я bindvalues_impl
Оптимизатор понимает, что если тип аргумента T2 (или T3, T4) - NullType, то после следующего условия все, что есть в функции можно выкинуть:
if (ArgTraits<T2>::stop_arg)
return;
Проверял сгенерированный код на VC7.1 - в Release всё хорошо инлайнится, if'ы с константными условиями выкидываются, все как надо. Но что-то мне тут не нравится, наверное не хочется зависеть сильно от оптимизатора. Как этот пример можно улучшить?
