2009-08-29 11 views
7

Soy más nuevo en C++, y mi primer idioma es el chino, por lo que mis palabras en inglés pueden ser irrelevantes, por ejemplo, lo siento primero. Sé que hay una forma de escribir una función con parámetros variables, cuyo número o tipo puede ser diferente para cada llamada, podemos usar las macros de va_list, va_start y va_end. Pero como todos saben, es el estilo C. Cuando usemos las macros, perderemos el beneficio de type-safe y auto-inference, entonces intento hacerlo con la plantilla de C++. Mi trabajo es seguido:función de parámetro variable, cómo hacer que el tipo sea seguro y más significativo?


#include<iostream> 
#include<vector> 
#include<boost/any.hpp> 

struct Argument 
{ 
    typedef boost::bad_any_cast bad_cast; 

    template<typename Type> 
    Argument& operator,(const Type& v) 
    { 
     boost::any a(v); 
     _args.push_back(a); 
     return *this; 
    } 

    size_t size() const 
    { 
     return _args.size(); 
    } 

    template<typename Type> 
    Type value(size_t n) const 
    { 
     return boost::any_cast<Type>(_args[n]); 
    } 

    template<typename Type> 
    const Type* piont(size_t n) const 
    { 
     return boost::any_cast<Type>(&_args[n]); 
    } 
private: 
    std::vector<boost::any> _args; 
}; 

int sum(const Argument& arg) 
{ 
    int sum=0; 
    for(size_t s=0; s<arg.size(); ++s) 
    { 
     sum += arg.value<int>(s); 
    } 

    return sum; 
} 

int main() 
{ 
    std::cout << sum((Argument(), 1, 3, 4, 5)) << std::endl; 

    return 0; 
} 

creo que es feo, quiero que hay una manera de hacerlo mejor? Gracias, y perdón por los errores de lenguaje.

+0

Qué función (s) se necesita para hacer que necesitan Var- args y tipo de seguridad? – strager

+0

es la cantidad de argumentos verdaderamente "cualquier cosa" o es algo práctico como "hasta 10"? –

+0

Si los valores son correctos en tiempo de compilación, puede usar algún tipo de tupla para almacenarlos. Algunos template-meta magic pueden hacer que el objeto tuple sea bastante conveniente de crear. Sin embargo, si el código resultante es menos feo es, erm, _subjective_. Pero definitivamente es más rápido en tiempo de ejecución. ':)' – sbi

Respuesta

3

se puede hacer algo como esto:

template <typename T> 
class sum{ 
    T value; 
    public: 
    sum() 
      : value() {}; 
    // Add one argument 
    sum<T>& operator<<(T const& x) 
      { value += x; return *this; } 
    // to get funal value 
    operator T() 
      { return value;} 
    // need another type that's handled differently? Sure! 
    sum<T>& operator<<(double const& x) 
      { value += 100*int(x); return *this; } 
}; 

#include <iostream> 

int main() 
{ 
    std::cout << (sum<int>() << 5 << 1 << 1.5 << 19) << "\n"; 
    return 0; 
} 

Dicha técnica (sobrecarga de operadores y función de corriente de clase similar) puede resolver diferentes problemas con argumentos variables, no sólo éste. Por ejemplo:

create_window() << window::caption - "Hey" << window::width - 5; 
    // height of the window and its other parameters are not set here and use default values 
+2

No recomendaría usar una función de conversión para esto. Incluso si * no * sobrecarga su operador << para el doble, todavía obtendrá una ambigüedad para 'suma () << 2L', porque entra en conflicto con el operador de desplazamiento incorporado para tipo int (estas situaciones son muy sutiles). Proporcionaría una función 'get' que no sea miembro que devuelva la suma (consulte la expresión" non-member get() idiom ") y, en general, evite las funciones de conversión. –

1

escribí un artículo sobre un typesafe printf C++ en aplicación DDJ.com hace algunos años. Abiertamente trata con problemas comparables. Tal vez eso ayude.

Ver http://www.ddj.com/cpp/184401999

0

Después de pensarlo un poco, he encontrado una manera de hacerlo utilizando una typelist. No necesita un tipo any de esa manera, y su código se vuelve seguro.

Se basa en la construcción de una estructura de plantilla que contiene una cabeza (de un tipo conocido) y una cola, que de nuevo es una lista de tipos. He añadido un poco de azúcar sintáctica para que sea más intuitivo: utilizar la siguiente manera:

// the 1 argument processing function 
template< typename TArg > void processArg(const TArg& arg) { 
    std::cout << "processing " << arg.value << std::endl; 
} 

// recursive function: processes 
// the first argument, and calls itself again for 
// the rest of the typelist 
// (note: can be generalized to take _any_ function 
template< typename TArgs > 
void process(const TArgs& args) { 
    processArg(args.head); 
    return process(args.rest); 
} 

template<> void process<VoidArg>(const VoidArg& arg){} 

int main() { 
    const char* p = "another string"; 
    process((arglist= 1, 1.2, "a string", p)); 
} 

Y aquí es el marco argumento que pasa:

#include <iostream> 

// wrapper to abstract away the difference between pointer types and value types.  
template< typename T > struct TCont { 
    T value; 
    TCont(const T& t):value(t){} 
}; 

template<typename T, size_t N> struct TCont<T[N]> { 
    const T* value; 
    TCont(const T* const t) : value(t) { } 
}; 

template<typename T> struct TCont<T*> { 
    const T* value; 
    TCont(const T* t): value(t){} 
}; 


// forward definition of type argument list 
template< typename aT, typename aRest > 
struct TArgList ; 

// this structure is the starting point 
// of the type safe variadic argument list 
struct VoidArg { 

    template< typename A > 
    struct Append { 
    typedef TArgList< A, VoidArg > result; 
    }; 

    template< typename A > 
    typename Append<A>::result append(const A& a) const { 
    Append<A>::result ret(a, *this); 
    return ret; 
    } 

    //syntactic sugar 
    template< typename A > typename Append<A>::result operator=(const A& a) const { return append(a); } 

} const arglist; 



// typelist containing an argument 
// and the rest of the arguments (again a typelist) 
// 
template< typename aT, typename aRest > 
struct TArgList { 
    typedef aT T; 
    typedef aRest Rest; 
    typedef TArgList< aT, aRest > Self; 

    TArgList(const TCont<T>& head, const Rest& rest): head(head), rest(rest){} 

    TCont<T> head; 
    Rest rest; 

    template< typename A > struct Append { 
    typedef TArgList< T, typename Rest::Append<A>::result > result; 
    }; 

    template< typename A > 
    typename Append<A>::result append(const A& a) const { 
    Append<A>::result ret (head.value, (rest.append(a))); 
    return ret; 
    } 

    template< typename A > typename Append<A>::result operator,(const A& a) const { return append(a); } 
}; 
Cuestiones relacionadas