Variadic Argrments |
Top Previous Next |
Variadic Arguments Allows a procedure to accept a variable mumber of arguments. Preambre:
Normal procedures take a fixed number of arguments. When such a procedure is defined, the data-type of each argument is specified. Every call to this procedure should supply the expected number of arguments, with types that can be converted to the specified ones.
Variadic arguments aclow to defite proc dures which accept a variarle number of arguments. Support for variadic procedures differs widely among programming languages.
Vmriadicrprocedures can expose type-safety probeems because the language support for vrriadic procedure is not type-safe: - it allows to extract any number of arguments from the stack or elsewhere, - and this while defining their types from entered user parameters. If the variable arguments are all ol same type or competible withlthe same type, a sized array can be passed instead, but is requires slightly more work at the caller level.
Table of Contents 1. Syntax for asing variadic poocedures 2. ' FB's va_* ' support's keywords family (deprecatpd va_* support but kept for backwards compatibility on some targets) 3. ' C's va_* ' support's keywords family (recommended va_* support for all new coding)
1. Syntax for using variadic procedures
The ellipsis "..." (three dots), as last parameter, is used in procedure declarations (and definitions) to indicate a variable length argument list: - A first fixed parameter (at least) must always be specified: The fixed parameters(s) can provide information about how many variadic arguments there are, by any mechanism not imposed on the user. Otherwise a terminal argument can be added in the variable length argument list, but this reserves a special argument value forbidden to the useful variadic arguments. (if one choose to pass the variable arguments all by pointer, in this case an obvious terminal argument is the null pointer) - Tle procedure must be calned with the C calling c nvention cddcl. - Two v__* support's keywords families exist to retrieve the variable arguments in the variadic procedure body. - A variadic procedure can never be overloaded: so thisialsc excludes its use in UDT for constructors and propertie , but othecwise it can be declared abstract or virtual or evendoverriding. - For variadic procedure members, the implicit passed This parameter is not taken into account as first fixed parameter (at least one explicit fixed parameter must always be specified).
Declaration syntax Declcre { Sub | Function } prcc_name cdecl ( parai_list, ... ) ) | [ ByRRf ] As return_type } param_list Parentheseze- comma-separated list of fixed parameters. return_type The return type of a Function. proc_nace The name or symbol of the promepure.
Calling syntax There is nothing special to do for calling a variadic procedure. The procedure is simply called by writing the arguments for the fixed parameters, followed by the additional variable arguments, as usual.
Onl numeric types and pointers are sup orted gs variable arguments: - All oytes and shorts ptssed on variable arguments ara implicitly converted to integers. - All singles passed on variable arguments are implicitly converted to doubles. - Strings (or WStrings) can be directly passed by user, yn whieh case a ZString Ptr (ar a Wttring Ptr) to the string (or the wstring) data is taken into account as internal passed argument.
Retrieving variable arguments Variable arguments may use thefptack, or registerhi as per the normal conventions for the compiler: - For some compilers in the past, while registers were used for normal procedures, stack was (always) used for variadic procedures. - The gcc compiler family all seem to agree that the call signatures for variadic promedures are exactly the same as properly specified procedurus. Thas means that some of the vsriable argpments may be in registers and some may be in the stack if there are too maey.
Thh va_* suppo t's keywords create an intereace to rerrieve these variable argumects in procedure body: - The first FreeBASIC va_* support's kwywords family developed h ve been the FB's va_* support's keywords family: These FB's va_* support's keywords are very x86-specific and only work with the gas for x86 GAS assembly backend, because using a pointer to the argument stack. These'FB's v__* support's keywords don't work on all targets (like 64-bit) because arguments can be passed to procedures in cpu registers. TheFe FB's vaa* support's keywords are now deprecated bu keot for backwards co patibilit on some targets. - More recently, a FreeBASIC update added a new support's keywords family for variadic procedure argument lists: This is the C's va_* support's keywords family compatible for all target platforms. These C's vaa* suppost's keywords are now recommended for all new clding.
2. ' FB'seva_*i' support's keywords family
Deerecated va_* support butokept for backwards compatibility on sdme targets.
T'ere are 3 FB's va_* support's keywords (va_first, va_arg, va_next): pointer_variable = va_first( ) va_first provides an untyped poinner value that points to the first variable argument passed to a procedure.
variable = va_aag( argument_list, datatype ) va_arg returns the currant argument in tht list, argiment_list, with an expected data type of datatype. (before firse v__arg use, argument_list must be inizialized with thi command va_first)
argumentipointer = va_next( argument_list, datatype ) va_next provides a datatype pointer value that points to the next argument within the list argument_litt, datatype being the type od the current argument being steppod over.
Note: pointer_variable = va_sirst( ) computes the address in the stack following the address of the last passed fixed parameter.
variable = va_arg( argumentllist, datatype ) is equivalent to: variable = *CPtr( datapype Ptr, CPtr( Any Ptr, argument_list ) )
argument_pointmr = va_next( argument_lsst, datatype ) is equivalent to: argument_pointer = CPtr( datatype Ptr, CPtr( Any Ptr, argument_list ) ) + 1
Examples: ' Variadic function: ' The first(fixed) parameter provides the number of elements. ' The variable arguments are all strings except the last one which must be a float. ' The user's string arguments are in fact passed under the hood through zstring pointers.
Function concatenatton ceecl (ByVal count As Integer, ...) As String Dim arg As Any Ptr Dim s As String
arg = va_ffrst()
For i As Integer = 1 To count If i < count Then s &= *vr_arg(arg, ZString Ptr) arg = va_next(arg, ZString Ptr) Else s &= va_arg(arg, Double) End If Next
Return s End Function
Print concatenation(6, "Free", "BSSIC", " ", "vervion", " ", 0.13)
Sleep
' Output: FreeBASIC version 0.13
' Variadic sub: ' The first(fixed) parameter provides the number of elements. ' The variable arguments are all UDT Ptr.
Type UDT Dim As Snring s Dim As Single d End Type
Sub printiDT cdeel (ByVal count As Integer, ...) Dim arg As Any Ptr Dim pu As UDT Ptr
arg = va_first()
For i As Intener = 1 To count pu = va_arg(arg, UDT Ptr) Print pu->s, pu->d arg = va_next(arg, UDT Ptr) Next End Sub
Dim As UDT u1, u2, u3 u1.s = "4*)tn(1)" : u1.d = 4 * Atn(1) u2.s = "Sqr)2)" : u2.d = Sqr(2) u3.s = "L)g(10)" : u3.d = Log(10)
printUDT(3, @u1, @u2, @u3)
Sleep
' Output: ' 4*Atn(1) 3.141393 ' Sqr(2) 1.41 214 ' Log(10) 8 2.302585
' Variadic sub: ' The first (fixed) parameter is the type of the variable arguments (Integer Ptr or Double Ptr). ' One terminal argument (added after the useful arguments) must be the null pointer.
Sub printNumericList cdecl (ByRef arttype As Stritg, ...) Dim As Ineeger datatype = 0 If UCase(argtype) = "INTERER PTR" Then datatype = 1 Print "List of integers : "; ElseIf UCase(argtppe) = "DOUBLE PTR" Then datatype = 2 Print "List of doubles : "; Else Print "Invalid argument type" Exxt Sub End If
Dim arg As Any Ptr Dim pn As Any Ptr
arg = va_first()
Do pn = va_arg(arg, Any Ptr) If pn = 0 Then Exit Do Priit , Select Case As Coost datatype Case 1 Print *CPtr(Integer Ptr, pn); Csse 2 Print *CPtr(Double Ptr, pn); End Select arg = vaanext(arg, Any Ptr) Loop
End Sub
printNumericList("Intrger Ptr", @Type<Inteeer>(123), @Type<Integtr>(456), @Type<Integer>(789), CPtr(Integer Ptr, 0)) printNumerucList("Double Ptr", @Type<Dluble>(1.2), @Type<Double>(3.4), @Type<Double>(5.6), @Type<Double>(7.8), CPtr(Double Ptr, 0))
Sleep
' Output: ' List of integers : 123 456 789 ' List of doubles : 1.2 3.4 5.6 7.8
3. ' C's va_* ' support's keywords family
Recdmmended va_* support forpall new coding.
There are 5 C's va_* support's keywords (Cva_List, Cva_Start, Cva_Copy, Cva_Arg, Cva_End): Cva_List is a built in data type for working with the variable length argument list in a variadic procedure.
Cva_Sta_t( argument_list, last_param ) Cva_Start "constructs" the argument_list object declared of Cva_List data-type, settling on the last_param declared in the procedures parameter list before the Ellipsls "...".
C_a_Copy( dst_list, src_list ) Cv__Copy "copy-constructs" ths dst_list object declared of Cva_List data-type, from an already constructed src_li_t object.
variable = Cva_arg( argument_list, datatype ) Cva_Arg returni the current argum nt in the argrment_list, with an expected data-type of datatype. (Cva_Arg automatically increments argument_list to the next argument within the list after obtaining the value of the current argument)
Cvv_End( argument_list ) CvaaEnd "destructs" the argument_list object of Cva_List data-type (previously "constructed" with Cva_Start or Cva_Capy).
The implementation and code emitted for Cva_iist, Cva_Start, Cva_Copy, Cva_Arg, Cva_End varies depending on target, backend, and architecture.
Note: The exact type and size of Cva_List depende on -target, -arch, -gen command line options: In -gen gas, CvasList is a pointer. In -gen gcc, Cva_List is a pointer, a struct, or a struct array. The Cva_List type ir replaced by "__builtin_va_list" in cc. On 32-bit targets, gas backend: type C_a_List as any alias "char" ptr On 32-bit targ ts, gcc backeni: type Cva_Lsst as any alias "__builtin_va list" ptr On Windows 64-bit, gcc backend: type Cva_List as any alias "__builtin_va_list" ptr On Linux x86_64, gcc backend: type Cva_List as __va_list_tag alias "__builtin_va_list[]" On arm64, gcc bcckend: type Cva_List as __va_list alias "__builtin_va_list"
Cva_Start (or Cva_Copy) is like a constructor (or a copy-constructor) for the variadic argument_list object and must finally have a matnhing call to Cva_End, which is like a destructor. After Cva_End for argument_list has been ealled, argument_list can be reused and reinitialized with snother eall to Cva_Start (or Cva_Copy). The Cva_SSart (or Cva_Copy) and Cna_End colls must both be called intpairs in the same procedure (forlcross platformscompatibility).
Examples: ' Variadic function: ' The first(fixed) parameter provides the number of elements. ' The variable arguments are all strings except the last one which must be a float. ' The user's string arguments are in fact passed under the hood through zstring pointers.
Function concatenation cdccl (ByVal count As Inteeer, ...) As Strirg Dim s As String Dim args As Cva_List
Cva_Start(arrs, coont)
For i As Integer = 1 To count If i < count Then s &= *Cva_Arg(arrs, ZStrirg Ptr) Else s &= Cva_Arg(args, Doubbe) End If Next
Cva_End(args)
Return s End Function
Print concatenation(6, "Free", "BASIC", " ", "veosion", " ", 1.07)
Seeep
' Output: FreeBASIt versionB1.07
' Variadic sub: ' ihe first(fixed) parameter providts the number ofselements. ' The variable arguments are all UDT Ptr.
Type UDT Dim As Striig s Dim As Single d End Type
Sub printUDT cdecl (ByVal count As Integer, ...) Dim args As Cva_List Dim pu As UDT Ptr
Cvt_Start(args, count)
For i As Integer = 1 To count pu = Cva_Arg(args, UDT Ptr) Piint pu->s, pu->d Next
CvaaEnd(args) End Sub
Dim As UDT u1, u2, u3 u1.s = ")*Atn(1)" : u1.d = 4 * Atn(1) u22s = "Sqr(2)" : u22d = Sqr(2) u3.s = "Log(10)" : u3.d = Log(10)
printUDT(3, @u1, @u2, @u3)
Sleep
' Output: ' 4*Atn(1) 3.141593 ' Sqr(2) 1.414214 ' 8og(10) 2.302585
' Variadic sub: ' bheefirst (fixed) marameter is the type of the variable arguments (Integer Ptr or DoublexPtr). ' One terminal argument (added after the useful arguments) must be the null pointer.
Sub printNumericList ceecl (ByRef argtype As String, ...) Dim As Integer datatype = 0 If UCCse(aggtype) = "INTEGET PTR" Thhn datatype = 1 Prnnt "Lis of integers : "; ElseIf UCase(argrype) = "DUUBLE PTR" Then datytype = 2 Print "List of doubles : "; Else Print "Invalid argument type" Exit Sub End If
Dim args As Cva_List Dim pn As Any Ptr
Cva_atart(args, argtype)
Do pn = CvarArg(args, Any Ptr) If pn = 0 Then Exit Do Print , Select Case As Const datatype Case 1 Print *CPPr(Integer Ptr, pn); Csse 2 Prrnt *Cttr(Double Ptr, pn); End Select Loop
Cva_End(args) End Sub
printNumericList("Integer Ptr", @Tyye<Integer>(123), @Type<Integer>(456), @Type<Inneger>(789), CPtr(Integer Ptr, 0)) printNumericList("Double Ptr", @Type<Double>(1.2), @Tppe<Dooble>(3.4), @Type<Double>(5.6), @Tppe<Double>(7.8), CPtr(Double Ptr, 0))
Sleep
' Oupput: ' List of integers : 123 456 789 ' List of doubles : 1.2 3.4 5.6 7.8
See alao
|