Thread overview
Variadic functions again
Jun 09, 2004
Andrew Edwards
Jun 09, 2004
J C Calvarese
Jun 09, 2004
Derek Parnell
Jun 09, 2004
Ben Hinkle
Jun 09, 2004
Andrew Edwards
June 09, 2004
This is an attempt to generate conversation on variadic functions, the
implementation of which would ultimately lesson our search for an
adequate solution to the D I/O issue.

Note: I'm often uncomfortable referring to arguments and parameters in
      speech and writing so I'll be referring to all such items as
      arguments.

------print.d------
int main()
{
  double d;
  char c;
  print(d,c);
}

void print(char[] start, ...)
{
}

-------------------
compiler errors messages:
print.d(5): function print (char[]start,...) does not match argument
types (double,char)
print.d(5): cannot implicitly convert double to char[]

Given the function print(), as defined above, it is clear to see (from
the generated error messages) that the compiler is capable of not only
discerning the total numbers of arguments used to call that function,
but also the type of each of those arguments.

IMHO it wouldn't be too unreasonable to assume that in order for it to
gather this information, the compiler must know beforehand where each of
those arguments are stored (i.e. the address of each argument) in
memory.

Now suppose for a moment that we had a built-in type-pointer, called
"variant", that points to and assumes the characteristics of any, and
all, other built-in types. Making the following legal:

  variant variable; // value automatically dereferenced to the value of
                    // whatever it points to. (Does NOT return an
                    // address and is NOT a copy!)
  void space;
  char[] string;
  int i; double j; char c; etc...

  variable = space;  // typeof(variable) -> void
  variable = string; // typeof(variable) -> char[]
  variable = i;      // typeof(variable) -> int
  variable = j;      // typeof(variable) -> double
  variable = c;      // typeof(variable) -> char
  etc...

Wouldn't this allow us to deal with variables at compile time without
knowing it's actual type?

Hold that thought for a moment!

Review of the stdarg functions reveal that variable arguments are
arranged sequentially in memory. Simply knowing the size of each argument
allows us to move from one memory location to another within the
argument list.

So what if we stored those arguments in an array (call it "sequence" if
you will) internally to the compiler whenever a variadic function is
encountered? [e.g. echo(char[] v, int i, char c, ...) or scan(...), but
               not foo(char ch) or bar()]

Wouldn't we be able to access each argument individually through array
indexing (akin to accessing arguments passed in to the main() function)?

Now suppose the array was an associative array:

  variant[int] sequence; // syntactic sugar for this would be ...

Wouldn't we be able to foreach through each of those arguments and
manipulate them without regard to type?

This functionality could drastically reduce our search for a proper I/O
facility for The D Language and completely eliminate the problem of
traversing arguments of a variadic function (thereby eliminating the
need for std.c.stdarg).

Your thoughts and opinions are greatly appreciated.

If I'm somewhere off in coocoo land, please don't be afraid to say so!!!

Thanks,
Andrew
June 09, 2004
Andrew Edwards wrote:
> This is an attempt to generate conversation on variadic functions, the
> implementation of which would ultimately lesson our search for an
> adequate solution to the D I/O issue.
> 
> Note: I'm often uncomfortable referring to arguments and parameters in
>       speech and writing so I'll be referring to all such items as
>       arguments.
> 
> ------print.d------
> int main()
> {
>   double d;
>   char c;
>   print(d,c);
> }
> 
> void print(char[] start, ...)
> {
> }
> 
> -------------------
> compiler errors messages:
> print.d(5): function print (char[]start,...) does not match argument
> types (double,char)
> print.d(5): cannot implicitly convert double to char[]
> 
> Given the function print(), as defined above, it is clear to see (from
> the generated error messages) that the compiler is capable of not only
> discerning the total numbers of arguments used to call that function,
> but also the type of each of those arguments.
> 
> IMHO it wouldn't be too unreasonable to assume that in order for it to
> gather this information, the compiler must know beforehand where each of
> those arguments are stored (i.e. the address of each argument) in
> memory.
> 
> Now suppose for a moment that we had a built-in type-pointer, called
> "variant", that points to and assumes the characteristics of any, and
> all, other built-in types. Making the following legal:
> 
>   variant variable; // value automatically dereferenced to the value of
>                     // whatever it points to. (Does NOT return an
>                     // address and is NOT a copy!)
>   void space;
>   char[] string;
>   int i; double j; char c; etc...
> 
>   variable = space;  // typeof(variable) -> void
>   variable = string; // typeof(variable) -> char[]
>   variable = i;      // typeof(variable) -> int
>   variable = j;      // typeof(variable) -> double
>   variable = c;      // typeof(variable) -> char
>   etc...
> 
> Wouldn't this allow us to deal with variables at compile time without
> knowing it's actual type?
> 
> Hold that thought for a moment!
> 
> Review of the stdarg functions reveal that variable arguments are
> arranged sequentially in memory. Simply knowing the size of each argument
> allows us to move from one memory location to another within the
> argument list.
> 
> So what if we stored those arguments in an array (call it "sequence" if
> you will) internally to the compiler whenever a variadic function is
> encountered? [e.g. echo(char[] v, int i, char c, ...) or scan(...), but
>                not foo(char ch) or bar()]
> 
> Wouldn't we be able to access each argument individually through array
> indexing (akin to accessing arguments passed in to the main() function)?
> 
> Now suppose the array was an associative array:
> 
>   variant[int] sequence; // syntactic sugar for this would be ...
> 
> Wouldn't we be able to foreach through each of those arguments and
> manipulate them without regard to type?
> 
> This functionality could drastically reduce our search for a proper I/O
> facility for The D Language and completely eliminate the problem of
> traversing arguments of a variadic function (thereby eliminating the
> need for std.c.stdarg).
> 
> Your thoughts and opinions are greatly appreciated.
> 
> If I'm somewhere off in coocoo land, please don't be afraid to say so!!!
> 
> Thanks,
> Andrew

I think a built-in variant type would be Fantastic!

It would also help us seduce programmers from all kinds of languages that have easy variant support, such as Visual Basic, VBScript, JavaScript, JScript, etc. (Of course some might also consider this a disadvantage. ;) )

Now we just need to twist Walter's arm...

-- 
Justin (a/k/a jcc7)
http://jcc_7.tripod.com/d/
June 09, 2004
On Tue, 08 Jun 2004 21:54:15 -0400, Andrew Edwards wrote:

> This is an attempt to generate conversation on variadic functions, the implementation of which would ultimately lesson our search for an adequate solution to the D I/O issue.
> 
> Note: I'm often uncomfortable referring to arguments and parameters in
>        speech and writing so I'll be referring to all such items as
>        arguments.
> 
> ------print.d------
> int main()
> {
>    double d;
>    char c;
>    print(d,c);
> }
> 
> void print(char[] start, ...)
> {
> }
> 
> -------------------
> compiler errors messages:
> print.d(5): function print (char[]start,...) does not match argument
> types (double,char)
> print.d(5): cannot implicitly convert double to char[]
> 
> Given the function print(), as defined above, it is clear to see (from the generated error messages) that the compiler is capable of not only discerning the total numbers of arguments used to call that function, but also the type of each of those arguments.
> 
> IMHO it wouldn't be too unreasonable to assume that in order for it to gather this information, the compiler must know beforehand where each of those arguments are stored (i.e. the address of each argument) in memory.
> 
> Now suppose for a moment that we had a built-in type-pointer, called "variant", that points to and assumes the characteristics of any, and all, other built-in types. Making the following legal:
> 
>    variant variable; // value automatically dereferenced to the value of
>                      // whatever it points to. (Does NOT return an
>                      // address and is NOT a copy!)
>    void space;
>    char[] string;
>    int i; double j; char c; etc...
> 
>    variable = space;  // typeof(variable) -> void
>    variable = string; // typeof(variable) -> char[]
>    variable = i;      // typeof(variable) -> int
>    variable = j;      // typeof(variable) -> double
>    variable = c;      // typeof(variable) -> char
>    etc...
> 
> Wouldn't this allow us to deal with variables at compile time without knowing it's actual type?
> 
> Hold that thought for a moment!
> 
> Review of the stdarg functions reveal that variable arguments are arranged sequentially in memory. Simply knowing the size of each argument allows us to move from one memory location to another within the argument list.
> 
> So what if we stored those arguments in an array (call it "sequence" if
> you will) internally to the compiler whenever a variadic function is
> encountered? [e.g. echo(char[] v, int i, char c, ...) or scan(...), but
>                 not foo(char ch) or bar()]
> 
> Wouldn't we be able to access each argument individually through array
> indexing (akin to accessing arguments passed in to the main() function)?
> 
> Now suppose the array was an associative array:
> 
>    variant[int] sequence; // syntactic sugar for this would be ...
> 
> Wouldn't we be able to foreach through each of those arguments and manipulate them without regard to type?
> 
> This functionality could drastically reduce our search for a proper I/O facility for The D Language and completely eliminate the problem of traversing arguments of a variadic function (thereby eliminating the need for std.c.stdarg).
> 
> Your thoughts and opinions are greatly appreciated.
> 
> If I'm somewhere off in coocoo land, please don't be afraid to say so!!!
> 
> Thanks,
> Andrew

Love it!

I'm *so* used to working with polymorphic variables that I know I'll be missing them in D. And your idea will not cause compiled programs that DO NOT use the variant type to slow down any either. So, if a coder chooses to use a variant datatye then they accept the (slight) performance hit in order to use a *much* easier programming paradigm.

I assume that we could work with classes and structures in this scheme too.

-- 
Derek
Melbourne, Australia
9/Jun/04 2:36:25 PM
June 09, 2004
Andrew Edwards wrote:

> This is an attempt to generate conversation on variadic functions, the implementation of which would ultimately lesson our search for an adequate solution to the D I/O issue.
> 
> Note: I'm often uncomfortable referring to arguments and parameters in
>        speech and writing so I'll be referring to all such items as
>        arguments.
> 
> ------print.d------
> int main()
> {
>    double d;
>    char c;
>    print(d,c);
> }
> 
> void print(char[] start, ...)
> {
> }
> 
> -------------------
> compiler errors messages:
> print.d(5): function print (char[]start,...) does not match argument
> types (double,char)
> print.d(5): cannot implicitly convert double to char[]
> 
> Given the function print(), as defined above, it is clear to see (from the generated error messages) that the compiler is capable of not only discerning the total numbers of arguments used to call that function, but also the type of each of those arguments.
> 
> IMHO it wouldn't be too unreasonable to assume that in order for it to gather this information, the compiler must know beforehand where each of those arguments are stored (i.e. the address of each argument) in memory.
> 
> Now suppose for a moment that we had a built-in type-pointer, called "variant", that points to and assumes the characteristics of any, and all, other built-in types. Making the following legal:
> 
>    variant variable; // value automatically dereferenced to the value of
>                      // whatever it points to. (Does NOT return an
>                      // address and is NOT a copy!)
>    void space;
>    char[] string;
>    int i; double j; char c; etc...
> 
>    variable = space;  // typeof(variable) -> void
>    variable = string; // typeof(variable) -> char[]
>    variable = i;      // typeof(variable) -> int
>    variable = j;      // typeof(variable) -> double
>    variable = c;      // typeof(variable) -> char
>    etc...
> 
> Wouldn't this allow us to deal with variables at compile time without knowing it's actual type?
> 
> Hold that thought for a moment!
> 
> Review of the stdarg functions reveal that variable arguments are arranged sequentially in memory. Simply knowing the size of each argument allows us to move from one memory location to another within the argument list.
> 
> So what if we stored those arguments in an array (call it "sequence" if
> you will) internally to the compiler whenever a variadic function is
> encountered? [e.g. echo(char[] v, int i, char c, ...) or scan(...), but
>                 not foo(char ch) or bar()]
> 
> Wouldn't we be able to access each argument individually through array
> indexing (akin to accessing arguments passed in to the main() function)?
> 
> Now suppose the array was an associative array:
> 
>    variant[int] sequence; // syntactic sugar for this would be ...
>
> Wouldn't we be able to foreach through each of those arguments and manipulate them without regard to type?
>
> This functionality could drastically reduce our search for a proper I/O facility for The D Language and completely eliminate the problem of traversing arguments of a variadic function (thereby eliminating the need for std.c.stdarg).
> 
> Your thoughts and opinions are greatly appreciated.
> 
> If I'm somewhere off in coocoo land, please don't be afraid to say so!!!
> 
> Thanks,
> Andrew

three thoughts come to mind:
1) variant feels very close to Object except variant also works for
primitive types (reminds me of post long ago about boxing/unboxing or
wrapping primitive types)
2) have you looked into using a struct like
  struct variant {
    TypeInfo* ti;
    void* data;
  }
3) variant is somewhat like a plain old void* since it isn't clear what
"manipulate them without regard to type" would mean in a compiled language.
What operations should be supported by a variant? addition? toString?
printf?

I think this is an interesting topic but is hard to work into a compiled language where there isn't any RTTI for primitive types.
June 09, 2004
In article <ca70oe$2adn$1@digitaldaemon.com>, Ben Hinkle says...
>
>Andrew Edwards wrote:
>
>> This is an attempt to generate conversation on variadic functions, the implementation of which would ultimately lesson our search for an adequate solution to the D I/O issue.
>> 
>> Note: I'm often uncomfortable referring to arguments and parameters in
>>        speech and writing so I'll be referring to all such items as
>>        arguments.
>> 
>> ------print.d------
>> int main()
>> {
>>    double d;
>>    char c;
>>    print(d,c);
>> }
>> 
>> void print(char[] start, ...)
>> {
>> }
>> 
>> -------------------
>> compiler errors messages:
>> print.d(5): function print (char[]start,...) does not match argument
>> types (double,char)
>> print.d(5): cannot implicitly convert double to char[]
>> 
>> Given the function print(), as defined above, it is clear to see (from the generated error messages) that the compiler is capable of not only discerning the total numbers of arguments used to call that function, but also the type of each of those arguments.
>> 
>> IMHO it wouldn't be too unreasonable to assume that in order for it to gather this information, the compiler must know beforehand where each of those arguments are stored (i.e. the address of each argument) in memory.
>> 
>> Now suppose for a moment that we had a built-in type-pointer, called "variant", that points to and assumes the characteristics of any, and all, other built-in types. Making the following legal:
>> 
>>    variant variable; // value automatically dereferenced to the value of
>>                      // whatever it points to. (Does NOT return an
>>                      // address and is NOT a copy!)
>>    void space;
>>    char[] string;
>>    int i; double j; char c; etc...
>> 
>>    variable = space;  // typeof(variable) -> void
>>    variable = string; // typeof(variable) -> char[]
>>    variable = i;      // typeof(variable) -> int
>>    variable = j;      // typeof(variable) -> double
>>    variable = c;      // typeof(variable) -> char
>>    etc...
>> 
>> Wouldn't this allow us to deal with variables at compile time without knowing it's actual type?
>> 
>> Hold that thought for a moment!
>> 
>> Review of the stdarg functions reveal that variable arguments are arranged sequentially in memory. Simply knowing the size of each argument allows us to move from one memory location to another within the argument list.
>> 
>> So what if we stored those arguments in an array (call it "sequence" if
>> you will) internally to the compiler whenever a variadic function is
>> encountered? [e.g. echo(char[] v, int i, char c, ...) or scan(...), but
>>                 not foo(char ch) or bar()]
>> 
>> Wouldn't we be able to access each argument individually through array
>> indexing (akin to accessing arguments passed in to the main() function)?
>> 
>> Now suppose the array was an associative array:
>> 
>>    variant[int] sequence; // syntactic sugar for this would be ...
>>
>> Wouldn't we be able to foreach through each of those arguments and manipulate them without regard to type?
>>
>> This functionality could drastically reduce our search for a proper I/O facility for The D Language and completely eliminate the problem of traversing arguments of a variadic function (thereby eliminating the need for std.c.stdarg).
>> 
>> Your thoughts and opinions are greatly appreciated.
>> 
>> If I'm somewhere off in coocoo land, please don't be afraid to say so!!!
>> 
>> Thanks,
>> Andrew
>
>three thoughts come to mind:
>1) variant feels very close to Object except variant also works for
>primitive types (reminds me of post long ago about boxing/unboxing or
>wrapping primitive types)

The idea is for it to be a self-dereferencing pointer (similar to the &var of C++ or the inout parameter of D). Modifying the variant directly affects the variable to which it points, but it should facilitate pointing to a different variable later on.

>2) have you looked into using a struct like
>  struct variant {
>    TypeInfo* ti;
>    void* data;
>  }

As a matter of fact I have. This, however, does not fit the bill. The variant should assume the characteristics of the variable to which it points.

double trouble;  // initial value nan
variant var;     // initial value null
var = trouble;   // var takes the address of trouble (var == nan)
// maybe we could recall the adressof operator (&)?
var = 6.66;      // trouble = 6.66

typeof(var) foo; // foo is of type double

printf(toString(var));  // calls toString(double)

>3) variant is somewhat like a plain old void* since it isn't clear what "manipulate them without regard to type" would mean in a compiled language. What operations should be supported by a variant? addition? toString? printf?

Not quite! "variant" is like a void* in the sense that it can point to anything. However, unlike void*, once it points to a variable it assumes all characteristics of that variable. If it points to an int, you can treat the variant as if it were an int. All changes made to the variant would <strong>*directly*</strong> affect the variable to which it points. Absolutely no need for casting as with void*s.

>I think this is an interesting topic but is hard to work into a compiled language where there isn't any RTTI for primitive types.

This is where I get confused. It is also the purpose of the little example provided earlier. If the compiler is capable of identifying that the incorrect number and type of arguments (and vice-versa) are used to call a function, isn't that enought information to determine the type of variable to which a variant points?