Reputation: 1784
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?
Upvotes: 4
Views: 1286
Reputation: 14157
You could use macroprocessor to count arguments.
#include <stdarg.h>
#include <stdio.h>
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;
}
#define TENTH(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,...) p10
#define NARGS(...) TENTH(__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)
#define printme(...) printme(NARGS(__VA_ARGS__), __VA_ARGS__)
int main(void)
{
printme("X", "YY");
}
How it works:
Macro TENTH
simply returns the tenth argument
Example:
TENTH(a,b,c,d,e,f,g,h,i,j,k)
expands as j
.
It can used to obtain a number of argument:
| tenth argument
v
TENTH("X", "XY", 9,8,7,6,5,4,3,2,1,0)
expand as 2
because 2
is the tenth argument of TENTH
.
Similarly,
| tenth argument
v
TENTH("A", "B", "C", "D", 9,8,7,6,5,4,3,2,1,0)
expands as 4
.
Macro NARGS
places its arguments before sequence 9,8,...
shifting the sequence to get the number of arguments.
I've used printme
to avoid polluting namespace.Whenever a macro is expand is get disabled to avoid infinite recursion. It will not expand any further becaming a call to original printme()
. The first argument num
is computed with NARGS macro. All remaining arguments are passed after the num
one.
There are still some disadvantages:
Upvotes: 2
Reputation: 1784
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
Upvotes: 3
Reputation: 4929
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");
}
Upvotes: 3