Search code examples
c++c++11c++14variadic-templateslibpqxx

Wrapping ellipsis-based function with variadic template function


I am trying to wrap a function 'prepared(.)' from an external library (libpqxx). Said function accepts a variable number of arguments. This is achieved by using multiple operator() in succession. One operator() pr. argument, like so:

pqxx::connection connection(connection_str);
connection.prepare("update_person", "UPDATE person SET name = $1 WHERE id = $2 AND version = $3");
pqxx::work w(connection);

// The following executes prepared statement.
// The number of arguments is variable. Notice
// the syntax with multiple () in succession...
w.prepared("update_person")("Jack")(1)(0).exec();

I am trying to wrap the last function by using a variadic template function, like so:

template<typename... T>
pqxx::result exec_prepared(const std::string& name, const T&... args) const {
    return w.prepared(name)(args...).exec();
}

...but it does not work. The code compiles, but I get a runtime error saying that the number of arguments do not match the expected number of arguments given the prepared-sql-statement.

Could someone please clarify how wrapping of this type of function is done using variadic template? Thanks!


Solution

  • If the pack args represents three parameters, then

    return w.prepared(name)(args...).exec();
    

    expands to

    return w.prepared(name)(args0, args1, args2).exec();
    

    which is different from the

    return w.prepared(name)(args0)(args1)(args2).exec();
    

    which you need.

    It looks like you'll need a helper function which recursively applies one argument at a time:

    private:
        template <typename E>
        static E&& apply_prepared_args(E&& expr) {
            return std::forward<E>(expr);
        }
        template <typename E, typename First, typename... Rest>
        static decltype(auto) apply_prepared_args(
            E&& expr, const First& first, const Rest& ...rest) {
            return apply_prepared_args(std::forward<E>(expr)(first), rest...);
        }
    
    public:
        template<typename... T>
        pqxx::result exec_prepared(
            const std::string& name, const T&... args) const {
            return apply_prepared_args(w.prepared(name), args...).exec();
        }