是否可以编写一个c ++模板函数,它接受不同类型的可变数量的输入变量(输入的数量可以限制为10)?
例如,使用函数sql_exec()
执行sql查询字符串并将结果行保存在所提供类型的std向量中,即
std::vector<double> x,y;
std::vector<std::string> s;
std::string query="select * from ...";
sql_exec(query, s,x,y); // error if less than 3 rows or conversion not possible
现在我的天真方法(仅限于最多2个向量)
struct null_type {};
template <typename T1=null_type, typename T2=null_type>
void sql_query(const std::string& query_str, std::vector<T1>& col1,
std::vector<T2>& col2) {
...
}
当然这是愚蠢的,因为我没有告诉函数有关默认参数,我们得到
error: default template arguments may not be used in function templates
但实际上它使用gcc和-std=c++0x
进行编译。但是,显然sql_query()
仍然不接受可变长度输入,需要使用2个向量调用。此外,我想在大多数当前的编译器上都有可移植的东西。我忽略了什么明显的东西?我知道我可以改变设计,也许可以使用boost::tuple
或其他东西,但我喜欢这么简单的界面。
答案 0 :(得分:6)
在C ++ 0x中,这是通过可变参数模板实现的(并且参数的数量可以变得很大,限制是特定于实现的。)
在C ++ 03中,这是通过让预处理器宏生成各种arity的模板函数来模拟的(参见Boost.Preprocessor)。
我使用C ++ 03技术从1到10个参数生成“绑定”,它运行得很好。
答案 1 :(得分:5)
如上所述,如果C ++ 0x不可用,Boost.Preprocessor就是可行的方法,尽管需要一段时间才能习惯语法。下面的示例演示了Boost.Preprocessor可用于定义具有可变(但有限)数量的参数的函数的方式。
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#define MAX_PARAMS 2
class sql {
public:
// definition of the function in macro form
#define SQL_QUERY_DEF(z, n, unused) \
template <BOOST_PP_ENUM_PARAMS(n, class T)> \
void query(const std::string& query, \
BOOST_PP_ENUM_BINARY_PARAMS(n, const T, & x) );
// does the actual code replication of SQL_QUERY_DEF
#define BOOST_PP_LOCAL_MACRO(n) SQL_QUERY_DEF(~, n, ~)
#define BOOST_PP_LOCAL_LIMITS (1, MAX_PARAMS)
#include BOOST_PP_LOCAL_ITERATE()
...
};
// two helper functions:
// expands to var0.clear(); var1.clear(); ...
#define SQL_VECTOR_CLEAR(z,i,var) var##i.clear();
// expands to var0.push_back(this->get_col<T0>(0); ...
#define SQL_VECTOR_PUSH_BACK(z,i,var) var##i.push_back(this->get_col<T##i>(i));
// definition of the function in macro form
#define SQL_QUERY(z, n, unused) \
template <BOOST_PP_ENUM_PARAMS(n, class T)> \
void sql::query(const std::string& query, \
BOOST_PP_ENUM_BINARY_PARAMS(n, std::vector< T,>& x) ){ \
this->do_query(query); \
if(this->num_cols()<n){ \
throw std::runtime_error(); \
} \
BOOST_PP_REPEAT(n, SQL_VECTOR_CLEAR, x) \
while(this->is_open()) { \
BOOST_PP_REPEAT(n, SQL_VECTOR_PUSH_BACK, x) \
this->step(); \
} \
}
// does the actual code replication of SQL_QUERY
#define BOOST_PP_LOCAL_MACRO(n) SQL_QUERY(~, n, ~)
#define BOOST_PP_LOCAL_LIMITS (1, MAX_PARAMS)
#include BOOST_PP_LOCAL_ITERATE()
预处理器将其扩展为:
$ g++ -P -E sql.cpp | astyle
class sql {
public:
template < class T0> void query(const std::string& query, const T0 & x0 );
template < class T0 , class T1> void query(const std::string& query, const T0 & x0 , const T1 & x1 );
...
};
template < class T0> void sql::query(const std::string& query, std::vector< T0 >& x0 ) {
this->do_query(query);
if(this->num_cols()<1) {
throw std::runtime_error();
}
x0.clear();
while(this->is_open()) {
x0.push_back(this->get_col<T0>(0));
this->step();
}
}
template < class T0 , class T1> void sql::query(const std::string& query, std::vector< T0 >& x0 , std::vector< T1 >& x1 ) {
this->do_query(query);
if(this->num_cols()<2) {
throw std::runtime_error();
}
x0.clear();
x1.clear();
while(this->is_open()) {
x0.push_back(this->get_col<T0>(0));
x1.push_back(this->get_col<T1>(1));
this->step();
}
}
注意,这里我们不能使用BOOST_PP_REPEAT(MAX_PARAMS, SQL_QUERY, ~)
,因为它以0参数开始复制,但我们需要从1开始,这就是为什么BOOST_PP_LOCAL_ITERATE()
需要更灵活的原因。