2011-06-24 9 views
6

Actualmente estoy escribiendo una función que tomará una cantidad variable de argumentos. Paso el número de argumentos en la función y luego iteraré a través de la lista de argumentos.Verificando los argumentos variables son del tipo esperado

Cada uno de los argumentos pasados ​​debe ser un número entero. Agregaré este entero a un vector de números enteros que se usarán más adelante.

Me gustaría asegurarme de que algún comodín no intente pasar esta función a otra cosa que no sea un número entero en el futuro. Reconozco que puedo verificar el argumento actual de va_arg para asegurarme de que no es NULL y puedo usar algo como isanum (va_arg()) para determinar si es un entero válido. Supongo que podría incluso verificar el tamaño de (va_arg) y compararlo con el tamaño de (int) y asegurar que sean iguales.

¿Hay algún otro control que pueda ejecutar para verificar que se me haya pasado un número entero válido?

Gracias de antemano por la ayuda

+0

Qué compilador está usando? – Xeo

+0

** Muy buena ** pregunta!+1 –

+0

gcc puede hacer verificaciones de cordialidad en los argumentos 'printf', y estoy bastante seguro de que hay una forma de extender ese comportamiento a las funciones personalizadas. Obviamente, no es extremadamente portable, pero si usted está usando gcc de todos modos, puede valer la pena investigarlo. –

Respuesta

8

No hay forma sensata de hacerlo. Las funciones de argumento variable funcionan al concatenar todas las representaciones binarias en bruto de los argumentos en una gran porción de datos en la pila. Por lo tanto, depende tanto de la persona que llama como de la persona que llama para acordar el número y tipo de argumentos (de lo contrario, terminará leyendo, por ejemplo, un int como si fuera un float).

En cuanto a sus ideas específicas:

  • va_arg() es una macro que simplemente interpreta un número de bytes de los datos de la pila primas como cualquier tipo que especifique. Por lo tanto, invocar sizeof() en él simplemente le dirá el tamaño del tipo de datos que solicitó.

  • En general, no hay patrones de datos binarios sin procesar que formen un entero no válido. Por lo tanto, el hipotético isanum() no podría funcionar.

+0

Si bien agradezco la opinión de todos, usted respondió directamente mi pregunta. Gracias. – BSchlinker

1

argumentos variables son inseguros por diseño. No puede verificar que el usuario haya pasado el tipo correcto de ninguna manera. C++ 0x viene al rescate con plantillas variadas pero no muchos compiladores lo soportan hoy en día (solo GCC afaik).

+0

Las plantillas variables no son necesarias si todos los argumentos son del mismo tipo. Una 'initializer_list ' es suficiente en ese caso. – fredoverflow

4

Cada uno de los argumentos pasados ​​debe ser un entero.

Si usted tiene un compilador de C++ 0x, que sugieren una initializer_list<int> en lugar de varargs:

#include <initializer_list> 

void foo(std::initializer_list<int> numbers) 
{ 
    my_vector.insert(my_vector.end(), numbers.begin(), numbers.end()); 
} 

int main() 
{ 
    foo({2, 3, 5, 7}); 
} 

Esta es directa y completamente de tipo seguro.

+0

Lamentablemente, mi entorno aquí no tiene un compilador C++ 0x, por lo que no puedo usar este método. Aunque es bueno saberlo, así que gracias. – BSchlinker

+0

Puede obtener algo parecido a esto usando boost :: assign. –

1

Lamentablemente, realmente no hay una manera de hacer esto. Las funciones como printf() pueden activarse fácilmente pasando el número de argumentos incorrecto o incorrecto.

En C++, esta es una función avanzada que requiere la programación con dicho código para garantizar que se pasen los argumentos correctos.

3

Cada uno de los argumentos pasados ​​debe ser entero.Voy a agregar este entero a un vector de números enteros que se utilizará más adelante.

¿Por qué no aceptar un vector de números enteros?

void AddIntegers(const std::vector<int>& vec); 

Siempre puede concatenar vectores juntos utilizando iteradores.

O crea un interfaz de la siguiente manera:

void AddInteger(int newInt); 

O incluso esto:

void AddIntegers(const int* integers, unsigned int numIntegers); 

template <unsigned int Size> 
void AddIntegers(int (&integers)[Size]) 
{ 
    AddIntegers(integers, Size); 
} 

int main() 
{ 
    int i[] = {1, 2, 3, 4}; 
    AddIntegers(i); 
} 

Estos funcionará si es necesario trabajar con un compilador de C++ 03. Si tiene un compilador C++ 0x, hay soluciones muy superiores disponibles.

1

No se puede hacer ningún tipo de comprobación de tipo con varargs. Sugeriría utilizar un rango de iterador en su lugar (como funciones de biblioteca estándar) o posiblemente un std::vector<int>. De esta forma los tipos no pueden ser subvertidos.

0

Dado que está utilizando C++, ¿qué tal sobrecargar a un operador y pasar los argumentos uno a uno? Por ejemplo

class MyFunction { 
    std::vector<int> param; 
    public: 
    MyFunction() { /* some initialisation? */ } 
    MyFunction &operator,(int eatMe) { 
    param.push_back(eatMe); 
    return *this; 
    } 
    ~MyFunction() { 
    //the implementation of your function goes here 
    } 
} 

Entonces se le puede llamar así:

MyFunction(),2,3,5,7; 

Nota, el uso del operador coma puede dar miedo, pero en realidad es muy útil en este caso. Es el operador asociativo de izquierda más bajo posible.

Si su función toma algunos parámetros adicionales, no solo la longitud desconocida de int-s, puede pasarlos en el constructor.

Si alguien usa algo más que int, se usará el operador de coma predeterminado (evaluar el lado izquierdo, descartar, evaluar el lado derecho). Si no te gusta, elige un operador diferente, p. stream-like << o boost-like %.

+1

Normalmente utilizo 'operator()' para esto, y también le permito al constructor aceptar el "primer" parámetro, para que pueda escribir, p. MyFunction (2) (3) (5) (7). El truco del destructor es limpio, y lo he usado, pero a veces es más útil usar una llamada explícita. –

0

Si está restringido a C++ 03 y todos sus argumentos deben ser enteros, una solución sería simplemente ocultar la función de argumento variable (en un espacio de nombres de "detalles" por ejemplo) y crear una serie de funciones sobrecargadas para 1 a N cantidad de argumentos. Esas funciones serían simples funciones en línea que reenvía la llamada a la versión vararg de la función real. De esta forma, tiene una implementación real, sin sobrecarga en tiempo de ejecución, y expone una interfaz de tipo seguro a la persona que llama (y la persona que llama siempre puede usar la versión vararg si necesita más de N argumentos).

Boost.PP también puede ayudar a generar estos tipos de patrones repetitivos.

Por supuesto, si tiene algún nivel de compatibilidad con C++ 0x, entonces el problema se puede resolver de muchas maneras, incluyendo initializer_list o plantillas variadic.

0

Sólo para ilustrar mi comentario sobre la respuesta de CygnusX1, podría hacerlo como:

class MyFunction { 
    std::vector<int> params; 
    public: 
    MyFunction() { (*this)(); } 
    MyFunction(int eatMe) { (*this)(eatMe); } 
    MyFunction& operator()(int eatMe) { 
    params.push_back(eatMe); 
    return *this; 
    } 
    void operator()() { 
    // use params to do something interesting 
    } 
} 

MyFunction(2)(3)(5)(7)(); 
Cuestiones relacionadas