Variable argument count to functions

New to FreeBASIC? Post your questions here.
Post Reply
GWBASICcoder
Posts: 21
Joined: Jul 29, 2022 12:43

Variable argument count to functions

Post by GWBASICcoder »

Hi All,

I have a linked list UDT with a push method that takes in one string argument.
I am trying to use varargs to send a list of strings to a sub that iterates over the list
and calls the UDT's push method for each string. I cannot use the first argument
as an argument count.

I could use the __FB_ARG_COUNT__ intrinsic macro but don't know how to use
it in a sub/function.

My expectation here is that cva_arg(args, zstring ptr) will return a valid zstring ptr
for each argument passed or null when all arguments have been iterated over.

Code: Select all

sub pushstrings (s as string, ...)
    dim args as cva_list
    cva_start(args, s)
	dim zsp as zstring ptr = 0
	print s
	'call list.push
	zsp = cva_arg(args, zstring ptr)
	do while zsp <> 0
		print *zsp
		'call list.push
		zsp = cva_arg(args, zstring ptr)
	loop
    cva_end(args)
end sub

pushstrings("first s", "ab1", "aab2")
However, I see the output:

Code: Select all

first s
ab1
aab2
ab1
Here, the first vararg ("ab1") has been repeated and 3 cva_arg values are output instead of 2.
What am I missing?

Thanks.
GWBc
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Variable argument count to functions

Post by fxm »

Code: Select all

sub pushstrings Cdecl (s as string, ...)
    dim args as cva_list
    cva_start(args, s)
	dim zsp as zstring ptr = 0
	print s
	'call list.push
	zsp = cva_arg(args, zstring ptr)
	do while zsp <> 0
		print *zsp
		'call list.push
		zsp = cva_arg(args, zstring ptr)
	loop
    cva_end(args)
end sub

pushstrings("first s", "ab1", "aab2", 0)
GWBASICcoder
Posts: 21
Joined: Jul 29, 2022 12:43

Re: Variable argument count to functions

Post by GWBASICcoder »

Thanks.
Is there no way around the last 0 in the call?
It's not elegant :)
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Variable argument count to functions

Post by fxm »

Code: Select all

sub pushstrings Cdecl (s as string, ...)
    dim args as cva_list
    cva_start(args, s)
	dim zsp as zstring ptr = 0
	print s
	'call list.push
	zsp = cva_arg(args, zstring ptr)
	do while *zsp <> ""
		print *zsp
		'call list.push
		zsp = cva_arg(args, zstring ptr)
	loop
    cva_end(args)
end sub

pushstrings("first s", "ab1", "aab2", "")
GWBASICcoder
Posts: 21
Joined: Jul 29, 2022 12:43

Re: Variable argument count to functions

Post by GWBASICcoder »

My list needs to support null "" string as an element!
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Variable argument count to functions

Post by dodicat »

Just use another string to finish.
fxm code +

Code: Select all

sub pushstrings Cdecl (s as string, ...)
    dim args as cva_list
    cva_start(args, s)
	dim zsp as zstring ptr = 0
	print s
	'call list.push
	zsp = cva_arg(args, zstring ptr)
	do while *zsp <> "end of story"
		print *zsp
		'call list.push
		zsp = cva_arg(args, zstring ptr)
	loop
    cva_end(args)
end sub

pushstrings("first s", "ab1", "aab2","","Hello","","Goodbye", "end of story") 
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Variable argument count to functions

Post by fxm »

Any garbage might be on the stack.
Put the number of terms (see below) or a terminal term in the caller to know when to stop.

Code: Select all

sub pushstrings Cdecl (n as integer, ...)
    dim args as cva_list
    cva_start(args, n)
    dim zsp as zstring ptr = 0
    for i as integer = 1 to n
        'call list.push
        zsp = cva_arg(args, zstring ptr)
        print *zsp
    next i
    cva_end(args)
end sub

pushstrings(3, "first s", "ab1", "aab2")
GWBASICcoder
Posts: 21
Joined: Jul 29, 2022 12:43

Re: Variable argument count to functions

Post by GWBASICcoder »

Yeah, right.

Can't the compiler push the argument count first on the stack before the arguments?
It probably needs two passes after seeing the ellipsis... Or it can push an extra null pointer as
the last argument.

Thanks for the answers.

GWBc
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Variable argument count to functions

Post by fxm »

GWBASICcoder wrote: Aug 26, 2022 12:38 Can't the compiler push the argument count first on the stack before the arguments?
It probably needs two passes after seeing the ellipsis... Or it can push an extra null pointer as
the last argument.
A variadic macro can do either one or the other:

Code: Select all

#macro pushstrings (parameters...)
    _pushstrings(parameters, 0)
#endmacro
private sub _pushstrings Cdecl (s as string, ...)
    dim args as cva_list
    cva_start(args, s)
	dim zsp as zstring ptr = 0
	print s
	'call list.push
	zsp = cva_arg(args, zstring ptr)
	do while zsp <> 0
		print *zsp
		'call list.push
		zsp = cva_arg(args, zstring ptr)
	loop
    cva_end(args)
end sub

pushstrings("first s", "ab1", "aab2")

Code: Select all

#macro pushstrings (parameters...)
    _pushstrings(__fb_arg_count__(parameters), parameters)
#endmacro
private sub _pushstrings Cdecl (n as integer, ...)
    dim args as cva_list
    cva_start(args, n)
    dim zsp as zstring ptr = 0
    for i as integer = 1 to n
        'call list.push
        zsp = cva_arg(args, zstring ptr)
        print *zsp
    next i
    cva_end(args)
end sub

pushstrings("first s", "ab1", "aab2")

GWBASICcoder wrote: Aug 26, 2022 9:05 I could use the __FB_ARG_COUNT__ intrinsic macro but don't know how to use
it in a sub/function.
Can only be used in a macro body like above.
GWBASICcoder
Posts: 21
Joined: Jul 29, 2022 12:43

Re: Variable argument count to functions

Post by GWBASICcoder »

This works very nicely if I directly call the macro. Thanks.

However, my requirement is to call a public method of the list UDT.

This doesn't compile (error 14: Expected identifier, found '.')

Code: Select all

#macro pushstrings(parameters...)
    this._pushstrings(parameters, 0)
#endmacro

sub mlist.public_pushstrings (s as string, ...)
	pushstrings(s, ...)
end sub
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Variable argument count to functions

Post by fxm »

A variadic macro can call a variadic procedure on variable parameter list but not the other way around.
Similarly, a variadic procedure cannot call another variadic procedure on variable parameter list.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Variable argument count to functions

Post by fxm »

If the variable arguments are all of same type or compatible with the same type, a sized array (initialized with ellipsis) can be passed instead, but this requires slightly more work at the caller level:

Code: Select all

type mlist
    dim as integer __  '' only for UDT not empty
    declare sub pushstrings (s() as string)
end type

sub mlist.pushstrings (s() as string)
    for i As integer = 0 to ubound(s)
        print s(i)
    next i
    print
end sub

dim as mlist ml1

scope
    dim as string list(...) = {"first s", "ab1", "aab2"}
    ml1.pushstrings(list())
end scope

dim as mlist ml2

scope
    dim as string list(...) = {"2nd s", "2nd ab1", "2nd aab2"}
    ml2.pushstrings(list())
end scope

sleep
The array name 'list()' can only be defined/initialized once in the same scope.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Variable argument count to functions

Post by dodicat »

I am not sure the format of your list.
But to push an array of type list, you could do something like:

Code: Select all

Type list
    As String  s
    As Long    n
    Declare Constructor
    Declare Constructor(As String,As Long)
    Declare Sub show(() As list)
    Declare Sub push Cdecl(() As list,count As long, ...)
End Type

Constructor list
End Constructor

Constructor list(sval As String,nval As Long)
s=sval
n=nval
End Constructor

Sub list.push Cdecl(a() As list,count As long, ... ) 
    Dim args As Cva_List 
    Cva_Start( args, count ) 
    Var start=Iif(Ubound(a)=-1,0,Ubound(a))
    Redim Preserve a(1 To start+count)
    For i As Long = 1 To count 
        Var z=Cva_Arg(args,list Ptr)
        a(i+start)=list(z->s,z->n)
    Next
    Cva_End( args ) 
End Sub

Sub list.show(L() As list)
    For n As Long=Lbound(L) To Ubound(L)
        Print n,L(n).s,L(n).n
    Next
End Sub


Redim As list u()
Type<list>.push(u(),3,@list("abc",3),@list("def",5),@list("ghi",12))
Print "First run"
Type<list>.show(u())

Type<list>.push(u(),4,@list("ABC",7),@list("DEF",50),@list("GHI",120),@list("xyz",8))
Print "second run"
Type<list>.show(u())

Type<list>.push(u(),1,@list("Press any key to end",0))
Print "last run"
Type<list>.show(u())

Sleep 
Tested 32/64/-gen gas64
Post Reply