K&R-style variadic functions
Regan Heath
regan at netmail.co.nz
Tue Jul 17 06:05:35 PDT 2012
On Tue, 17 Jul 2012 13:32:08 +0100, Regan Heath <regan at netmail.co.nz>
wrote:
> On Tue, 17 Jul 2012 12:43:46 +0100, Jacob Carlborg <doob at me.com> wrote:
>
>> On 2012-07-17 09:06, Mehrdad wrote:
>>
>>> My eyes!! They're bleeding!!
>>
>> First I wanted to know if my interpretation is correct and then I was
>> trying to figure out how my tool should behave if it encounters a
>> function like this.
>
> After a bit of googling and a test with my local MSVC9 I think old-style
> variadics look like this:
>
> #include <varargs.h>
> #include <stdio.h>
>
> void foo(va_alist)
> va_dcl
> {
> va_list p;
> va_start(p);
> vprintf("%d %d %d\n", p);
> }
>
> void main()
> {
> foo(1, 2, 3);
> }
>
> (the above runs and outputs "1 2 3" on the console)
Some follow up information, for interests sake.
1. va_dcl is defined in varargs.h as..
#define va_dcl va_list va_alist;
So, the old-style variable args function definiton can be written:
void foo(va_alist)
va_list va_alist;
{
...
}
2. va_start in defined in varargs.h as..
#define va_start(ap) ap = (va_list)&va_alist
So, it assumes a parameter called va_alist exists, and it simply takes the
address of it. A new style variadic uses a definition like this..
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) +
_INTSIZEOF(v) )
which takes the address of 'v' (which is the argument prior to the ...)
then skips past it, obtaining the address of the first ... argument.
So, in effect they are doing the same thing, taking the address of the
first variable argument. In which case, I can only assume if we can call
new-style variadics from D, we can call old-style ones as well - provided
any preceding arguments to the va_alist argument are defined correctly,
.e.g.
given this old-style function declaration:
void foo();
which actually happens to match this old-style variadic function
definition:
void foo(a,b,va_alist)
int a;
int b;
va_list va_alist;
{
}
we could produce the following:
extern (C) void foo(/*TODO: add args here*/);
and, once the user filled in the args
extern (C) void foo(int a, int b, ...);
it should be callable from D.. in fact I just did a test (albeit with a
slightly different test case..)
[testdll.c]
#include <varargs.h>
#include <stdio.h>
__declspec( dllexport )
void foo(va_alist)
va_list va_alist;
{
va_list p;
va_start(p);
vprintf("%d %d %d\n", p);
}
(compiled with MSVC to dll and lib, lib converted with coffimplib to OMF
format)
[test.d]
extern (C) void foo(int a, ...);
int main(string[] args)
{
foo(1, 3, 5);
return 0;
}
compile with dmd test.d testdll.lib, run test.exe, outputs "1 3 5" :)
But, this highlights one issue I was unaware of. The D extern (C)
declaration needs at least 1 parameter before the ..., having just:
extern (C) void foo(...);
is illegal.
In my specific case I could add "int a" as the first parameter, and all
was well. Each case will be different, and it's conceivable there is a C
old-style variadic which takes /any/ type of first parameter, which could
be a problem, however the key issue is the size of the argument. If all
types which could be passed are 32 bits big, then "int a" is sufficient to
get it working in all cases.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/
More information about the Digitalmars-d
mailing list