Reputation: 205
Is it possible to create a template function that takes a variable number of arguments, for example, in this Vector< T, C >
class constructor:
template < typename T, uint C >
Vector< T, C >::Vector( T, ... )
{
va_list arg_list;
va_start( arg_list, C );
for( uint i = 0; i < C; i++ ) {
m_data[ i ] = va_arg( arg_list, T );
}
va_end( arg_list );
}
This almost works, but if someone calls Vector< double, 3 >( 1, 1, 1 )
, only the first argument has the correct value. I suspect that the first parameter is correct because it is cast to a double
during the function call, and that the others are interpreted as int
s and then the bits are stuffed into a double
. Calling Vector< double, 3 >( 1.0, 1.0, 1.0 )
gives the desired results. Is there a preferred way to do something like this?
Upvotes: 11
Views: 17845
Reputation: 506837
You can do what you want, but don't do it, because it's not typesafe. Best pass a vector of T
or a pair of iterators containing those values.
template < typename T, uint C >
Vector< T, C >::Vector(int N, ... )
{
assert(N < C && "Overflow!");
va_list arg_list;
va_start(arg_list, N);
for(uint i = 0; i < N; i++) {
m_data[i] = va_arg(arg_list, T);
}
va_end(arg_list);
}
Vector<int> v(3, 1, 2, 3);
This can be better solved, since all the elements are homogeneous typed anyway.
template < typename Iter, uint C >
Vector< T, C >::Vector(Iter begin, Iter end)
{
T *data = m_data;
while(begin != end)
*data++ = *begin++;
}
int values[] = { 1, 2, 3 };
Vector<int> v(values, values + 3);
Of course, you have to make sure there is enough place in m_data
.
Upvotes: 2
Reputation: 1
The problem with variable arguments in constructors is :
So the "correct" code (MS) could be :
template < typename T, uint C > __cdecl Vector< T, C >::Vector( T, ... )
but the compiler will say:
illegal calling convention for constructor/destructor (MS C4166)
Upvotes: 0
Reputation: 2935
You can use variadic , variadic means template with variable argument.more
Upvotes: 0
Reputation: 41333
std::tr1::array
(which looks similar to yours) does not define a constructor, and can be initialized as an aggregate (?)
std::tr1::array<int, 10> arr = {{ 1, 2, 3, 4, 5, 6 }};
Also you could check out Boost.Assignment library.
For example the constructor could be
template < typename T, uint C >
template < typename Range >
Vector< T, C >::Vector( const Range& r )
and instances created with
Vector<int, 4> vec(boost::assign::cref_list_of<4>(1)(3)(4)(7));
Upvotes: 0
Reputation: 52519
This code looks dangerous and I think your analysis on why it isn't working is spot on, there's no way for the compiler to know that when calling:
Vector< double, 3 >( 1, 1, 1 )
the ones should be passed as doubles.
I would change the constructor to something like:
Vector< T, C >::Vector(const T(&data)[C])
instead, and have the user pass the arguments as an array. Another sort of ugly solution would be something like this:
template < typename T, uint C >
Vector< T, C >::Vector(const Vector<T, C - 1>& elements, T extra) {
}
and call it like this (with some typedefs):
Vector3(Vector2(Vector1(1), 1), 1);
Upvotes: 2
Reputation: 95449
In C++0x (really should be called C++1x), you can use template varargs to achieve what you want in a typesafe fashion (and you won't even need to specify the number of arguments!). However, in the current version of C++ (ISO C++ 1998 with 2003 amendments), there is no way to accomplish what you want. You can either hold off or do what Boost does, which is use preprocessor macro magic to repeat the definition of the constructor multiple times with different numbers of parameters up to a hard-coded, but large limit. Given that Boost.Preprocessor is kind of complicating, you could just define all of the following yourself:
Vector<T,C>::Vector(); Vector<T,C>::Vector(const T&); Vector<T,C>::Vector(const T&, const T&); // ...
Since the above is kind of painful to do by hand, though, you could write a script to generate it.
Upvotes: 0
Reputation: 4209
Alas, right now there's no good way to do this. Most of the Boost packages that need to do something similar use macro tricks to define things like this:
template < typename T >
Vector< T >::Vector( T )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2, C c3 )
{ ... }
The macros generate some set number (typically around 10) versions, and provide a mechanism to change the max number of parameters before expanding the construction.
Basically, its a real pain which is why C++0x is introducing variable-length template arguments and delegation methods that will let you do this cleanly (and safely). In the meantime you can either do it with macros, or try a C++ compiler that has support for (some of) these new experimental features. GCC is a good one for this.
Be warned though that since C++0x isn't actually out yet, things can still change and your code may not be in sync with the final version of the standard. Plus, even after the standard comes out, there'll be 5 years or so during which many compilers will only partially support the standard, so your code won't be very portable.
Upvotes: 9
Reputation: 7876
Does TypeList fit your needs?
Upvotes: 0