2009-08-29 6 views
7

Je suis un nouveau pour C++, et ma première langue est le chinois, donc mes mots avec l'anglais peuvent être sans intérêt, dis désolé d'abord. Je sais qu'il existe un moyen d'écrire une fonction avec des paramètres variables dont le nombre ou le type peut être différent à chaque appel, on peut utiliser les macros de va_list, va_start et va_end. Mais comme tout le monde le sait, c'est le style C. Lorsque nous utiliserons les macros, nous perdrons le bénéfice de la sécurité de type et de l'auto-inférence, alors j'essaie de le faire avec le template C++. Mon travail est suivi:fonction de paramètre variable, comment rendre le type sûr et plus significatif?


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

Je pense qu'il est laid, je veux y est un moyen de faire mieux? Merci, et désolé pour les erreurs de langue.

+0

Quelle fonction (s) avez-vous besoin de faire qui ont besoin var- arguments et type de sécurité? – strager

+0

est le nombre d'arguments vraiment "quelque chose" ou est-ce quelque chose de pratique comme "jusqu'à 10"? –

+0

Si les valeurs sont fix au moment de la compilation, vous pouvez utiliser un certain type de tuple pour les stocker. Un peu de magie de template-meta pourrait rendre l'objet tuple très pratique à créer. Si le code résultant est moins laid est, erm, _subjective, cependant. Mais c'est définitivement plus rapide à l'exécution. ':)' – sbi

Répondre

3

Vous pouvez faire quelque chose comme ceci:

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

Cette technique (de la surcharge des opérateurs et la classe de fonction flux semblable) peut résoudre des problèmes différents avec des arguments variables, non seulement celui-ci. Par exemple:

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

Je ne recommanderais pas d'utiliser une fonction de conversion pour cela. Même si vous * ne surchargez pas votre opérateur << pour le double, vous aurez toujours une ambiguïté pour 'sum () << 2L', car elle est en conflit avec l'opérateur de décalage intégré pour le type int (ces situations sont très subtiles). Je fournirais une fonction non-membre 'get' qui retourne la somme (voir le" idiome de non-membre get() ") et évite en général les fonctions de conversion. –

1

J'ai écrit un article sur une implémentation C++ de typesafe printf sur DDJ.com il y a quelques années. Il traite propablement des problèmes comparables. Peut-être que ça aide.

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

0

Après avoir réfléchi, j'ai trouvé une façon de le faire en utilisant un typelist. Vous n'avez pas besoin d'un type any de cette façon, et votre code devient sécurisé.

Il est basé sur la construction d'une structure de gabarit contenant une tête (d'un type connu) et une queue, qui est encore une typeliste. J'ai ajouté un peu de sucre syntaxique pour le rendre plus intuitive: utiliser comme ceci:

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

Et voici le cadre de passage d'arguments:

#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); } 
}; 
Questions connexes