How to work in C++ with functions the following form: void function(...) {}?
Do really need at least one implicit parameter?
Repeating from a comment:
It seems there is an interesting difference between C99 and C++11 here: C++11 allows a function declaration void foo(...) because the parameter-declaration-list in the parameter-declaration-clause is optional: [dcl.fct]
Function declaration:
D1 (parameter-declaration-clause)cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt
Parameters:
parameter-declaration-clause:
parameter-declaration-listopt...opt
parameter-declaration-list, ...
(Note how both the parameter-declaration-list and the ... are separately opt here, meaning you can leave out one or the other or both. This interpretation is supported by clang++ and g++.)
In C99, this declaration is not allowed since the parameter-list is not optional in the parameter-type-list: 6.7.5/1
Function declaration:
direct-declarator
(parameter-type-list)
Parameters:
parameter-type-list:
parameter-list
parameter-list, ...
As the va_start etc. macros/functions are inherited from C99, there's no way to use the arguments matched with the ellipsis with an empty parameter-declaration-list in C++.
Description of va_start in C99: 7.15.1.4
void va_start(va_list ap,parmN);
[...]
The parameter parmN is the identifier of the rightmost parameter in the variable parameter list in the function definition (the one just before the, ...). [...]
Emphasis mine. C99 assumes there's a parameter before the ellipsis, because it isn't legal in C99 to declare a function with an ellipsis but without parameters.
Yet, I can see two reasons to use a function with an ellipsis but w/o any parameters in C++:
Overload resolution. Matching arguments to an ellipsis leads to a very low ranking of the overload: An ellipsis conversion sequence is worse than any user-defined and standard conversion sequence [over.ics.rank]/2. This can be useful for metaprogramming:
char foo(int);
int foo(...);
struct S{};
S s;
sizeof(foo(42)); // yields 1
sizeof(foo(s)); // yields sizeof(int)
Implementation-defined tricks. Your implementation may provide a mean to access those arguments matched with an ellipsis. E.g. see BobTFish's example
If you want to maintain some semblance of platform independence, you need at least one parameter and use the va_arg macros.
If you know the low level details of the architecture and calling convention, then you can pull the arguments directly out of the registers and/or stack (depending on where the various parameters end up).
Given that the va_start "function" requires an argument to set up the va_list, I'd say it's not possible to do this in a way that works reliably and portably. It may well be possible to find something that works on specific platforms - but don't expect it to work if you change the compiler, compile for a different platform, and in some cases even if you compile with different compiler options (or change the code in the function, e.g. introducing local variables).
...
va_list vl;
va_start(vl, arg);
...
Of course, your other problem is knowing when there is NO arguments (which would be a valid case). So if you don't have at least one argument, what happens when you pass no argument at all? How do you "know" this is the case?
...in the argument list. It just magically appears.thispointer was always passed in a register.