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