Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using va_args without passing num (like printf)

The following is the most basic example I can come up with to pass variable-length args to a function:

int printme(int num, ...)
{
    va_list ap;
    va_start(ap, num);
    for (int i = 0; i < num; ++i) {
        char *arg = va_arg(ap, char *);
        printf("%d. %s\n", i + 1, arg);
    }
    va_end(ap);
    return 1;
}

int main(void)
{
    printme(2, "X", "YY");
}

However, notice that I am passing in the length as the first argument (or as any argument). Is it possible to use these va_ macros with something like a printf-ish function? For example, could I do something like this (without passing the number of args?

print2("Hello something %s %s", "Arg 1", "Arg 2");
print2("Hello something %s %s %s", "Arg 1", "Arg 2", "Arg 3");

If so, what would the function to receive that look like? And if it's not possible, how does printf implement it then?

like image 257
carl.hiass Avatar asked Jan 25 '23 11:01

carl.hiass


2 Answers

If you only want to pass strings, it can be done quite easily by writing a function to parse the first argument like countargs(char*) that I've written here. It returns the number of arguments:

#include <stdarg.h>
#include <stdio.h>
int printme(char* fmt, ...)
{
    va_list ap;
    int num = countargs(fmt);
    va_start(ap, num);
    for (int i=0; i < num; ++i) {
        char* arg = va_arg(ap, char*);
        printf("%d. %s\n", i+1, arg);
    }
    va_end(ap);
    return 1;
}

int countargs(char* fmt)
{
    int i, num = 0;
    if(strlen(fmt) < 2) 
    { 
        return 0;
    }
    for(i = 0; fmt[i+1] != '\0'; i++)
    {
        if (fmt[i] == '%' && fmt[i+1] == 's')
        {
            num++;
        }
    }
    return num;
}

int main(void)
{
    printme("%s%s", "Stack", "Overflow");
}
like image 182
Amal K Avatar answered Feb 01 '23 09:02

Amal K


Here's a basic example of detecting the number of arguments from a parsed string. It's not taking into account any special circumstances and doesn't do anything with actually formatting/parsing the strings, but shows a basic working program to show how the count can be parsed from a string:

int printmy(char * format, ...)
{
    // num args
    int num = 0;
    for(char idx=0, c; c=format[idx]; idx++) {
        // ignore escape char + double-percent
        if (c=='\\' || (c=='%' && format[idx+1]=='%'))
            idx++;
        else if (c=='%')
            num++;
    }

    // print variable args
    va_list ap;
    va_start(ap, format);   // need to give it the last argument before the "..."
    for (int i=0; i < num; ++i) {
        char* arg = va_arg(ap, char*);
        printf("%d. %s\n", i+1, arg);
    }
    va_end(ap);
    
    // return num args received
    return num;
}

int main(void)
{
    int num_args = printmy("Hello something \% \\% %% %s %s %s", "Arg 1", "Arg 2", "Arg 3");    
    printf("Num Args Parsed: %d\n", num_args);
}

And we get:

1. Arg 1
2. Arg 2
3. Arg 3
4.
Num Args Parsed: 4
like image 33
carl.hiass Avatar answered Feb 01 '23 11:02

carl.hiass