Hossein
Hossein

Reputation: 33

How would it be possible to use variadic function without passing number of arguments and only with 3 dots?

Considering the code below, is it possible to define a variadic function without getting number of arguments?

void printFunction(...)
{
    int noOfArgs = "Any way to get number of arguments?";
    va_list args;
    va_start(args, noOfArgs);
    cout << va_arg(args, int) << endl;
    for (int i = 2; i <= noOfArgs; i++)
    {
        cout << va_arg(args, int) << endl;
    }
    va_end(args);
}


int main()
{

    printFunction(1, 2, 3, 4, 5);

    return 0;
}

Upvotes: 2

Views: 2521

Answers (3)

TheCppZoo
TheCppZoo

Reputation: 1242

In C++ 11 and after, it is very easy:

template<typename... Arguments>
void printFunction(Arguments &&...args) {
    auto noOfArgs = sizeof...(Arguments);

However, you are using an old-style, plain-C, preprocessor macro style, which is very different. First, to use the preprocessor macros you need to have at least one named parameter, which will be used to start the va_list, and that's the beginning of the complications.

I think there is no standard way to count the number of arguments, it can be implemented in a number of ways, if that is what you are really asking, which I would have tagged as C instead of C++.

How to access the arguments: there are two choices, let us call it the "machine gun" and the "drip-drip-drip" --these are my names--:

#include <utility>

template<typename... Ts>
void ignoreResults(Ts &&...) {}

template<typename Argument>
int machineGunBullet(Argument &&arg);

inline void drip() {} // do nothing

template<typename A1, typename... Tail>
void drip(A1 &&a1, Tail &&...tail) {
    // do something here with a1
    drip(std::forward<Tail>(tail)...);
}

template<typename... Arguments>
void printFunction(Arguments &&...args) {
    ignoreResults(machineGunBullet(std::forward<Arguments>(args))...);
    // or
    drip(std::forward<Arguments>(args)...);
}

This builds in the compiler explorer, https://godbolt.org/z/aSx2wv

Upvotes: 3

eerorika
eerorika

Reputation: 238421

There is technically one way, which is analogous to how it is possible to get the length of a null terminated string: Use particular argument value to designate the end of the argument list.

However, note that in order to use va_start, there must be at least one named parameter. In your example you attempt to pass a local variable into va_start which is not allowed. That macro doesn't take "the number of arguments". It takes "last named argument before variadic parameters start".

That said: I recommend against using C-style variadic functions.

Upvotes: 1

user3684240
user3684240

Reputation: 1590

Getting the amount of arguments is not possible in C va_args. The best you can do is to use a magic value that ends the sequence and a macro to hide it from your user:

#define printFunction(...) actualPrintFunction(__VA_ARGS__, 42)
void actualPrintFunction(int first, ...)
{
    va_list args;
    va_start(args, first);
    int arg;
    std::cout << first << endl;
    while((arg = va_arg(args, int)) != 42)
    {
        cout << arg << endl;
    }
    va_end(args);
}

As mentioned in the comments, you could alternatively use C++ fold expressions. The above has the advantage of being compatible with C and compiling faster, while the C++ approach gives you type safety.

Upvotes: 3

Related Questions