So I’ve been making a custom parser for a scripting language, and I wanted to be able to pass only ellipses arguments. I don’t need or want an initial variable, however Microsoft and C seem to want something else. FYI, see bottom for info.
I’ve looked at the va_* definitions
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
and the part I don’t want is the v in va_start. As a little background I’m competent in goasm and I know how the stack works so I know what’s happening here. I was wondering if there is a way to get the function stack base without having to use inline assembly.
Ideas I’ve had:
#define im_va_start(ap) (__asm { mov [ap], ebp })
and etc… but really I feel like that’s messy and I’m doing it wrong.
struct function_table {
const char* fname;
(void)(*fptr)(...);
unsigned char maxArgs;
};
function_table mytable[] = {
{ "MessageBox", &tMessageBoxA, 4 } };
… some function that sorts through a const char* passed to it to find the matching function in mytable and calls tMessageBoxA with the params. Also, the maxArgs argument is just so I can check that a valid number of parameters is being sent. I have personal reasons for not wanting to send it in the function, but in the meantime we can just say it’s because I’m curious.
This is just an example; custom libraries are what I would be implementing so it wouldn’t just be calling WinAPI stuff.
void tMessageBoxA(...) {
// stuff to load args passed
MessageBoxA(arg1, arg2, arg3, arg4);
}
I’m using the __cdecl calling convention and I’ve looked up ways to reliably get a pointer to the base of the stack (not the top) but I can’t seem to find any. Also, I’m not worried about function security or typechecking.
edit: Thanks for input, it appears it wasn’t possible.
My fix has ended up being
#define im_va_start(ap) {\
__asm push eax\
__asm mov eax, ebp\
__asm add eax, 8h\
__asm mov ap, eax\
__asm pop eax\
}
And then I can continue as normal.
As for why I require it, I’m doing some (unique) read: unsafe tricks and using a struct array with a pointer to the function as defined above. Since each function is unique, and most are from my custom library, they have… different behaviors. I don’t really know how to explain it, but I’ll release the source when I finish the POC.
I’m not really worried about portability so that’ll have to work. Also, for counting the args I did:
#define im_va_count(ap, num, t) {\
for(num = 0; *(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) > 0; ++num){ }\
--num;\
im_va_start(argptr);\
}
which works for me. If anyone’s interested…
Unfortunately this is not possible, the C standard says:
And the
...doesn’t count as the “one or more parameters”. Further,That it should be called like
And that
So as you can see, you cannot have a variadic function without at least one parameter before the ellipsis in standard C++. The non-portable assembly trick is the closest you can come.