On Tue, 17 Jul 2012 13:32:08 +0100, Regan Heath <re...@netmail.co.nz> wrote:

On Tue, 17 Jul 2012 12:43:46 +0100, Jacob Carlborg <d...@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/

Reply via email to