7

I am a newer for C++, and my first language is Chinese, so my words with English may be unmeaningful, say sorry first. I know there is a way to write a function with variable parameters which number or type maybe different each calling, we can use the macros of va_list,va_start and va_end. But as everyone know, it is the C style. When we use the macros, we will lose the benefit of type-safe and auto-inference, then I try do it whit C++ template. My work is followed:


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

I think it's ugly, I want to there is a way to do better? Thanks, and sorry for language errors.

easyeagel
  • 83
  • 5
  • What function(s) do you need to make which need var-args and type safety? – strager Aug 29 '09 at 06:48
  • is the number of arguments truly "anything" or is it something practical like "up to 10"? – Evan Teran Aug 29 '09 at 07:41
  • If the values are fix at compile-time, you can use some tuple type to store them. Some template-meta magic might make the tuple object quite convenient to create. Whether the resulting code is less ugly is, erm, _subjective_, though. But it's definitely faster at run-time. `:)` – sbi Aug 29 '09 at 14:37

2 Answers2

3

You can do something like this:

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

Such technique (operator overloading and stream-like function class) may solve different problems with variable arguments, not only this one. For example:

create_window() << window::caption - "Hey" << window::width - 5;
     // height of the window and its other parameters are not set here and use default values
P Shved
  • 96,026
  • 17
  • 121
  • 165
  • 2
    I would not recommend to use a conversion function for this. Even if you *don't* overload your operator<< for double, you will still get an ambiguity for `sum() << 2L`, because it conflicts with the built-in shift-operator for type int (these situations are very subtle). I would provide a non-member `get` function that returns the sum (see the "non-member get() idiom") and in general avoid conversion functions. – Johannes Schaub - litb Aug 29 '09 at 18:22
0

After giving it some thought, I found a way to do it using a typelist. You don't need an any type that way, and your code becomes type-safe.

It's based on building a template structure containing a head (of a known type) and a tail, which is again a typelist. I added some syntactic sugar to make it more intuitive: use like this:

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

And here is the argument passing framework:

#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); }
};
xtofl
  • 40,723
  • 12
  • 105
  • 192