www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Typesafe variable arguments proposal

reply J Anderson <REMOVEanderson badmama.com.au> writes:
Just starting the type safe variable arguments argument :) up again.

I proposed something like this ages ago, I've had a bit of time to 
reflect and so I'll propose it again.

Why not make variable arguments behave like templates:

template printT(T)
{
     void print(T t)
     {
        
     }
}


void print( ... data)
{
     foreach (... type; data)
     {
         printT!(typeof(data)).print(data);
     }
}

//Note you could change the ... to something else for inner function part.

Say I wrote something like:

int a;
char [] b;

print(a, b);

The compiler would produce a function like from the print template 
above, where data is a constant array.

void print(int data[0], char [] data[1]) //Of course arrays like this 
aren't possible at the moment
{
     foreach (... type; data) //Note this loop could be unrolled if the 
compiler felt like it.
     {
         printT!(typeof(data)).print(data);
     } 
}

Also, it should be possible to specialize these functions (by 
overloading them, with the exact parameters).

If you wanted to do the switch thing you could do it with if's like 
(which would easily be optimised):

    template nameof(T : bit) { char [] name() { return "bit"; } }
    template nameof(T : byte) { char [] name() { return "byte"; } }

void print( ... data)
{
     foreach (... type; data)
     {
         if (nameof(data) == "bit")
         {
                //...
          }
          else if (nameof(data) == "byte")
          {
               //...
          }
     }
}

But I wouldn't recommend it.

It would also be useful to write templates with ... arguments:

template funcT(... T)
{
    void func(T data)
    {
        //...
    }
}

alias funcT(int, char[]).func func;

-- 
-Anderson: http://badmama.com.au/~anderson/
May 03 2004
parent reply "Achilleas Margaritis" <axilmar in.gr> writes:
"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c75c6f$1sdd$1 digitaldaemon.com...
 Just starting the type safe variable arguments argument :) up again.

 I proposed something like this ages ago, I've had a bit of time to
 reflect and so I'll propose it again.

 Why not make variable arguments behave like templates:

 template printT(T)
 {
      void print(T t)
      {

      }
 }


 void print( ... data)
 {
      foreach (... type; data)
      {
          printT!(typeof(data)).print(data);
      }
 }

 //Note you could change the ... to something else for inner function part.

 Say I wrote something like:

 int a;
 char [] b;

 print(a, b);

 The compiler would produce a function like from the print template
 above, where data is a constant array.

 void print(int data[0], char [] data[1]) //Of course arrays like this
 aren't possible at the moment
 {
      foreach (... type; data) //Note this loop could be unrolled if the
 compiler felt like it.
      {
          printT!(typeof(data)).print(data);
      }
 }

 Also, it should be possible to specialize these functions (by
 overloading them, with the exact parameters).

 If you wanted to do the switch thing you could do it with if's like
 (which would easily be optimised):

     template nameof(T : bit) { char [] name() { return "bit"; } }
     template nameof(T : byte) { char [] name() { return "byte"; } }

 void print( ... data)
 {
      foreach (... type; data)
      {
          if (nameof(data) == "bit")
          {
                 //...
           }
           else if (nameof(data) == "byte")
           {
                //...
           }
      }
 }

 But I wouldn't recommend it.

 It would also be useful to write templates with ... arguments:

 template funcT(... T)
 {
     void func(T data)
     {
         //...
     }
 }

 alias funcT(int, char[]).func func;

 --
 -Anderson: http://badmama.com.au/~anderson/
I like the variant implementation better.
May 03 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Achilleas Margaritis wrote:

I like the variant implementation better.
  
How would you do UDT's with it? How would you extend it? -- -Anderson: http://badmama.com.au/~anderson/
May 03 2004
parent reply Achilleas Margaritis <Achilleas_member pathlink.com> writes:
In article <4096CC39.6090903 badmama.com.au>, J Anderson says...
Achilleas Margaritis wrote:

I like the variant implementation better.
  
How would you do UDT's with it? How would you extend it? -- -Anderson: http://badmama.com.au/~anderson/
What is there to extend ? The variant type would work for any D type, either primitive or user-defined. And by using RTTI, one could safely access the data. A variant type would also be useful in other situations; for example exchanging data without regard to the data type, implementing reflection etc. C++ can easily do variants using templates. Unfortunately, the standard vararg implementation ignores this possibility.
May 04 2004
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Achilleas Margaritis wrote:

In article <4096CC39.6090903 badmama.com.au>, J Anderson says...
  

Achilleas Margaritis wrote:

    

I like the variant implementation better.
 

      
How would you do UDT's with it? How would you extend it? -- -Anderson: http://badmama.com.au/~anderson/
What is there to extend ? The variant type would work for any D type, either primitive or user-defined. And by using RTTI, one could safely access the data.
Your thinking C++. Say to define a new type and you want to add it to printf (or whatever the new function is called). You can't go and change printf. With the version I propose you'd do something like this: module X; //You can't change this module void print( ... data) { foreach (... type; data) { printT!(typeof(data)).print(data); } } module Y; class A {} template printT(T : A) //Add printf for A { void print(T t) { ... } } int main() { A a; printf(a); return 1; }
A variant type would also be useful in other situations; for example exchanging
data without regard to the data type, implementing reflection etc.
  
I've already shown how a variant type could be done. It seems a variant type from your point of view contains extra run-time costs as well. If you wish ... could be called sequence. The difference is in the interpretation. You shouldn't need to use a switch statement but u can if your inclined. -- -Anderson: http://badmama.com.au/~anderson/
May 04 2004
parent reply Achilleas Margaritis <Achilleas_member pathlink.com> writes:
In article <c78334$2vl2$1 digitaldaemon.com>, J Anderson says...
Achilleas Margaritis wrote:

In article <4096CC39.6090903 badmama.com.au>, J Anderson says...
  

Achilleas Margaritis wrote:

    

I like the variant implementation better.
 

      
How would you do UDT's with it? How would you extend it? -- -Anderson: http://badmama.com.au/~anderson/
What is there to extend ? The variant type would work for any D type, either primitive or user-defined. And by using RTTI, one could safely access the data.
Your thinking C++. Say to define a new type and you want to add it to printf (or whatever the new function is called). You can't go and change printf.
If you want to add a new type to printf, you will have to change printf, because you will have to add a new format specifier.
With the version I propose you'd do something like this:

module X; //You can't change this module

void print( ... data)
{
    foreach (... type; data)
    {
        printT!(typeof(data)).print(data);
    }
}

module Y;

class A {}

template printT(T : A) //Add printf for A
{
    void print(T t)
    {
       ...
    }
}

int main()
{
   A a;
   printf(a);
   return 1;
}




A variant type would also be useful in other situations; for example exchanging
data without regard to the data type, implementing reflection etc.
  
I've already shown how a variant type could be done. It seems a variant type from your point of view contains extra run-time costs as well.
It is quite useful to have a variant type. Sometimes I don't need a variable number of parameters, and I don't care about the type.
If you wish ... could be called sequence.  The difference is in the 
interpretation.  You shouldn't need to use a switch statement but u can 
if your inclined.

-- 
-Anderson: http://badmama.com.au/~anderson/
What you are proposing is basically an automation of function overloading. The compiler would produce different versions of printf according to parameters. This can already be done manually. Do you really see a wide range of usage for such a capability to be embedded in the language ? the only place I see varargs useful is in printf, and I don't think printf execution is critical one...with your solution, an executable will be bloated with lots of printf versions; with the variants solution, only one printf needs to exist, being a little slower in run-time. Furthermore, with your solution, the variable argument list can't be passed as a parameter to another function; whereas in the variants solution, the array of variants can be passed as a parameter to another function. This is useful in printf-style functions, because only one function can be used for the decoding, parameterized with the type of output. The variant solution solves the problems of the C-style vararg solution: type recognition and the number of parameters. And the existence of a 'variant' type gives plenty of room to implement type-agnostic architectures: drag-n-drop, reflection, COM data exchange, etc.
May 04 2004
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Achilleas Margaritis wrote:

In article <c78334$2vl2$1 digitaldaemon.com>, J Anderson says...
  

Achilleas Margaritis wrote:

    

In article <4096CC39.6090903 badmama.com.au>, J Anderson says...
 

      

Achilleas Margaritis wrote:

   

        

I like the variant implementation better.


     

          
How would you do UDT's with it? How would you extend it? -- -Anderson: http://badmama.com.au/~anderson/
What is there to extend ? The variant type would work for any D type, either primitive or user-defined. And by using RTTI, one could safely access the data.
Your thinking C++. Say to define a new type and you want to add it to printf (or whatever the new function is called). You can't go and change printf.
If you want to add a new type to printf, you will have to change printf, because you will have to add a new format specifier.
With the version I propose you'd do something like this:

module X; //You can't change this module

void print( ... data)
{
   foreach (... type; data)
   {
       printT!(typeof(data)).print(data);
   }
}

module Y;

class A {}

template printT(T : A) //Add printf for A
{
   void print(T t)
   {
      ...
   }
}

int main()
{
  A a;
  printf(a);
  return 1;
}




    

A variant type would also be useful in other situations; for example exchanging
data without regard to the data type, implementing reflection etc.
 

      
I've already shown how a variant type could be done. It seems a variant type from your point of view contains extra run-time costs as well.
It is quite useful to have a variant type. Sometimes I don't need a variable number of parameters, and I don't care about the type.
If you wish ... could be called sequence.  The difference is in the 
interpretation.  You shouldn't need to use a switch statement but u can 
if your inclined.

-- 
-Anderson: http://badmama.com.au/~anderson/
    
What you are proposing is basically an automation of function overloading. The compiler would produce different versions of printf according to parameters. This can already be done manually. Do you really see a wide range of usage for such a capability to be embedded in the language ? the only place I see varargs useful is in printf, and I don't think printf execution is critical one...with your solution, an executable will be bloated with lots of printf versions; with the variants solution, only one printf needs to exist, being a little slower in run-time. Furthermore, with your solution, the variable argument list can't be passed as a parameter to another function; whereas in the variants solution, the array of variants can be passed as a parameter to another function. This is useful in printf-style functions, because only one function can be used for the decoding, parameterized with the type of output. The variant solution solves the problems of the C-style vararg solution: type recognition and the number of parameters. And the existence of a 'variant' type gives plenty of room to implement type-agnostic architectures: drag-n-drop, reflection, COM data exchange, etc.
I never said it couldn't be run-time. The compiler would choose. Also ... in the for-loop was the variant type. You could also write: class A { ... b; } Sequence could be a better name though. -- -Anderson: http://badmama.com.au/~anderson/
May 04 2004
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
J Anderson wrote:

 Achilleas Margaritis wrote:

 What you are proposing is basically an automation of function 
 overloading. The
 compiler would produce different versions of printf according to 
 parameters.
 This can already be done manually. Do you really see a wide range of 
 usage for
 such a capability to be embedded in the language ? the only place I 
 see varargs
 useful is in printf, and I don't think printf execution is critical 
 one...with
 your solution, an executable will be bloated with lots of printf 
 versions; with
 the variants solution, only one printf needs to exist, being a little 
 slower in
 run-time.

 Furthermore, with your solution, the variable argument list can't be 
 passed as a
 parameter to another function; whereas in the variants solution, the 
 array of
 variants can be passed as a parameter to another function. This is 
 useful in
 printf-style functions, because only one function can be used for the 
 decoding,
 parameterized with the type of output. The variant solution solves 
 the problems
 of the C-style vararg solution: type recognition and the number of 
 parameters.

 And the existence of a 'variant' type gives plenty of room to implement
 type-agnostic architectures: drag-n-drop, reflection, COM data 
 exchange, etc.
  
I never said it couldn't be run-time. The compiler would choose.
I should add that the compiler would staticly type check everything, before making use of duplicate code. Reflection would be easly supported with static sized arrays. Com -> write your own variable array. I guess there could be a dynamic version as well. But I doubt there would be much *bloat* even if the compiler rolled out all these functions, particularly because there's a greater chance of in lining things. You would end up with a much more efficient design.
 Also ... in the for-loop was the variant type.  You could also write:

 class A
 {
   ... b;

 }

 Sequence could be a better name though.
-- -Anderson: http://badmama.com.au/~anderson/
May 04 2004
parent reply "Achilleas Margaritis" <axilmar in.gr> writes:
"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c79jvi$29ct$1 digitaldaemon.com...
 J Anderson wrote:

 Achilleas Margaritis wrote:

 What you are proposing is basically an automation of function
 overloading. The
 compiler would produce different versions of printf according to
 parameters.
 This can already be done manually. Do you really see a wide range of
 usage for
 such a capability to be embedded in the language ? the only place I
 see varargs
 useful is in printf, and I don't think printf execution is critical
 one...with
 your solution, an executable will be bloated with lots of printf
 versions; with
 the variants solution, only one printf needs to exist, being a little
 slower in
 run-time.

 Furthermore, with your solution, the variable argument list can't be
 passed as a
 parameter to another function; whereas in the variants solution, the
 array of
 variants can be passed as a parameter to another function. This is
 useful in
 printf-style functions, because only one function can be used for the
 decoding,
 parameterized with the type of output. The variant solution solves
 the problems
 of the C-style vararg solution: type recognition and the number of
 parameters.

 And the existence of a 'variant' type gives plenty of room to implement
 type-agnostic architectures: drag-n-drop, reflection, COM data
 exchange, etc.
I never said it couldn't be run-time. The compiler would choose.
I should add that the compiler would staticly type check everything, before making use of duplicate code. Reflection would be easly supported with static sized arrays. Com -> write your own variable array. I guess there could be a dynamic version as well. But I doubt there would be much *bloat* even if the compiler rolled out all these functions, particularly because there's a greater chance of in lining things. You would end up with a much more efficient design.
 Also ... in the for-loop was the variant type.  You could also write:

 class A
 {
   ... b;

 }

 Sequence could be a better name though.
-- -Anderson: http://badmama.com.au/~anderson/
I don't think efficiency plays any role here. It is not that printf is used in code where efficiency matters. On the other hand, code size matters. Imagine a program where there are lots of trace output: each different trace will be a different function. And there may be thousands (for example, one of the applications in the place I work, it is 120,000 lines of code, and it has over 10,000 TRACE lines; it is a DIS simulation with lots of custom protocols used in radars).
May 05 2004
parent J Anderson <REMOVEanderson badmama.com.au> writes:
Achilleas Margaritis wrote:

I don't think efficiency plays any role here. It is not that printf is used
in code where efficiency matters.

On the other hand, code size matters. Imagine a program where there are lots
of trace output: each different trace will be a different function. And
there may be thousands (for example, one of the applications in the place I
work, it is 120,000 lines of code, and it has over 10,000 TRACE lines; it is
a DIS simulation with lots of custom protocols used in radars).

  
I don't buy that, not for function arguments. You can already write your own dynamic variable arrays. That is something that could be in stl. Besides even D is able to make use of repetitive codes in templates now. The compiler should be able to decide if it is going to expand (roll-out) the function or not. It's the static type checking that is most important. -- -Anderson: http://badmama.com.au/~anderson/
May 05 2004
prev sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Achilleas Margaritis wrote:

"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c75c6f$1sdd$1 digitaldaemon.com...
  

Just starting the type safe variable arguments argument :) up again.

I proposed something like this ages ago, I've had a bit of time to
reflect and so I'll propose it again.

Why not make variable arguments behave like templates:

template printT(T)
{
     void print(T t)
     {

     }
}


void print( ... data)
{
     foreach (... type; data)
     {
         printT!(typeof(data)).print(data);
     }
}

//Note you could change the ... to something else for inner function part.

Say I wrote something like:

int a;
char [] b;

print(a, b);

The compiler would produce a function like from the print template
above, where data is a constant array.

void print(int data[0], char [] data[1]) //Of course arrays like this
aren't possible at the moment
{
     foreach (... type; data) //Note this loop could be unrolled if the
compiler felt like it.
     {
         printT!(typeof(data)).print(data);
     }
}

Also, it should be possible to specialize these functions (by
overloading them, with the exact parameters).

If you wanted to do the switch thing you could do it with if's like
(which would easily be optimised):

    template nameof(T : bit) { char [] name() { return "bit"; } }
    template nameof(T : byte) { char [] name() { return "byte"; } }

void print( ... data)
{
     foreach (... type; data)
     {
         if (nameof(data) == "bit")
         {
                //...
          }
          else if (nameof(data) == "byte")
          {
               //...
          }
     }
}

But I wouldn't recommend it.

It would also be useful to write templates with ... arguments:

template funcT(... T)
{
    void func(T data)
    {
        //...
    }
}

alias funcT(int, char[]).func func;

--
-Anderson: http://badmama.com.au/~anderson/
    
I like the variant implementation better.
You could also do switch statements with my proposal. Another difference I see is mine allows for a more abstract level of programming. template nameof(T : bit) { char [] name() { return "bit"; } } //Would would probably be in the std/language eventually template nameof(T : byte) { char [] name() { return "byte"; } } void print( ... data) { foreach (... type; data) { switch (nameof(data)) { case "bit" break; case "byte" break; } } } I still don't recommend the switch way of doing things. It's non-pluginable and it just looks like reflection to me. -- -Anderson: http://badmama.com.au/~anderson/
May 03 2004
parent "Achilleas Margaritis" <axilmar in.gr> writes:
"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c76q23$12h5$1 digitaldaemon.com...
 Achilleas Margaritis wrote:

"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c75c6f$1sdd$1 digitaldaemon.com...


Just starting the type safe variable arguments argument :) up again.

I proposed something like this ages ago, I've had a bit of time to
reflect and so I'll propose it again.

Why not make variable arguments behave like templates:

template printT(T)
{
     void print(T t)
     {

     }
}


void print( ... data)
{
     foreach (... type; data)
     {
         printT!(typeof(data)).print(data);
     }
}

//Note you could change the ... to something else for inner function
part.
Say I wrote something like:

int a;
char [] b;

print(a, b);

The compiler would produce a function like from the print template
above, where data is a constant array.

void print(int data[0], char [] data[1]) //Of course arrays like this
aren't possible at the moment
{
     foreach (... type; data) //Note this loop could be unrolled if the
compiler felt like it.
     {
         printT!(typeof(data)).print(data);
     }
}

Also, it should be possible to specialize these functions (by
overloading them, with the exact parameters).

If you wanted to do the switch thing you could do it with if's like
(which would easily be optimised):

    template nameof(T : bit) { char [] name() { return "bit"; } }
    template nameof(T : byte) { char [] name() { return "byte"; } }

void print( ... data)
{
     foreach (... type; data)
     {
         if (nameof(data) == "bit")
         {
                //...
          }
          else if (nameof(data) == "byte")
          {
               //...
          }
     }
}

But I wouldn't recommend it.

It would also be useful to write templates with ... arguments:

template funcT(... T)
{
    void func(T data)
    {
        //...
    }
}

alias funcT(int, char[]).func func;

--
-Anderson: http://badmama.com.au/~anderson/
I like the variant implementation better.
You could also do switch statements with my proposal. Another difference I see is mine allows for a more abstract level of programming. template nameof(T : bit) { char [] name() { return "bit"; } } //Would would probably be in the std/language eventually template nameof(T : byte) { char [] name() { return "byte"; } } void print( ... data) { foreach (... type; data) { switch (nameof(data)) { case "bit" break; case "byte" break; } } } I still don't recommend the switch way of doing things. It's non-pluginable and it just looks like reflection to me.
It is reflection.
May 05 2004