Image

Imagemarrkiz wrote in Imageru_cpp

темплейт функции с произвольным числом аргуметнов

Есть следующая задача - сделать в С++ функцию-член класса, споcобную принимать произвольное количество аргументов (не более некоторого небольшого числа, в примере под катом - 4) разных типов. Нужно для привязывания значений к SQLite statement'ам, (но на SQLite можно не обращать внимания, вопрос чисто по C++, просто в абстрактный пример не хочется переделывать). Вот пока получается такой набросок:



#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'ы с константными условиями выкидываются, все как надо. Но что-то мне тут не нравится, наверное не хочется зависеть сильно от оптимизатора. Как этот пример можно улучшить?