使用可变参数模板函数包装基于省略号的函数

时间:2017-11-04 23:07:22

标签: c++ c++11 c++14 variadic-templates libpqxx

我正在尝试从外部库(libpqxx)中包装函数“prepared(.)”。所述函数接受可变数量的参数。这是通过连续使用多个operator()来实现的。一operator() pr。论证,如:

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();

我试图通过使用可变参数模板函数来包装最后一个函数,如下所示:

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

......但它不起作用。代码编译,但我得到一个运行时错误,说参数的数量与给定prepared-sql语句的预期参数数量不匹配。

有人可以澄清如何使用可变参数模板完成此类函数的包装吗?谢谢!

3 个答案:

答案 0 :(得分:1)

如果包args代表三个参数,那么

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

扩展为

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

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

你需要的。

看起来你需要一个辅助函数,它一次递归地应用一个参数:

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();
    }

答案 1 :(得分:0)

Aschepler向您解释了您的实际代码有什么问题。

不确定libpqxx库是如何工作的,但我可以尝试一种有根据的猜测并提出一个非常简单的解决方案。

如下代码(抱歉:未经测试)怎么办? (并由aschepler更正;谢谢!)

template <typename ... Ts>
pqxx::result exec_prepared (std::string const & name,
                            Ts const & ... args) const
 {
   using unused = int[];

   auto p = w.prepared(name);

   (void)unused { 0, (p = p(args), 0)... };

   return p.exec();
 }

如果你可以使用C ++ 17,我想你可以简化(避免使用未使用的技巧)如下(再次对不起:未经测试)

pqxx::result exec_prepared (std::string const & name,
                            Ts const & ... args) const
 {
   auto p = w.prepared(name);

   (p = p(args), ...);

   return p.exec();
 }

答案 2 :(得分:0)

我会用折叠来做这件事。

template<class F>
struct invoke_by_times_t;
template<class F>
invoke_by_times_t<F> invoke_by_times(F&&)

template<class F>
struct invoke_by_times_t {
  F f;
  template<class Rhs>
  auto operator*(Rhs&&rhs)&&{
    return invoke_by_times( std::forward<F>(f)(std::forward<Rhs>(rhs)) );
  }
};

让我们这样做:

w.prepared("update_person")("Jack")(1)(0).exec();

(invoke_by_times([&](auto&&x){ return w.prepared(x); })*("update_person")*("Jack")*(1)*(0)).f.exec();

现在这不是那么有用,但是使用C ++ 17这是args的简单折叠!

中,我们可以编写产品:

template<class A1, class A2>
delctype(auto) product(A1&& a1, A2&& a2){
  return std::forward<A1>(a1)*std::forward<A2>(a2);
}
template<class A1, class A2, class...Args>
delctype(auto) product(A1&& a1, A2&& a2, Args&&...args){
  return product(std::forward<A1>(a1)*std::forward<A2>(a2), std::forward<Args>(args)...);
}

给我们:

product(invoke_by_times([&](auto&&x){ return w.prepared(x); }), "update_person", "Jack", 1, 0).f.exec();