c++ template functions with variable arguments
Is it possible to write a c++ template function which takes a variable number of input variables of different types (number of input can be limited to say 10)?
For example take a function sql_exec()
which executes an sql query string and saves the resulting rows in std vectors of the type supplied, i.e.
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
Now my naive approach would have been (limited to max 2 vectors)
struct null_type {};
template <typename T1=null_type, typename T2=null_type>
void sql_query(const std::string& query_str, std::vector<T1>& col1,
开发者_JAVA百科 std::vector<T2>& col2) {
...
}
Of course that's stupid as I didn't tell the function about default arguments and we get
error: default template arguments may not be used in function templates
but actually it compiles with gcc and -std=c++0x
. However, obviously sql_query()
still doesn't take variable length input and needs to be called with 2 vectors. Also, I'd like to have something portable working on most of the current compilers. Anything obvious I've overlooked? I know I can change the design and maybe use boost::tuple
or something else but I'd have liked such a simple interface.
In C++0x this achieved through variadic templates (and the number of arguments can get huge, limit being implementation specific).
In C++03, this is emulated by having preprocessor macros generating lots of template functions of various arity (see Boost.Preprocessor).
I've used the C++03 technic to generate the "bind" from 1 to 10 arguments and it works pretty well.
As said above, Boost.Preprocessor is the way to go if C++0x is not available, although it takes a while to get used to the syntax. The example below demonstrate the way Boost.Preprocessor can be used to define functions with variable (but limited) number of arguments.
#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()
The preprocessor expands this to:
$ 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();
}
}
Note, here we can't use BOOST_PP_REPEAT(MAX_PARAMS, SQL_QUERY, ~)
as it starts replication with 0 parameters but we need to start with 1, that's why BOOST_PP_LOCAL_ITERATE()
is needed which is more flexible.
精彩评论