You can let the preprocessor help you cheat using this strategy, stolen and tweaked from another answer:
#include <stdio.h>
#include <stdarg.h>
#define PP_NARG(...) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
_71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
_81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
_91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
_101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
_111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
_121,_122,_123,_124,_125,_126,_127,N,...) N
#define PP_RSEQ_N() \
127,126,125,124,123,122,121,120, \
119,118,117,116,115,114,113,112,111,110, \
109,108,107,106,105,104,103,102,101,100, \
99,98,97,96,95,94,93,92,91,90, \
89,88,87,86,85,84,83,82,81,80, \
79,78,77,76,75,74,73,72,71,70, \
69,68,67,66,65,64,63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
void _variad(size_t argc, ...);
#define variad(...) _variad(PP_NARG(__VA_ARGS__), __VA_ARGS__)
void _variad(size_t argc, ...) {
va_list ap;
va_start(ap, argc);
for (int i = 0; i < argc; i++) {
printf("%d ", va_arg(ap, int));
}
printf("\n");
va_end(ap);
}
int main(int argc, char* argv[]) {
variad(2, 4, 6, 8, 10);
return 0;
}
There's a few clever tricks here.
1) Instead of calling the variadic function directly, you're calling a macro that counts the arguments and passes the argument count as the first argument to the function. The end result of the preprocessor on main looks like:
_variad(5, 2, 4, 6, 8, 10);
2) PP_NARG
is a clever macro to count arguments.
The workhorse here is PP_ARG_N
. It returns its 128th argument, by ignoring the first 127 arguments (named arbitrarily _1
_2
_3
etc.), naming the 128th argument N
, and defining the result of the macro to be N
.
PP_NARG
invokes PP_ARG_N
with __VA_ARGS__
concatenated with PP_RSEQ_N
, a reversed sequence of numbers counting from 127 down to 0.
If you provide no arguments, the 128th value of PP_RSEQ_N
is 0. If you pass one argument to PP_NARG
, then that argument will be passed to PP_ARG_N
as _1
; _2
will be 127, and the 128th argument to PP_ARG_N
will be 1. Thus, each argument in __VA_ARGS__
bumps PP_RSEQ_N
over by one, leaving the correct answer in the 128th slot.
(Apparently 127 arguments is the maximum C allows.)
The safest way is as described above. But if you REALLY need to know the number of arguments without adding the extra argument mentioned then you can do it this way (but note that it is very machine dependent, OS dependent and even, in rare cases, compiler dependent). I ran this code using Visual Studio 2013 on a 64 bit DELL E6440.
Another point, at the point where I divided by sizeof(int), that was because all of my arguments were int's. If you have different size arguments, there my need to be some adjustment there.
This relies on the calling program to use the standard C calling convention. (varfun() gets the number of arguments from the "add esp,xxx" and there are two forms of the add, (1) short form and (2) long form. In the 2nd test I passed a struct because I wanted to simulate lots of arguments to force the long form).
The answers printed will be 6 and 501.
varfun(1, 2, 3, 4, 5, 6);
00A03CC8 6A 06 push 6
00A03CCA 6A 05 push 5
00A03CCC 6A 04 push 4
00A03CCE 6A 03 push 3
00A03CD0 6A 02 push 2
00A03CD2 6A 01 push 1
00A03CD4 E8 E5 D3 FF FF call _varfun (0A010BEh)
00A03CD9 83 C4 18 add esp,18h
varfun(1, x);
00A03CDC 81 EC D0 07 00 00 sub esp,7D0h
00A03CE2 B9 F4 01 00 00 mov ecx,1F4h
00A03CE7 8D B5 28 F8 FF FF lea esi,[x]
00A03CED 8B FC mov edi,esp
00A03CEF F3 A5 rep movs dword ptr es:[edi],dword ptr [esi]
00A03CF1 6A 01 push 1
00A03CF3 E8 C6 D3 FF FF call _varfun (0A010BEh)
00A03CF8 81 C4 D4 07 00 00 add esp,7D4h
#include<stdio.h>
#include<stdarg.h>
void varfun(int i, ...);
int main()
{
struct eddy
{
int x[500];
} x = { 0 };
varfun(1, 2, 3, 4, 5, 6);
varfun(1, x);
return 0;
}
void varfun(int n_args, ...)
{
va_list ap;
unsigned long *p;
unsigned char *p1;
unsigned int nargs;
va_start(ap, n_args);
p = (long *)(ap - _INTSIZEOF(int) - _INTSIZEOF(&varfun));
p1 = (char *)*p;
if (*p1 == 0x83) // short add sp,x
{
nargs = p1[2] / sizeof(int);
}
else
{
nargs = *(unsigned long *)(p1+2) / sizeof(int);
}
printf("%d\n", nargs);
va_end(ap);
}