5

¿Es posible escribir una función de plantilla de C++ que tome un número variable de variables de entrada de diferentes tipos (el número de entrada puede limitarse a decir 10)? Por ejemplo tomar una función sql_exec() que ejecuta una cadena de consulta SQL y guarda las filas resultantes en los vectores de enfermedades de transmisión sexual del tipo suministrado, es decirfunciones de plantillas de C++ con argumentos variables

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 

Ahora mi enfoque ingenuo habría sido (limitado a un máximo de 2 vectores)

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) { 
    ... 
} 

Por supuesto que es tonto como yo no le dije a la función sobre los argumentos por defecto y nos llevamos

error: default template arguments may not be used in function templates 

pero real l compila con gcc y -std=c++0x. Sin embargo, obviamente sql_query() todavía no toma entrada de longitud variable y necesita ser llamado con 2 vectores. Además, me gustaría tener algo portátil trabajando en la mayoría de los compiladores actuales. ¿Algo obvio que he pasado por alto? Sé que puedo cambiar el diseño y tal vez usar boost::tuple o algo más, pero me hubiera gustado una interfaz tan simple.

+1

como esta? http://en.wikipedia.org/wiki/C%2B%2B0x#Variadic_templates – andrewdski

+0

Sí, gracias. Sin embargo, estoy tratando de evitar C++ 0x y también la forma recursiva de definir la función dificultaría las cosas en este caso. Dado que estoy contento con un número máximo de entradas limitadas, ¿hay alguna otra manera? – tom

+0

es casi seguro que hay una forma de usar plantillas variadic con elegancia. Este tipo de pensamiento basado en plantillas toma un tiempo para acostumbrarse, pero es probable que sea mucho más simple que cualquier cosa que haya creado sin plantillas variadas. –

Respuesta

4

Como se dijo anteriormente, Boost.Preprocessor es el camino a seguir si C++ 0x no está disponible, aunque lleva un tiempo acostumbrarse a la sintaxis. El siguiente ejemplo demuestra la forma en que Boost.Preprocessor se puede usar para definir funciones con un número variable (pero limitado) de argumentos.

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

El preprocesador expande esto a:

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

Tenga en cuenta, que aquí no podemos usar BOOST_PP_REPEAT(MAX_PARAMS, SQL_QUERY, ~), ya que comienza la replicación con 0 parámetros, pero tenemos que comenzar con 1, es por eso que se necesita BOOST_PP_LOCAL_ITERATE() cuales es mas flexible

6

En C++ 0x esto se logra a través de plantillas variadic (y el número de argumentos puede ser enorme, el límite es específico de la implementación).

En C++ 03, esto se emula al tener macros de preprocesador que generan muchas funciones de plantilla de distinta aridad (consulte Boost.Preprocessor).

He utilizado la técnica C++ 03 para generar el "enlace" de 1 a 10 argumentos y funciona bastante bien.

Cuestiones relacionadas