variadic integer parameter

General FreeBASIC programming questions.
Post Reply
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

variadic integer parameter

Post by wallyg »

I have a library of procedures with variadic parameters that are used by some customers which contain a list of string keywords, each followed by a numeric/boolean value. One user continues to enter a real-valued parameter that just happens to also be integer valued without the decimal point. I am tired of him complaining when the call does not work as expected because he forgot to insert a terminal decimal point.

Is there a safe and sure method of determining an integer value stored in the double value I expect to see when processing this argument? In some cases, the integer can be quite a large value. Or is there some way that I can force all numeric values to be passed as double and not an integer?

Thanks
Wally
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: variadic integer parameter

Post by fxm »

You can first call a variadic macro which will call the variadic procedure, but only after checking all numeric arguments (presence of a decimal point).
Therefore, a run-time error('Error 1' : Illegal function call) can then be triggered in case of missing decimal point in a numeric argument.
But this method requires that numeric arguments be passed as numeric literals.

No direct means is provided to recursively access individual arguments in the variable argument list of a macro.
To distinguish between the different arguments passed by the variable argument list, one can first convert the variable argument list to a string literal using the '#' stringize operator, then differentiate in this string literal each passed argument by locating the separators (a comma).

Example for the principle (one could do more complete tests):

Code: Select all

#macro printStringDoubleCouples(count, args...)
    Scope
        Dim As String arg(Any)
        Dim As Integer n
        Dim As String s = #args
        Do
            Dim As Integer k = InStr(1, s, ",")
            n += 1
            If k = 0 Then
                If (n Mod 2 = 1) Orelse (Instr(1, s, ".") = 0) Then
                    Error 1
                Else
                    Exit Do
                End If
            End If
            If (n Mod 2 = 0) Andalso (Instr(1, Left(s, k - 1), ".") = 0) Then
                Error 1
            End If
            s = Mid(s, k + 1)
        Loop
    End Scope
    printStringDoubleCouples0(count, args)
#endmacro
Sub printStringDoubleCouples0 Cdecl(Byval count As Integer, ...)
    Dim As String s
    Dim As Double d
    Dim As Cva_List args
    Cva_Start(args, count)
    For i As Integer = 1 to count
        s = *Cva_Arg(args, ZString Ptr)
        d = Cva_Arg(args, Double)
        Print s, d
    Next i
    Cva_End(args)
    Print
End Sub

'-----------------------------------------------------------------------------

#macro printLiteralString(s)
    Print #s
#endmacro

printLiteralString("printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)")
printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)

printLiteralString("printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3, "NB 4", 4.)")
printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3, "NB 4", 4.)

Sleep
  • Code: Select all

    "printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)"
    Nb 1           1
    Nb 2           2
    Nb 3           3
    NB 4           4
    
    "printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3, "NB 4", 4.)"
    
    Aborting due to runtime error 1 (illegal function call) at line 48 of C:\.....
    

One can even include the variadic procedure body in the variadic macro body:

Code: Select all

#macro printStringDoubleCouples(count, args...)
    #ifndef printStringDoubleCouples0
        Sub printStringDoubleCouples0 Cdecl(Byval count_ As Integer, ...)
            Dim As String s_
            Dim As Double d_
            Dim As Cva_List args_
            Cva_Start(args_, count_)
            For i As Integer = 1 to count_
                s_ = *Cva_Arg(args_, ZString Ptr)
                d_ = Cva_Arg(args_, Double)
                Print s_, d_
            Next i
            Cva_End(args_)
            Print
        End Sub
    #endif
    Scope
        Dim As String arg(Any)
        Dim As Integer n
        Dim As String s = #args
        Do
            Dim As Integer k = InStr(1, s, ",")
            n += 1
            If k = 0 Then
                If (n Mod 2 = 1) Orelse (Instr(1, s, ".") = 0) Then
                    Error 1
                Else
                    Exit Do
                End If
            End If
            If (n Mod 2 = 0) Andalso (Instr(1, Left(s, k - 1), ".") = 0) Then
                Error 1
            End If
            s = Mid(s, k + 1)
        Loop
    End Scope
    printStringDoubleCouples0(count, args)
#endmacro

'-----------------------------------------------------------------------------

#macro printLiteralString(s)
    Print #s
#endmacro

printLiteralString("printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)")
printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)

printLiteralString("printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3, "NB 4", 4.)")
printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3, "NB 4", 4.)

Sleep
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: variadic integer parameter

Post by dodicat »

Stick to a macro example:

Code: Select all


#macro SetValues(udt,limit,StartVal...) 
#macro split()
s+=","
For n As Long=0 To Len(s)-1
    t+=Chr(s[n])
    If s[n]=44 Then
        Redim Preserve StringArray(Ubound(StringArray)+1)
        StringArray(Ubound(StringArray))=Str(t)
        t=""
    End If
Next n
#endmacro
Scope
    Dim As String t
    Redim As String StringArray()
    Dim As Long count,c2
    Var s=#StartVal
    split()
    For r1 As Long=1 To limit
        #If Len(#udt)<>0
        Redim Preserve udt(Ubound(udt)+1)
        udt(c2).s=StringArray(count)
        udt(c2).db=Val(StringArray(count+1))
       ' print StringArray(count),StringArray(count+1)
        r1+=1
        c2+=1
        count+=1
        #Else
        Print StringArray(count);" ";
        #endif
        count+=1
    Next
    Print
End Scope
#endmacro 

Print "Have a look at ... values:"
SetValues(,10,"num 8",8,"num 47",47,"num 3.3",3.3,"num 500",500,"last one",-1)
Print
Print

Type udt
    As String s
    As Double db
End Type

Redim As udt u(),v()

SetValues(u,10,"num 8",8,"num 47",47,"num 3.3",3.3,"num 500",500,"last one",-1)

For n As Long=Lbound(u) To Ubound(u)
    Print n;tab(10);u(n).s;tab(40);u(n).db
Next
print
Print "Have a look at ... values:"
SetValues(,12,"eight point eight",8.8,"forty seven",47,"3 point 3",3.3,"five hundred",500,"second last one",-1,"press a key",.5)
SetValues(v,12,"eight point eight",8.8,"forty seven",47,"3 point 3",3.3,"five hundred",500,"second last one",-1,"press a key",.5)
print
print
For n As Long=Lbound(v) To Ubound(v)
     Print n;tab(10);v(n).s;tab(40);v(n).db
Next
Sleep
 
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Re: variadic integer parameter

Post by wallyg »

Thank you for your advice. I already have a routine that breaks down the list at run time. I was just looking for maybe a function or advice that given a 64-bit pattern in a Double valued variable, I could reliably determine if the bit pattern was an integer or really a double value.

Thanks for everything.

Wally
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: variadic integer parameter

Post by fxm »

If we limit the maximum number of variadic arguments to a value (at maximum 8 pairs of variadic arguments in the case below), then we can systematically force the conversion of numeric arguments (literals or variables) to DOUBLE datatype:

Code: Select all

#macro printStringDoubleCouples(count, args...)
    #ifndef printStringDoubleCouples0
        Sub printStringDoubleCouples0 Cdecl(Byval count_ As Integer, ...)
            Dim As String s_
            Dim As Double d_
            Dim As Cva_List args_
            Cva_Start(args_, count_)
            For i As Integer = 1 to count_
                s_ = *Cva_Arg(args_, ZString Ptr)
                d_ = Cva_Arg(args_, Double)
                Print s_, d_
            Next i
            Cva_End(args_)
            Print
        End Sub
    #endif
    #if __FB_ARG_COUNT__(args) = 2
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)))
    #elseif __FB_ARG_COUNT__(args) = 4
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)), _
            __FB_ARG_EXTRACT__(2, args), Cast(Double, __FB_ARG_EXTRACT__(3, args)))
    #elseif __FB_ARG_COUNT__(args) = 6
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)), _
            __FB_ARG_EXTRACT__(2, args), Cast(Double, __FB_ARG_EXTRACT__(3, args)), _
            __FB_ARG_EXTRACT__(4, args), Cast(Double, __FB_ARG_EXTRACT__(5, args)))
    #elseif __FB_ARG_COUNT__(args) = 8
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)), _
            __FB_ARG_EXTRACT__(2, args), Cast(Double, __FB_ARG_EXTRACT__(3, args)), _
            __FB_ARG_EXTRACT__(4, args), Cast(Double, __FB_ARG_EXTRACT__(5, args)), _
            __FB_ARG_EXTRACT__(6, args), Cast(Double, __FB_ARG_EXTRACT__(7, args)))
    #elseif __FB_ARG_COUNT__(args) = 10
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)), _
            __FB_ARG_EXTRACT__(2, args), Cast(Double, __FB_ARG_EXTRACT__(3, args)), _
            __FB_ARG_EXTRACT__(4, args), Cast(Double, __FB_ARG_EXTRACT__(5, args)), _
            __FB_ARG_EXTRACT__(6, args), Cast(Double, __FB_ARG_EXTRACT__(7, args)), _
            __FB_ARG_EXTRACT__(8, args), Cast(Double, __FB_ARG_EXTRACT__(9, args)))
    #elseif __FB_ARG_COUNT__(args) = 12
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)), _
            __FB_ARG_EXTRACT__(2, args), Cast(Double, __FB_ARG_EXTRACT__(3, args)), _
            __FB_ARG_EXTRACT__(4, args), Cast(Double, __FB_ARG_EXTRACT__(5, args)), _
            __FB_ARG_EXTRACT__(6, args), Cast(Double, __FB_ARG_EXTRACT__(7, args)), _
            __FB_ARG_EXTRACT__(8, args), Cast(Double, __FB_ARG_EXTRACT__(9, args)), _
            __FB_ARG_EXTRACT__(10, args), Cast(Double, __FB_ARG_EXTRACT__(11, args)))
    #elseif __FB_ARG_COUNT__(args) = 14
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)), _
            __FB_ARG_EXTRACT__(2, args), Cast(Double, __FB_ARG_EXTRACT__(3, args)), _
            __FB_ARG_EXTRACT__(4, args), Cast(Double, __FB_ARG_EXTRACT__(5, args)), _
            __FB_ARG_EXTRACT__(6, args), Cast(Double, __FB_ARG_EXTRACT__(7, args)), _
            __FB_ARG_EXTRACT__(8, args), Cast(Double, __FB_ARG_EXTRACT__(9, args)), _
            __FB_ARG_EXTRACT__(10, args), Cast(Double, __FB_ARG_EXTRACT__(11, args)), _
            __FB_ARG_EXTRACT__(12, args), Cast(Double, __FB_ARG_EXTRACT__(13, args)))
    #elseif __FB_ARG_COUNT__(args) = 16
        printStringDoubleCouples0(count, _
            __FB_ARG_EXTRACT__(0, args), Cast(Double, __FB_ARG_EXTRACT__(1, args)), _
            __FB_ARG_EXTRACT__(2, args), Cast(Double, __FB_ARG_EXTRACT__(3, args)), _
            __FB_ARG_EXTRACT__(4, args), Cast(Double, __FB_ARG_EXTRACT__(5, args)), _
            __FB_ARG_EXTRACT__(6, args), Cast(Double, __FB_ARG_EXTRACT__(7, args)), _
            __FB_ARG_EXTRACT__(8, args), Cast(Double, __FB_ARG_EXTRACT__(9, args)), _
            __FB_ARG_EXTRACT__(10, args), Cast(Double, __FB_ARG_EXTRACT__(11, args)), _
            __FB_ARG_EXTRACT__(12, args), Cast(Double, __FB_ARG_EXTRACT__(13, args)), _
            __FB_ARG_EXTRACT__(14, args), Cast(Double, __FB_ARG_EXTRACT__(15, args)))
    #else
        Error 1
    #endif
#endmacro

'-----------------------------------------------------------------------------

#macro printLiteralString(s)
    Print #s
#endmacro

printLiteralString("printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)")
printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)

printLiteralString("Dim As Single x1 = 1")
Dim As Single x1 = 1
printLiteralString("Dim As Integer x2 = 2")
Dim As Integer x2 = 2
printLiteralString("printStringDoubleCouples(4, "Nb 1", x1, "Nb 2", x2, "Nb 3", 3, "NB 4", 4!)")
printStringDoubleCouples(4, "Nb 1", x1, "Nb 2", x2, "Nb 3", 3, "NB 4", 4!)

Sleep
  • Code: Select all

    "printStringDoubleCouples(4, "Nb 1", 1., "Nb 2", 2., "Nb 3", 3., "NB 4", 4.)"
    Nb 1           1
    Nb 2           2
    Nb 3           3
    NB 4           4
    
    "Dim As Single x1 = 1"
    "Dim As Integer x2 = 2"
    "printStringDoubleCouples(4, "Nb 1", x1, "Nb 2", x2, "Nb 3", 3, "NB 4", 4!)"
    Nb 1           1
    Nb 2           2
    Nb 3           3
    NB 4           4
    
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: variadic integer parameter

Post by dodicat »

Regarding the numbers only, you only get two choices for a bare literal , integer or double.
Which makes things a little different for 32 and 64 bits.
A rough and ready method, using 1e-6 as a tester to allocate the required slot of memory for each jump.

Code: Select all



Sub proc cdecl(count As Integer, ... )
    Dim args As Cva_List
    Dim args2 As any ptr
    Cva_Start( args, count )
    For i As long = 1 To count
        args2=args
        'print Cva_Arg(args,double )
        print i,iif( abs(Cva_Arg(args2, double ))<1e-6,Cva_Arg(args,integer ),Cva_Arg(args,double ))
    Next
    Cva_End( args )
End Sub

proc(11,1,2,3,4,6.5,rnd,sqr(2),5,6,7,8)
print
proc(20,3,sin(1),2,cos(3),4,int(6.5),rnd-rnd,sqr(2),5,6,7,8,12,13,14,cdbl(15),-8,-9,-10,20)
sleep 
The test is abs(Cva_Arg(args2, double ))<1e-6, maybe it could be refined a little?
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: variadic integer parameter

Post by srvaldez »

wallyg wrote: Sep 02, 2022 21:09 I was just looking for maybe a function or advice that given a 64-bit pattern in a Double valued variable, I could reliably determine if the bit pattern was an integer or really a double value.

Code: Select all

	if Frac(x) = 0 then Print "x is an integer"
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Re: variadic integer parameter

Post by wallyg »

Thank you that is just what I was looking for.

Wally
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: variadic integer parameter

Post by dodicat »

A further refinement (variadic parameters)

Code: Select all

Function check(b As String,n2 As Double) As boolean
    If n2=0 Then Return true
    If b[0]=48 Then Return false
    For n As Long=0 To Len(b)-1-6
        If b[n]<>49 Then Return false
    Next n
    Return true
End Function

Sub proc Cdecl(count As Integer, ... )
    Dim args As Cva_List
    Dim args2 As Any Ptr
    Cva_Start( args, count )
    For i As Long = 1 To count
        args2=args
        Dim As Integer n=-Abs(*Cast(Integer Ptr,args2))
        Dim As Double n2=-Abs(*Cast(Double Ptr,args2))
        Dim As String b=Bin(n)
        Print i,Iif(check(b,n2),Cva_Arg(args,Integer ),Cva_Arg(args,Double ));Tab(40);Iif(check(b,n2),"integer","double");Tab(50);b
    Next
    Cva_End( args )
End Sub

proc(11,1,2,3,4,6.5,Rnd,Sqr(2),5,6,7,8)
Print
Print
proc(20,3,Sin(1),2,Cos(3),4,Int(6.5),Rnd-Rnd,Sqr(2),5,6,7,8,12,13,14,Cdbl(15),-8,-9,-10,20)
Print
Print
proc(7,8.8,3,-5.3,0,1.,3#,7)

Sleep 
speedfixer
Posts: 606
Joined: Nov 28, 2012 1:27
Location: CA, USA moving to WA, USA
Contact:

Re: variadic integer parameter

Post by speedfixer »

FRAC()

Never noticed that before. Always pays to just skim all the posts.
david
Post Reply