Thread overview
[Suggestion] A few small improvements to variadic functions
Sep 29, 2004
Stewart Gordon
Sep 29, 2004
Ben Hinkle
Sep 29, 2004
Stewart Gordon
Sep 29, 2004
Ben Hinkle
Sep 30, 2004
Stewart Gordon
Sep 30, 2004
pragma
September 29, 2004
1.  http://www.digitalmars.com/d/function.html#variadic

"To protect against the vagaries of stack layouts on different CPU architectures, use std.stdarg to access the variadic arguments: "

By the looks of it, you show us how to access variadic arguments by incrementing _argptr, but then tell us that the method isn't portable and that we should use std.stdarg.

Ever since I discovered stdarg.h in C I've thought it ridiculous, as it means that C isn't self-contained.  If the ability to declare a variadic function is built in, so should the means of accessing the arguments.

Having invented a system for variadicity that's built in and typesafe, it ought to at least be portable.

My idea is to give D-linkage variadic functions one more implicit parameter, void*[] _argList, which points to each argument's data.

Then the example code would become:

----------
void foo(int x, ...)  {
printf("%d arguments\n", _arguments.length);
for (int i = 0; i < _arguments.length; i++)
{
_arguments[i].print();

if (_arguments[i] == typeid(int))
{
int j = *cast(int*) _argList[i];
printf("\t%d\n", j);
}
else if (_arguments[i] == typeid(long))
{
long j = *cast(long*) _argList[i];
printf("\t%lld\n", j);
}
else if (_arguments[i] == typeid(double))
{
double d = *cast(double*) _argList[i];
printf("\t%g\n", d);
}
else if (_arguments[i] == typeid(FOO))
{
FOO f = *cast(FOO*) _argList[i];
printf("\t%p\n", f);
}
else {
assert(0);
}
}
}

----------

2.  There ought to be a way of passing the arglist of one variadic function directly to another.

The simple solution, probably thought of by many, would be to allow the token '...' to be passed in.  Example:

void writeMultiple(int count, ...) {
for (int i = 0; i < count; i++) writefln(...);
}

Further, I suppose the '...' passed to the function shouldn't have to be the whole '...' of the callee.  For example, this might just as well be valid:

void printError(char[] formatStr, ...) {
writefln("Error: " ~ formatStr, ...);
}

or equivalently

void printError(...) {
writefln("Error: ", ...);
}

or possibly even

void printError(...) {
writefln("The error ", ..., " has occurred");
}

(Should we allow slicing of '...'?  Hmm....)

Stewart.


September 29, 2004
"Stewart Gordon" <Stewart_member@pathlink.com> wrote in message news:cjee0g$1034$1@digitaldaemon.com...
> 1.  http://www.digitalmars.com/d/function.html#variadic
>
> "To protect against the vagaries of stack layouts on different CPU architectures, use std.stdarg to access the variadic arguments: "
>
> By the looks of it, you show us how to access variadic arguments by incrementing _argptr, but then tell us that the method isn't portable and that we should use std.stdarg.

I agree that first example should be removed - just show people "the right way" from the start.

> Ever since I discovered stdarg.h in C I've thought it ridiculous, as it means that C isn't self-contained.  If the ability to declare a variadic function is built in, so should the means of accessing the arguments.
>
> Having invented a system for variadicity that's built in and typesafe, it ought to at least be portable.
>
> My idea is to give D-linkage variadic functions one more implicit parameter, void*[] _argList, which points to each argument's data.

It would be nice but there isn't much difference to the user between writing
 int j = *cast(int*)_argList[i];
and
 int j = va_arg!(int)(_argptr);
I suppose the _argList version lets you arbitrarily index the i^{th}
parameter while the va_arg version only lets you access the parameters in
order. It takes time to fill those pointers, though. The _arguments array
can be determined at compile time so it doesn't add much overhead to the
call (I don't know exactly how DMD create _arguments, though).

> Then the example code would become:
>
> ----------
> void foo(int x, ...)  {
> printf("%d arguments\n", _arguments.length);
> for (int i = 0; i < _arguments.length; i++)
> {
> _arguments[i].print();
>
> if (_arguments[i] == typeid(int))
> {
> int j = *cast(int*) _argList[i];
> printf("\t%d\n", j);
> }
> else if (_arguments[i] == typeid(long))
> {
> long j = *cast(long*) _argList[i];
> printf("\t%lld\n", j);
> }
> else if (_arguments[i] == typeid(double))
> {
> double d = *cast(double*) _argList[i];
> printf("\t%g\n", d);
> }
> else if (_arguments[i] == typeid(FOO))
> {
> FOO f = *cast(FOO*) _argList[i];
> printf("\t%p\n", f);
> }
> else {
> assert(0);
> }
> }
> }
>
> ----------
>
> 2.  There ought to be a way of passing the arglist of one variadic function directly to another.

The C way is to typically have a variadic version and a va_list version (eg
printf and vprintf). That way you can pass va_lists around. In D the pattern
would become
 void foo(...) { }
 void vfoo(TypeInfo[] arguments, va_list argptr) { }
Such functions should be added to std.stdio (or just make writex public).

> The simple solution, probably thought of by many, would be to allow the token '...' to be passed in.  Example:
>
> void writeMultiple(int count, ...) {
> for (int i = 0; i < count; i++) writefln(...);
> }
>
> Further, I suppose the '...' passed to the function shouldn't have to be the whole '...' of the callee.  For example, this might just as well be valid:
>
> void printError(char[] formatStr, ...) {
> writefln("Error: " ~ formatStr, ...);
> }
>
> or equivalently
>
> void printError(...) {
> writefln("Error: ", ...);
> }
>
> or possibly even
>
> void printError(...) {
> writefln("The error ", ..., " has occurred");
> }
>
> (Should we allow slicing of '...'?  Hmm....)
>
> Stewart.
>
>


September 29, 2004
Ben Hinkle wrote:
<snip>
>> My idea is to give D-linkage variadic functions one more implicit
>> parameter, void*[] _argList, which points to each argument's data.
> 
> 
> It would be nice but there isn't much difference to the user between writing
>  int j = *cast(int*)_argList[i];
> and
>  int j = va_arg!(int)(_argptr);

Except for the absurdity of effectively importing a language feature.  I thought half the point of the D vararg system was to be built in.

> I suppose the _argList version lets you arbitrarily index the i^{th}
> parameter while the va_arg version only lets you access the parameters in
> order. It takes time to fill those pointers, though.

It could be filled iff it's used....

<snip>
>> 2.  There ought to be a way of passing the arglist of one variadic
>> function directly to another.
> 
> 
> The C way is to typically have a variadic version and a va_list version (eg
> printf and vprintf). That way you can pass va_lists around.

To add my suggestion would protect us all from lib developers forgetting or not being bothered to do this.  It would also be simple to do AFAICS.

> In D the pattern would become
>  void foo(...) { }
>  void vfoo(TypeInfo[] arguments, va_list argptr) { }
> Such functions should be added to std.stdio (or just make writex public).
<snip>

Is it allowed for the two versions to be called the same?  Or does D reject such potential backward incompatibility, no matter how likely or not someone really is to want to output an array of TypeInfos followed by a va_list?

Stewart.
September 29, 2004
"Stewart Gordon" <smjg_1998@yahoo.com> wrote in message news:cjepmv$18ij$1@digitaldaemon.com...
> Ben Hinkle wrote:
> <snip>
> >> My idea is to give D-linkage variadic functions one more implicit parameter, void*[] _argList, which points to each argument's data.
> >
> >
> > It would be nice but there isn't much difference to the user between
writing
> >  int j = *cast(int*)_argList[i];
> > and
> >  int j = va_arg!(int)(_argptr);
>
> Except for the absurdity of effectively importing a language feature.  I thought half the point of the D vararg system was to be built in.

true enough. Walter does prefer language support over standard library support. I don't think I'd say the current scheme is "absurd", though.

> > I suppose the _argList version lets you arbitrarily index the i^{th} parameter while the va_arg version only lets you access the parameters
in
> > order. It takes time to fill those pointers, though.
>
> It could be filled iff it's used....
>
> <snip>
> >> 2.  There ought to be a way of passing the arglist of one variadic function directly to another.
> >
> >
> > The C way is to typically have a variadic version and a va_list version
(eg
> > printf and vprintf). That way you can pass va_lists around.
>
> To add my suggestion would protect us all from lib developers forgetting or not being bothered to do this.  It would also be simple to do AFAICS.

I suppose - though I don't exactly know how the mechanism would work. For
example would it be compatible with extern(C) variadic declarations:
 extern (C) printf(char* str, ...);
 void foo(char* str,...) { printf(str,...); }
Does the body of foo copy the inputs to foo from foo's stack frame so that
the printf stack frame sees them in the right place? One benefit of the
va_list declarations is that it is obvious a pointer is being passed around
instead of stack frames being copied.

> > In D the pattern would become
> >  void foo(...) { }
> >  void vfoo(TypeInfo[] arguments, va_list argptr) { }
> > Such functions should be added to std.stdio (or just make writex
public).
> <snip>
>
> Is it allowed for the two versions to be called the same?

Do you mean instead of
  void foo(...) { }
  void vfoo(TypeInfo[] arguments, va_list argptr) { }
have
  void foo(...) { }
  void foo(TypeInfo[] arguments, va_list argptr) { }
The problem is that there is an ambiguity since the code
 foo(_arguments, _argptr)
could match either one of the two functions. According to D's overloading
rules it would error.

> Or does D
> reject such potential backward incompatibility, no matter how likely or
> not someone really is to want to output an array of TypeInfos followed
> by a va_list?

Where is the backward incompatibility? I don't understand the problem. Can you give an example?



September 30, 2004
Ben Hinkle wrote:
<snip>
> I suppose - though I don't exactly know how the mechanism would work.
> For example would it be compatible with extern(C) variadic
> declarations:

Good question. But probably.

>  extern (C) printf(char* str, ...);
>  void foo(char* str,...) { printf(str,...); }
> Does the body of foo copy the inputs to foo from foo's stack frame so
> that the printf stack frame sees them in the right place? One benefit
> of the va_list declarations is that it is obvious a pointer is being
> passed around instead of stack frames being copied.

Good question. Maybe Walter or someone could comment....

<snip>
> Do you mean instead of
>   void foo(...) { }
>   void vfoo(TypeInfo[] arguments, va_list argptr) { }
> have
>   void foo(...) { }
>   void foo(TypeInfo[] arguments, va_list argptr) { }
> The problem is that there is an ambiguity since the code
>  foo(_arguments, _argptr)
> could match either one of the two functions. According to D's overloading
> rules it would error.

That's half what I said.  But does the spec indicate anywhere that matching '...' is classed as an exact match?

>> Or does D reject such potential backward incompatibility, no matter how likely or not someone really is to want to output an array of TypeInfos followed by a va_list?
> 
> Where is the backward incompatibility? I don't understand the
> problem. Can you give an example?

Suppose at first only

    void foo(...) ()

is defined.  And then someone tries calling it, meaning this form, with a TypeInfo[] and a va_list.  And then in a later version of the lib,

    void foo(TypeInfo[] arguments, va_list argptr) {}

is defined.  Then the effect of the call changes.

Stewart.
September 30, 2004
In article <cjf0hd$1chb$1@digitaldaemon.com>, Ben Hinkle says...
>
>Do you mean instead of
>  void foo(...) { }
>  void vfoo(TypeInfo[] arguments, va_list argptr) { }
>have
>  void foo(...) { }
>  void foo(TypeInfo[] arguments, va_list argptr) { }
>The problem is that there is an ambiguity since the code
> foo(_arguments, _argptr)
>could match either one of the two functions. According to D's overloading rules it would error.

Just a thought: this is another one of D's quirks that could use some attention. Suppose if the variadic function signatures were given the *lowest* priority for matching function overloads?  At the very least, it would be consistent with how D matches individual parameters where the specific case is investigated first.

version(DNG) pragma(EricAnderton,"yahoo");