Welcome, Guest
Username: Password: Remember me
This public forum is meant for questions and discussions about Visual FoxPro
  • Page:
  • 1
  • 2

TOPIC:

Passing Fox arrays by ref or value 22 Aug 2021 14:03 #19420

  • Karl-Heinz
  • Karl-Heinz's Avatar
  • Topic Author


  • Posts: 765
  • Guys,

    i´m currently looking at the options how to pass arrays to a function.

    clear
    
    set talk off
    set udfParms to value
    
    dime a[2]
    
    =Test ( a  , 4 ) 
    ? "Alen() after first Test() call" , alen ( a ) 
    ?
    =Test ( @a , 6 )
    ? "Alen() after second Test() call" , alen ( a ) 
    ?
    set udfParms to reference
    =Test ( a , 8 )
    ? "Alen() after third Test() call" , alen ( a ) 
    ?
    ? "APrinters() test"
    set udfParms to value
    dime apr[1]
    ? APrinters ( apr)
    && lists all installed printers
    disp memo like apr
    
    function Test
    parameters arr , n
    
    ? "Alen() inside Test() before redim" , alen ( arr )
    
    dime arr [ n ]
    
    return

    when you run the code the first Test() call shows the msgbox "'Arr' is no array". Simply press the [ok] button to continue. I also noticed that there are functions like APrinters(a) that change the passed array no matter if the array is passed by ref or if Set UdfParms is set to value.

    1. are there any other options to pass arrays ?
    2. I wonder how the various array scenarios described will be implemented in X# ?

    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 23 Aug 2021 15:07 #19421

    • robert
    • robert's Avatar


  • Posts: 2974
  • Karl-Heinz,
    (Fox) arrays are a reference type. The variable (either a local or a memory variable) contains a pointer to memory on the heap where the actual array is stored.
    If you pass an array variable to a function then the normal behavior is that you pass a copy of the reference to the function. This copy of the reference points to the same memory location. The same is true for objects.
    Resizing the array with a DIM command will therefore resize the original array.
    Allocating a new array and assigning it to the copy of the reference would update the pointer in the copy of the reference and the original variable would still point to the old copy.

    If you would pass an array variable by reference then you would be passing a reference to the location where the reference to the array in memory is located. Resizing the array would do the same as when passing the variable by reference.
    Allocating a new array would create a new pointer and this pointer would be stored in the original variable, so the 'old array' would no longer be referenced by this variable.

    The behavior of functions like APrinters() in FoxPro and their counterpart in X# would of course depend on how these functions are implemented.
    If you look at the FoxPro help file it says that the first parameter of Aprinters() is an array name. The example looks like this:
    IF APRINTERS(gaPrinters) > 0  
       CLEAR  && clear the current output window
       DISPLAY MEMORY LIKE gaPrinters && show the contents of the array
    ELSE  
       WAIT WINDOW 'No printers found.'
    ENDIF

    To me this looks like the first parameter is not "a name" (because that would mean that gaPrinters would be passed as a string) but a variable.
    This variable is passed without @ sign, so it is passed by value.
    If gaPrinters is already an array then it will be resized to match the number of printers found.

    The potential problem lies in this line inside the FoxPro help file:
    If the array you include does not exist, Visual FoxPro automatically creates the array

    If you are compiling with the option to allow "undeclared variables" then it would indeed be possible to pass gaPrinters without declaring it first. But in that case the only thing the APrinters() function could do is allocate a memory variable with the name "gaPrinters" and assign the array to that.
    But to do so, the function needs to know the name of the variable(), and normally a compiler would not pass the name of the variable to the function that is called.

    The solution that I see for this is now something like this:
    1) Declare a special attribute in the X# runtime that will be used to mark functions that need to be told about the parameter names passed to the. For the sake of this message we could call this the PassParameterNamesAttribute
    2) Mark the function (in this case APrinters()) with this attribute. That would look something like this:
    [PassParameterNames];
    FUNCTION APrinters(aTarget as __FoxArray, nValue := 0 AS LONG, __xsVariableNames__ as STRING[])
    RETURN ...

    3) Tell the compiler that when a method / function has this attribute then it needs to add an extra argument to the method call that contains the names (if any) of the parameters passed. When an argument is not a variable but an expression (like the literal 0 or 1 for the second parameter of APrinters) then the variable name passed should be an empty string.

    A call to Aprinters() like in the example code should then be translated to this by the compiler
    IF APrinters(gaPrinters, 0, <STRING>{"gaPrinters",""}) > 0

    Please note that I have not discussed this with the rest of the team, but something like this would work.
    Maybe we could change the attribute to contain the name of the parameter that needs his name passed, so the code could look like this, since there is usually only one parameter that needs to pass its name.
    [PassParameterName("aTarget")];
    FUNCTION APrinters(aTarget as __FoxArray, nValue := 0 AS LONG, __xsVariableName__ as STRING)
    RETURN ...

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 23 Aug 2021 15:18 #19422

    • robert
    • robert's Avatar


  • Posts: 2974
  • Karl Heinz,
    One more remark
    set udfParms to value
    ..
    set udfParms to reference

    We cannot support that like this. In a compiled language the way that parameters are passed is decided at compile time. We cannot let that be dependent at a runtime setting, except when we would generate all UDF calls twice inside an IIF:
    lResult := IIF(UdfParamsByReference, MyUdf(@var1, @var2), MyUdf(var1, var2))
    I really do not want to go there. That would more than double the size of the generated code for each UDF call.

    What we could do is add a compiler option (and a matching compiler pragma) to control this, but I am not looking forward to that either.

    Also I think it is very dangerous to control this at runtime.
    If I write a function and I think I am passing a variable by value, I do not want that to change if someone else uses my function and changes the setting to pass variables by reference. I think that is "asking for trouble" and does not belong to a professional development language.
    I am sure FoxPro 3rd party authors have encountered this problem and have then searched for (and found) a solution to safeguard their code against this, for example by passing the variable as an expression (by putting it between parentheses).

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Last edit: by robert.

    Passing Fox arrays by ref or value 23 Aug 2021 18:24 #19423

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Robert,
    [PassParameterName("aTarget")];
    FUNCTION APrinters(aTarget as __FoxArray, nValue := 0 AS LONG, __xsVariableName__ as STRING)
    RETURN ...

    yes, so __xsVariableName__ holds the "real" varname of aTarget. But wouldn´t it be better to not type the function ? - see my VFP code below.

    i tried several possibilities to feed the APrinters() function. As you can see i declare the privates 'var1' and 'var2'. The assignment Var2 := "text" causes later on the expected Fox error: "'Var2' is no array", while 'var1' becomes an array.

    To my surprise it´s even possible to pass undeclared vars to VFP functions !

    APrinters ( undeclaredvar ) creates the array 'undeclaredvar' and VarType ( undeclaredvar ) returns 'U'

    dime arr[1]
    private var1, var2
    
    var2 = "text"
    
    
    ? "Vartypes" , VarType ( var1 ) , VarType ( var2 ) , Vartype ( undeclaredvar ) && U C U
    
    
    ? "APrinters() I test"
    ? APrinters ( arr)  && uses the existing array 'arr'
    
    ? "APrinters() II test"  
    ? APrinters ( var1 )  && creates the array 'var1'
    
    ? "APrinters() III test"
    ? APrinters ( var2 )  && throws the runtime error "'var2' is no array" 
    
    ? "APrinters() IV test"
    ? APrinters ( undeclaredvar ) && creates the array 'undeclaredvar'
    
    
    disp memo like arr
    
    disp memo like var1 
    
    disp memo like undeclaredvar 

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 26 Aug 2021 13:51 #19457

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Guys,

    the attached 2.8c XSharpFoxProAPrinters.viaef inludes a limited X# Version of VFP's APrinters() function.
    Of course the parameter handling of a final X# APrinters() func must be completely different from the one i m currently using, but i just wanted to fill the array and compare the content with the array that VFP fills. Here, the X# and VFP array show the same content.

    FUNCTION Start( ) AS VOID
    LOCAL n AS DWORD 	
    DIMENSION a[1]
    // LOCAL ARRAY a [1]                 
    
    
    	// if there are printers the array will have five columns	
    	IF ( n := APrinters ( @a , 1)) > 0
    		? NTrim ( n ) + " Printers detected"
    		?   
    		ShowArray(a)
    	ELSE 
    		? "no printers detected"
    	ENDIF		
        
    	?
    	?
    	
    	// if there are printers the array will have two columns
    	IF ( n := APrinters ( @a ) )   > 0  // same as APrinters ( @a , 0 )
    		? NTrim ( n ) + " Printers detected"
    		? 
    		ShowArray(a)
    	ELSE 
    		? "no printers detected"
    	ENDIF		
    
    	RETURN


    And this is the VFP code that does the same:
    private n
    DIMENSION a[1]
    && LOCAL ARRAY a [1]                 
    
    
    	n = APrinters ( a , 1)  
    	if n > 0
    		&& the array will have five columns	
    		? Str ( n ) + " Printers detected"
    		?   
    		Disp memo like a 
    	ELSE 
    		? "no printers detected"
    	ENDIF		
        
    	?
    	?
    	
    
    	n = APrinters ( a )    && same as APrinters ( a , 0 )
    	if n > 0
    		&& the array will have two columns
    		? Str ( n ) + " Printers detected"
    		? 
    		Disp memo like a
    	ELSE 
    		? "no printers detected"
    	ENDIF

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 26 Aug 2021 14:04 #19458

    • robert
    • robert's Avatar


  • Posts: 2974
  • Karl Heinz,
    Thanks for the code.

    We have discussed the issue of the arrays being passed to functions such as AFields() and APrinters().
    Where FoxPro creates a new array when the variable does not exist, we have decided not to support that.
    We have decided that the parameter MUST exist in X#.
    Like I wrote before: in theory we could find a way to allow functions like APrinters() to create the array in the context of the calling function.
    But we really see that as a bridge too far. We think it is really not too much to ask the user to create the variable before calling the function.

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 26 Aug 2021 14:29 #19459

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Hi Robert,

    >> We have decided that the parameter MUST exist in X#.

    i agree. Does that mean that the user must pass an existing array to e.g. APrinters() by ref ?

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 26 Aug 2021 14:36 #19460

    • robert
    • robert's Avatar


  • Posts: 2974
  • Karl-Heinz,

    Karl-Heinz wrote: i agree. Does that mean that the user must pass an existing array to e.g. APrinters() by ref ?


    No, there is no need to pass the existing array by ref.
    Passing the array by value will work. We will resize the array inside APrinters() and the other functions that take an array.
    Passing something else will not work because that would require a parameter by reference.

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 24 Oct 2021 18:49 #20102

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Robert,

    if i want to change the array size in a function, without the need to pass the array by ref, the __FoxRedim() function seems to the way to go. Do you agree ? i changed in the APrinters() function the two DIMENSION to __FoxRedim(), and the caller code to APrinters ( a , 1) and APrinters ( a ). - now APrinters() works the same way as it does with VFP.

    btw. i noticed that the __FoxRedim() already exists in 2.8.3.15

    regards
    Karl-Hinz
    FUNCTION Start() AS VOID
    DIMENSION a[1]
    
    a[1] = "xyz"
        
    ShowArray ( a ) 
    ?
    ChangeArraySizeWithOutByref ( a , 6 , 3 ) // 6 rows , 3 cols
    	
    ShowArray ( a )
    ?
    ChangeArraySizeWithOutByref ( a , 8 , 0 )  // 8 rows , no cols
     
    ShowArray ( a )
    
    RETURN
    
    
    FUNCTION ChangeArraySizeWithOutByref ( a , nRows , nCols ) AS DWORD CLIPPER 
    	
    	a := __FoxRedim( a , nRows, nCols )  
    	
    	RETURN 0	
    	

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 25 Oct 2021 08:11 #20105

    • robert
    • robert's Avatar


  • Posts: 2974
  • Karl-Heinz,

    Arrays are a reference type. When you pass them to another function you can do anything that you want with that array in the function. So changing the size is also not a problem.
    You can pass an array by reference but considering the syntax for FoxPro arrays (in FoxPro) this does not make sense, since you cannot assign a new array with the FoxPro syntax. DIMENSION does not create a new array but resizes the array that already exists (keeping its contents).

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 25 Oct 2021 20:41 #20121

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Robert,

    how should a X# APrinters() signature look like ? From the VFP help:
    APRINTERS(ArrayName [, nValue])
    Which X# signature do you prefer ?
    FUNCTION APrinters ( ArrayName , nValue ) AS DWORD CLIPPER
    or
    FUNCTION APrinters ( ArrayName AS __FoxArray , nValue := 0 AS DWORD ) AS DWORD

    Should INTs be used instead of DWORDs ?

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 25 Oct 2021 23:47 #20127

    • robert
    • robert's Avatar


  • Posts: 2974
  • Karl-Heinz ,
    The signature is really not that important I think. This will most likely be used by people that have many, many untyped variables, so whatever you do, there will always be a runtime check needed.

    If you declare the parameter as __FoxArray and a USUAL variable is used then there will be an automatic runtime error when the usual is of the incorrect type. When you do not declare it then you can have more control over the error message.

    W.r.t. INT or DWORD: I personally prefer INT over DWORD, because signed ints are less likely to cause numeric overflows when you accidentally subtract 1 from a value of 0. This may happen in downto loops.
    For unsigned ints you always have to be careful and have to consider if code is being compiled with overflow checking enabled or disabled.

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 29 Oct 2021 13:26 #20173

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Hi Robert,

    attached is a modified XSharpFoxProAPrinters.viaef with a revised APrinters() function. This is how the params are checked
    FUNCTION APrinters ( ArrayName , nValue) AS INT CLIPPER
    LOCAL cPrinterName AS STRING 	
    LOCAL iCount AS INT 
    LOCAL oSearcher AS ManagementObjectSearcher
    
    	Default ( @nValue , 0 ) 
    
    	IF ( iCount := PrinterSettings:InstalledPrinters.Count ) > 0 
             
    		IF ! IsArray( ArrayName ) .or. ( IsArray ( ArrayName ) .and. ! ArrayName IS __FoxArray ) 
    			THROW ArgumentException {"parameter must be a Fox Array", nameof ( ArrayName ) }
    		ELSEIF ! IsNumeric ( nValue ) 
    			THROW ArgumentException {"wrong parameter type, must be numeric", nameof ( nValue ) } 
    		ELSEIF nValue < 0 .or. nValue > 1 
    			THROW ArgumentException {"parameter value must be either 0 or 1", nameof ( nValue ) } 
    		ENDIF

    i also added a GetPrinter() function. The VFP GetPrinter() dialog doesn´t show e.g. "print rage" and "copies" groupboxes ás the .net PrinerDialog does. But OOB the .net Printerdialog can´t be customized to get a more VFP compatible appearance. i think: "better that than nothing" ;-)

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 29 Oct 2021 18:05 #20176

    • robert
    • robert's Avatar


  • Posts: 2974
  • Karl Heinz,
    Thanks. I will have a look at this after the weekend.

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 30 Oct 2021 17:48 #20186

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Robert and Chris,

    IMO, trying to use the .net PrinterDialog to mimic the dialog that VFP opens when the GetPrinter() is called doesn´t make sence. Attached is a core winform viaef that might be a replacement. Of course the text you see is not yet regionalzied, but as a quick view i attached two images. Fox.png shows the VFP dialog, and mine.png my dialog :-). For the moment the "Status" description shows numbers only. It would be interesting to know which other values than 3 do you see when you run the app.

    btw. Chris, it´s a real pleasure to build winforms with XIDE !

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Last edit: by Karl-Heinz.

    Passing Fox arrays by ref or value 31 Oct 2021 13:05 #20187

    • Chris
    • Chris's Avatar


  • Posts: 3404
  • Glad you like it Karl-Heinz! Thanks for the code as well, I'm just not qualified to comment on what should be used, I'll just let Fox designers and/or Robert to decide on this.

    .
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 31 Oct 2021 18:10 #20188

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • Guys,

    1. Instead of a number you´ll see now one of the following values. However, I don't know if these descriptions match the descriptions that an english windows displays by default.

    _oList:Add( "Stopped:" )
    _oList:Add( "Unknown" )
    _oList:Add( "Ready" )
    _oList:Add( "Printing" )
    _oList:Add( "Warmup" )
    _oList:Add( "StoppedPrinting" )
    _oList:Add( "Offline" )

    2. If a printer was stopped, you´ll see now the number of waiting documents.

    e.g.
    "Stopped: 0 Documents waiting"
    "Stopped: 2 Documents waiting"

    3. The content of "Where" and "Location" is now handled properly.

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 31 Oct 2021 19:33 #20189

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • robert wrote: Karl Heinz,
    Thanks. I will have a look at this after the weekend.

    Robert


    i noticed that the APrinters() function uses a wrong property. In the APrinters() sources the property name "Description" must be changed to "Comment".

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 01 Nov 2021 15:51 #20201

    • Karl-Heinz
    • Karl-Heinz's Avatar
    • Topic Author


  • Posts: 765
  • a queston to the VFP community.

    Is it already known that there´s a VFP GetPrinter() "easter egg" ?

    1. start VFP
    2. Enter in the command window "? GetPrinter()"
    3. As expected, the focus is set to the combobox. Now press the <Tab> and then the <Space> or <Enter> key. Magic, isn't it ;-). There seems to be a hidden, but not disabled [Properties] button. I tried to open the additional dialog by mouse clicks inside the dialog area, but I did not succeed.
    .
    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    Passing Fox arrays by ref or value 01 Nov 2021 16:48 #20202

    • kevclark64
    • kevclark64's Avatar


  • Posts: 120
  • I didn't know about the phantom button. I assume you can't click it because it's located off-screen.

    The GetPrinter dialog should have a properties button, though, so if you're recreating it I'd say definitely include it.

    Please Log in or Create an account to join the conversation.

    • Page:
    • 1
    • 2