Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ varargs - Is how I am using them okay or are they bad? Is there a good alternative?

The ultimate goal of this is to have a function which can take a variable number of arguments of a certain type (the same type, not different types), that can be declared on the function call.

As I'm using Visual Studio 2010, I CANNOT do:

MyFunction({1,2,3});

In an earlier question which was answered, I found I could use boost::assign::list_of(), however I discovered later that this seems to have a bug of some kind if you try to pass it only one parameter.

So I did some more searching and found that I could use variadic functions to achieve what I was aiming for.

void TestFunction2<int>(int count, ...)
{}

However, I wanted to restrict it by type, so eventually found I could do this with templates:

template <class T>
void TestFunction(const T& count, ...);

template <>
void TestFunction<int>(const int& count, ...);

Unfortunately, varargs things like va_list do not apparently like references. The examples I saw to restrict types like this used const references. If I remove the const reference aspect of the count parameter, it works as I want, but I don't know if this is going to lead to horrible side-effects down the road, OR if this whole varargs thing is a bad idea to begin with.

So I guess my question is, is what I'm doing in the last example above good or bad? If it's bad, what is a good alternative so I can call a function with one or more parameters in-line like, say, int parameters?

like image 524
Interminable Avatar asked Oct 04 '22 04:10

Interminable


1 Answers

What you want is std::initializer_list<T>, unfortunately this require C++11 support.

An alternative, that is nearly as elegant and easy enough to upgrade from, is to use an array:

#include <iostream>
 
template <typename T, size_t N>
void func(T (&s)[N]) {
    for (size_t i = 0; i != N; ++i) {
        std::cout << s[i] << '\n';
    }
}
 
int main() {
    int array[] = {1, 2, 3};
    func(array);
}

When you move on to a compiler that supports initializer lists, this can be changed into:

#include <iostream>
 
template <typename T>
void func(std::initializer_list<T> s) {
    for (T const& t: s) {
        std::cout << t << '\n';
    }
}
 
int main() {
    func({1, 2, 3});
}

So both the function and call sites update will be painless.

Note: the call site could be made completely similar using a macro, I advise against such approach, the purported gain is not worth the obfuscation.

like image 148
Matthieu M. Avatar answered Oct 07 '22 19:10

Matthieu M.