Thread overview
varargs when they're not all the same type?
Mar 14
monkyyy
Mar 14
Basile B.
March 14
Can somebody give me a starting point for understanding varadic functions?  I know that we can declare them

  int[] args...

and pick through whatever the caller provided.  But if the caller wants to pass two int's and a _string_?  That declaration won't permit it.

I've looked into the formatter, and also the varargs implementation.  But it's a bit of a trip through a funhouse full of mirrors.  Can somebody describe the basic language approach to non-uniform varargs, and then I can take it the rest of the way reading the library.

Thanks in advance!
Andy

March 14
On Thursday, 14 March 2024 at 17:57:21 UTC, Andy Valencia wrote:
> Can somebody give me a starting point for understanding varadic functions?  I know that we can declare them
>
>   int[] args...
>
> and pick through whatever the caller provided.  But if the caller wants to pass two int's and a _string_?  That declaration won't permit it.
>
> I've looked into the formatter, and also the varargs implementation.  But it's a bit of a trip through a funhouse full of mirrors.  Can somebody describe the basic language approach to non-uniform varargs, and then I can take it the rest of the way reading the library.
>
> Thanks in advance!
> Andy

there are 3 (or 4?) variadic functions
try
```d
auto foo(T...)(T args){
  static foreach(arg;args){
    ...
```
March 14
On Thu, Mar 14, 2024 at 05:57:21PM +0000, Andy Valencia via Digitalmars-d-learn wrote:
> Can somebody give me a starting point for understanding varadic functions?  I know that we can declare them
> 
>   int[] args...
> 
> and pick through whatever the caller provided.  But if the caller wants to pass two int's and a _string_?  That declaration won't permit it.
> 
> I've looked into the formatter, and also the varargs implementation. But it's a bit of a trip through a funhouse full of mirrors.  Can somebody describe the basic language approach to non-uniform varargs, and then I can take it the rest of the way reading the library.
[...]

The best way to do multi-type varags in D is to use templates:

	import std;
	void myFunc(Args...)(Args args) {
		foreach (i, arg; args) {
			writefln("parameter %d is a %s with value %s",
				i, typeof(arg), arg);
		}
	}

	void main() {
		myFunc(123, 3.14159, "blah blah", [ 1, 2, 3 ], new Object());
	}

D also supports C-style varags (without templates), but it's not recommended because it's not type-safe. You can find the description in the language docs.


T

-- 
"Maybe" is a strange word.  When mom or dad says it it means "yes", but when my big brothers say it it means "no"! -- PJ jr.
March 14
On Thursday, 14 March 2024 at 18:05:59 UTC, H. S. Teoh wrote:
> ...
> The best way to do multi-type varags in D is to use templates:
>
> 	import std;
> 	void myFunc(Args...)(Args args) {

Thank you.  The first parenthetical list is of types, is it not?  I can't find anywhere which says what "type" is inferred for "Args..."?  (gdb pretends like "arg" is not a known symbol.)  Is it basically a tuple of the suitable type?

Andy

March 14

On Thursday, 14 March 2024 at 20:58:21 UTC, Andy Valencia wrote:

>

On Thursday, 14 March 2024 at 18:05:59 UTC, H. S. Teoh wrote:

>

...
The best way to do multi-type varags in D is to use templates:

import std;
void myFunc(Args...)(Args args) {

Thank you. The first parenthetical list is of types, is it not? I can't find anywhere which says what "type" is inferred for "Args..."? (gdb pretends like "arg" is not a known symbol.) Is it basically a tuple of the suitable type?

Andy

Most of the time the variadic template parameters are infered from the run time parameters. In that case indeed Args will be a type tuple of args types.

void myFunc(Args...)(Args args) {}
myFunc(0,0.1); // is like the more verbose `myFunc!(int,double)(0,0.1)`

However explicit instantiation can take whatever is known at compile time, such as constant expressions or even certain static variables. So that is rather called an alias sequence in D.

That being said and with the signature of myFunc that will effectively only work if Args is made of types.

About debugging, each individual runtime arg can be inspected using a bit of knowledge of D internals.

As you can see the elements are named following this pattern __param_[0-9]+.
So with gdb used as CLI, $ p __param_0 will (🤞🤞) print the first variadic element, and so on.

March 15

On Thursday, 14 March 2024 at 23:13:51 UTC, Basile B. wrote:

>

...
However explicit instantiation can take whatever is known at compile time, such as constant expressions or even certain static variables. So that is rather called an alias sequence in D.

Which statement leads me to section 77.2 of "Programming in D", and now I am deep into the mechanisms behind what you have very kindly shared. Thank you once more.

Andy

March 14
On Thu, Mar 14, 2024 at 08:58:21PM +0000, Andy Valencia via Digitalmars-d-learn wrote:
> On Thursday, 14 March 2024 at 18:05:59 UTC, H. S. Teoh wrote:
> > ...
> > The best way to do multi-type varags in D is to use templates:
> > 
> > 	import std;
> > 	void myFunc(Args...)(Args args) {
> 
> Thank you.  The first parenthetical list is of types, is it not?  I can't find anywhere which says what "type" is inferred for "Args..."? (gdb pretends like "arg" is not a known symbol.)  Is it basically a tuple of the suitable type?
[...]

The first set of parenthesis specify compile-time arguments. The specification `Args...` means "zero or more types".  So it could be any list of types, which naturally would be chosen according to the arguments given. For example, to pass an int and a float, you'd do something like:

	myFunc!(int, float)(123, 3.14159f);

and to pass a string, two ints, and a char, you'd write:

	myFunc!(string, int, int, char)("abc", 123, 456, 'z');

Having to specify types manually, of course, is a lot of unnecessary typing, since the compiler already knows what the types are based on what you write in the second pair of parentheses.  For this reason, typical D code will omit the first pair of parentheses (the `!(...)`, that is, the compile-time arguments) and just let the compiler infer the types automatically:

	myFunc(123, 3.14159f); // compiler figures out Args = (int, float)
	myFunc("abc", 123, 456, 'z'); // compiler figures out Args = (string, int, int, char)


T

-- 
A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. -- Michael B. Allen
March 16

On Friday, 15 March 2024 at 00:11:11 UTC, Andy Valencia wrote:

(varargs & friends)

>

Which statement leads me to section 77.2 of "Programming in D", and now I am deep into the mechanisms behind what you have very kindly shared. Thank you once more.

As some fruits of my labors here, below is a link to a "fmt" module which does C-style formatting. It supports int/long signed/unsigned, right/left padding and zero padding, plus strings (w. padding). It's memory and type safe; I ended up using unions to tabulate the arguments as I need to access them as an array (rather than walking them--I'm walking the format string instead). It adds 6k to an executable, which means dlang will work out fine for all of my smaller scripting needs in the future.

Calls look like:

auto s = fmt("%d %u - %20s", 123, 456, "Hi, Mom");

https://sources.vsta.org:7100/dlang/file?name=fmt.d&ci=tip

Comments are welcome! I'd post here, but it seems a little long for that?

Andy