2009-08-29 9 views
7

Sono un neofita per C++ e la mia lingua madre è il cinese, quindi le mie parole con l'inglese potrebbero essere scomode, per prima cosa scusate. So che c'è un modo per scrivere una funzione con parametri variabili che numero o tipo può essere diverso per ogni chiamata, possiamo usare i macro di va_list, va_start e va_end. Ma come tutti sanno, è lo stile C. Quando usiamo i macro, perderemo il beneficio di type-safe e auto-inferenza, quindi cerco di farlo con il modello C++. Il mio lavoro è seguito:funzione di parametro variabile, come renderlo sicuro e più 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; 
} 

penso che sia brutto, voglio ci sia un modo per fare di meglio? Grazie, e mi dispiace per gli errori di lingua.

+0

Quale funzione (s) avete bisogno di fare che hanno bisogno di Var- args e type safety? – strager

+0

è il numero di argomenti veramente "nulla" o è qualcosa di pratico come "fino a 10"? –

+0

Se i valori sono corretti in fase di compilazione, è possibile utilizzare un tipo di tupla per memorizzarli. Alcune magie template-meta potrebbero rendere l'oggetto tuple piuttosto comodo da creare. Se il codice risultante è meno brutto è, erm, _soggettivo_, però. Ma è decisamente più veloce in fase di esecuzione. ':)' – sbi

risposta

3

si può fare qualcosa di simile:

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; 
} 

Tale tecnica (overloading degli operatori e la classe funzione di corrente-like) può risolvere problemi diversi, con argomenti variabili, non solo questo. Ad esempio:

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

Non consiglierei di usare una funzione di conversione per questo. Anche se * non * sovraccarichi il tuo operatore << per il doppio, otterrai comunque un'ambiguità per la somma () << 2L', perché è in conflitto con l'operatore di spostamento incorporato per tipo int (queste situazioni sono molto sottili). Fornirei una funzione 'get' non membro che restituisce la somma (vedere l'espressione" non membro get() ") e in generale evita le funzioni di conversione. –

1

Ho scritto un articolo su un'implementazione C++ typcofe printf su DDJ.com alcuni anni fa. Affronta propostamente problemi analoghi. Forse questo aiuta.

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

0

Dopo aver dato qualche pensiero, ho trovato un modo per farlo utilizzando una TypeList. Non è necessario un tipo any in questo modo e il codice diventa sicuro per i tipi.

Si basa sulla costruzione di una struttura modello contenente una testina (di un tipo noto) e una coda, che è di nuovo una lista di caratteri. Ho aggiunto un po 'di zucchero sintattico per renderlo più intuitivo: utilizzare in questo modo:

// 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)); 
} 

Ed ecco il quadro argomento passaggio:

#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); } 
}; 
Problemi correlati