How to delegate varargs

Tomas Lindquist Olsen tomas at famolsen.dk
Tue Nov 20 14:14:14 PST 2007


Sean Kelly wrote:
> Tomas Lindquist Olsen wrote:
>> Frank Benoit wrote:
>>> How can I delegate the variadic argument list to another variadic
>>>  argument function?
>>> 
>>> e.g. void myWritef( char[] frm, ... ){ writefln( frm, ??? ); }
>>> 
>>> And how can I add or remove arguments to these varargs?
>> 
>> You have to write a "wrapper" function that explicitly takes the 
>> hidden arguments. In the case of writefln, phobos provides 
>> OutputStream.writefx.
> 
> Yup, and this stinks.  I don't suppose some language feature could be
>  provided for working around this?
> 
> 
> Sean

It should be possible, I can't really see anything that could be a
showstopper. Except using real variadic function to implement it.

On x86-64 va_list is not a simple pointer to data, so the storage for
the variadic arguments would have to alloca'd by the caller for this
idea to work.

This is exactly what I do in LLVMDC [1] btw, as implementing a variadic
function is broken on x86-64 (my development platform). Only calling one
works...

Ok :) Here's the idea, it's very simple!

The way it currently works:
// ---------------------------

// the function:
void func(...);

// really gets the prototype
void func_impl(TypeInfo[], void*);

// calling it like this:
void func(3.0f, "hello"[]);

// is implemented like this:
TypeInfo[2] arg_ti = [typeid(float), typeid(char[])];
struct Tmp { float f, char[] c; }
Tmp arg_val = Tmp(3.0f, "hello");
func_impl(arg_ti[], cast(void*)&arg_val);

// ----------------------------

Now to be able to pass on the arguments (and possible add to them), we
need another parameter:

size_t _argptrLength;

Further, we also need to be able to pass ... as an argument in a call
expression.

// ---------------------------

// lets implement func2
void func2(...)
{
	// call the first one adding some params
	func(42, ... , 8.0i);
}

// ---------------------------

// now...
// the function:
void func2(...);

// really gets the prototype
void func(TypeInfo[], void*, size_t);

// calling it like this:
func(42, ...);

// gets implemented like this:
TypeInfo[] arg_ti = 
(cast(TypeInfo*)alloca(TypeInfo.size*(1+_arguments.length)))[0..1+_arguments.length];

arg_ti[0] = typeid(int);
arg_ti[1..1+arguments.length] = _arguments[];

void* arg_val = alloca(int.sizeof + _argptrLength);
*(cast(int*)arg_val) = 42;
func_impl(arg_ti, arg_val);

// ----------------------------

I hope this makes sense, it could be written better, but it should show 
the idea. All this is stuff the compiler could figure out. Thinking in 
terms of LLVMDC and codegen, it could be done fairly easily.

I'm not sure how much work it would be to get the DMD frontend to accept 
the '...' token as a function argument and generate something meaningful 
in the AST though.

Excuse all the LLVMDC references, I know nobody is using it yet (with 
good reason), but it's my only reference to compiler development :)

Hope to get some feedback, don't think I've spent this much time on a NG 
post before :P

- Tomas Lindquist Olsen

[1] LLVMDC - LLVM D Compiler - http://www.dsource.org/projects/llvmdc



More information about the Digitalmars-d mailing list