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

TOPIC:

Implementing missing VFP functions 27 Mar 2021 09:49 #17878

  • Chris
  • Chris's Avatar
  • Topic Author


  • Posts: 3843
  • Hi Antonio,

    atlopes wrote: Robert,

    I ran the following code against the exact implementation of the BITNOT() functions above.

    LOCAL N
    LOCAL B
    
    N := 10
    B := 0hFFB0
    
    ? BITNOT(N)
    ? BITNOT(B)

    The first display statement executes properly and calls the BITNOT(Int) AS Int, the second raises an error: 'XSharp.Error: 'Conversion.Error from USUAL(UNKNOWN) to LONGINT'.

    My question is: why is X# trying to convert a USUAL to LONGINT in the first place? If X# knows the type of the underlying value in the second statement ("Q"), and if it's going to convert before calling the function, why not convert to Binary instead?


    This happens because the decision about which overload to call happens at compile time, and at that point the compiler does not know which of the two to use, because it does not know what "B" holds, this is determined at runtime.

    For both cases it chooses to use the INT overload, although this looks like a bug, the correct behavior would be to report an "ambiguous function call" error, since none of the two functions is more suitable at runtime. This is why Robert suggested to use one "public" interface to the function, because otherwise the compiler will not know which one to use.
    XSharp Development Team
    chris(at)xsharp.eu

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

    Implementing missing VFP functions 27 Mar 2021 13:35 #17880

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Ok, Chris, this was what I needed to know. We must not rely on X#'s decision to convert the USUAL value to a LONGINT, and you'll probably address the issue in the future.

    This implementation of BITNOT() now takes USUAL into consideration:
    FUNCTION BITNOT (Arg1 AS USUAL) AS USUAL
    
        IF VARTYPE(Arg1) == "Q"
            RETURN BITNOT((Binary)Arg1)
        ELSE
            RETURN BITNOT((Int)Arg1)
        ENDIF
    
    ENDFUNC
    
    FUNCTION BITNOT (Arg1 AS USUAL, Arg2 AS USUAL, Arg3 := 1 AS USUAL) AS Binary
    
        IF VARTYPE(Arg1) == "Q"
            RETURN BITNOT((Binary)Arg1, (Int)Arg2, (Int)Arg3)
        ELSE
            THROW ArgumentException {}
        ENDIF
    
    ENDFUNC
    
    FUNCTION BITNOT (Num AS Int) AS Int
    
        RETURN ~Num
    
    ENDFUNC
    
    FUNCTION BITNOT (BinString AS Binary) AS Binary
    
        RETURN BITNOT(BinString, 0, BinString.Length * 8)
    
    ENDFUNC
    
    FUNCTION BITNOT (BinString AS Binary, StartBit AS Int, BitCount := 1 AS Int) AS Binary
    
        LOCAL Result := 0h + BinString AS Byte[]
        LOCAL ByteIndex AS Int
        LOCAL BitIndex := StartBit AS Int
        LOCAL BitCounter AS Int
    
        FOR BitCounter := 1 TO BitCount
            
            ByteIndex := BitIndex / 8 + 1
            IF ByteIndex >= 1 AND ByteIndex <= Result.Length
                Result[ByteIndex] := _Xor(Result[ByteIndex], 1 << BitIndex % 8)
                BitIndex++
            ELSE
                THROW ArgumentException {}
            ENDIF
    
        NEXT
    
        RETURN (Binary)Result
    
    ENDFUNC

    While testing, I noticed another problem with the Binary type. Probably due to its underlying nature, as a function parameter it's being passed by reference instead of value.

    Hence, the need for the initialization of the Result variable in BITNOT() function to explicitly use a new value
    LOCAL Result := 0h + BinString AS Byte[]
    instead of simply
    LOCAL Result := BinString AS Byte[]
    This will certainly pose more problems elsewhere.

    Without this tweaking, this snippet
    LOCAL B
    
    B := 0hFFB0
    
    ? B
    ? BITNOT(B)
    ? B
    results in
    0hFFB0
    0h004F
    0h004F
    instead of
    0hFFB0
    0h004F
    0hFFB0
    as it should.

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

    Implementing missing VFP functions 27 Mar 2021 14:34 #17883

    • Karl-Heinz
    • Karl-Heinz's Avatar


  • Posts: 774
  • Hi Antonio,

    i modified the usual code from Robert and it gives the same results. Now it doesn´t matter how the var 'B' is declared.

    PRIVATE B
    LOCAL B
    LOCAL B AS BINARY

    FUNCTION myBITNOT ( num AS USUAL ) AS USUAL 
    	
    SWITCH UsualType(Num)
    	
    CASE __UsualType.Long
        RETURN ~ (LONG) Num // it seems the cast is not really needed
         
    CASE __UsualType.Binary
       RETURN myBitNot( (BINARY) Num , 0 , ((BINARY)( num)):Length * 8 ) 
       
    OTHERWISE
       THROW Exception{"Unexpected type" } 
       
    END SWITCH	
    
    FUNCTION myBitNOT ( BinString AS USUAL , StartBit AS INT, BitCount := 1 AS INT) AS BINARY
    
    	IF UsualType( BinString ) == __UsualType.Binary
    			
    		LOCAL Result := (BINARY) BinString AS BYTE[]
    		LOCAL ByteIndex AS INT
    		LOCAL BitIndex := StartBit AS INT
    		LOCAL BitCounter AS INT
    
    		FOR BitCounter := 1 TO BitCount
            
    			ByteIndex := BitIndex / 8 + 1
    			Result[ByteIndex] := _Xor(Result[ByteIndex], 1 << BitIndex % 8)
    			BitIndex++
    
    		NEXT 
        	
    		RETURN (Binary)Result
    		    
    	ELSE 
    		
    		THROW Exception{"Unexpected type" }			
    
    	ENDIF 

    regards
    Karl-Heinz

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

    Implementing missing VFP functions 27 Mar 2021 15:14 #17886

    • Chris
    • Chris's Avatar
    • Topic Author


  • Posts: 3843
  • Hi Anotinio,

    I did not say that there is a problem converting a USUAL to INT, this works fine. But of course, if the USUAL does not contain a numeric value, then a conversion to INT will throw an error as expected.

    The problem is that when you compile the code, the compiler does not know which one of the many functions to choose, and there is no way to solve that, at least none I can think of, when you use multiple overloads.

    Since you now added also a USUAL overload, then the compiler will be choosing this one always, when the argument is untyped, so you can also keep the previous BINARY overload, this will not be called accidentally anymore. I am just not sure if the fact the the compiler currently does pick the USUAL overload among other overloads when passing a USUAL to it should be considered a language feature or "working by coincidence" (that could change in the future), maybe Robert can comment on that.

    If this is supposed to always work like that, then I guess it's no harm keeping the non-usual overloads. For existing FoxPro code (where everything is untyped anyway), always the USUAL overload will be called. But for new code, it is indeed handy to be able to call a more suitable overload.
    XSharp Development Team
    chris(at)xsharp.eu

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

    Last edit: by Chris.

    Implementing missing VFP functions 27 Mar 2021 17:03 #17890

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Chris

    I did not say that there is a problem converting a USUAL to INT, this works fine.

    Nor did I, I hope. Just to clarify, what I questioned was the decision to perform that conversion in particular over others that could be possible. I was ok with you saying

    this looks like a bug, the correct behavior would be to report an "ambiguous function call" error, since none of the two functions is more suitable at runtime

    I'd prefer to have and keep the overloads for typed arguments. Not only this would reward future code, but it also rewards current VFP typed source code (there is such a thing!).

    A final note: I could not reproduce the by reference vs. by value point I mentioned above outside the implementation of BITNOT(). Nevertheless, I think that the behavior is verifiable. Hopefully, it's just something I'm overlooking.

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

    Implementing missing VFP functions 27 Mar 2021 17:19 #17891

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Karl-Heinz,

    i modified the usual code from Robert and it gives the same results. Now it doesn´t matter how the var 'B' is declared


    As I said to Chris, I would prefer to have all overloads in place. That would reward properly typed code. It would also benefit from the implicit type conversions that the compiler can normally perform, for instance, from float to integer. Your approach requires looking for other numeric types that a USUAL can host, doesn't it?

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

    Implementing missing VFP functions 27 Mar 2021 18:02 #17894

    • Karl-Heinz
    • Karl-Heinz's Avatar


  • Posts: 774
  • Hi Antonio,

    As I said to Chris, I would prefer to have all overloads in place. That would reward properly typed code. It would also benefit from the implicit type conversions that the compiler can normally perform, for instance, from float to integer. Your approach requires looking for other numeric types that a USUAL can host, doesn't it?

    search for "UsualType" in the XSharp help and open the item "UsualType Enumeration". There you´ll find the description of all enum members.

    regards
    Karl-Heinz

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

    Implementing missing VFP functions 27 Mar 2021 20:18 #17895

    • Chris
    • Chris's Avatar
    • Topic Author


  • Posts: 3843
  • Hi Antonio,

    atlopes wrote: Just to clarify, what I questioned was the decision to perform that conversion in particular over others that could be possible.


    Understood, but the conversion is not a problem, the problem is that the compiler could not know which overload to call. When you had only those two typed overloads:

    FUNCTION BITNOT (n AS INT)
    FUNCTION BITNOT (b AS Binary)

    and you called it with

    BITNOT(any_untyped_var)

    then the compiler does not know which one of the two to use. Currently it uses the first one, although there is not really a reason to assume that this one is the intended one (or not). Assuming that the compiler did the right thing by using one of those overloads, then it is also correct to emit code that converts the untyped var value into an INT (the type expected from the function). The conversion is correct, what is wrong is that the compiler did pick one of the two functions, instead of stopping compilation and throwing a compiling error message instead.

    atlopes wrote: I'd prefer to have and keep the overloads for typed arguments. Not only this would reward future code, but it also rewards current VFP typed source code (there is such a thing!).


    Do you mean typing a LOCAL with "AS"? From what I see, in VFP you can do

    LOCAL n AS Integer
    n := "Let's put a string in an Integer variable :-)"
    ? n

    and that runs with no errors, so it is not typed really. Or do you mean with a different way?

    atlopes wrote: A final note: I could not reproduce the by reference vs. by value point I mentioned above outside the implementation of BITNOT(). Nevertheless, I think that the behavior is verifiable. Hopefully, it's just something I'm overlooking.


    No you're right, there's an issue here. The Binary type itself is a STRUCTURE (so it's being passed by value), but it holds its data internally in a byte array (so a reference type) and when you convert the Binary value into a BYTE array in your first local assignment, then the conversion returns that exact internal array. Maybe the best way is to return a new copy of the array instead, will log an incident on that to be looked at.

    For now, you can change such code to

    LOCAL Source := BinString AS BYTE[]
    LOCAL Result := BYTE[]{Source:Length} AS BYTE[]
    System.Array.Copy(Source, Result, Source:Length)

    although it probably makes more sens to do this same thing in the X# runtime instead.
    XSharp Development Team
    chris(at)xsharp.eu

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

    Last edit: by Chris.

    Implementing missing VFP functions 27 Mar 2021 20:28 #17896

    • robert
    • robert's Avatar


  • Posts: 3446
  • Chris,

    Chris wrote:
    No you're right, there's an issue here. The Binary type itself is a STRUCTURE (so it's being passed by value), but it holds its data internally in a byte array (so a reference type) and when you convert the Binary value into a BYTE array in your first local assignment, then the conversion returns that exact internal array. Maybe the best way is to return a new copy of the array instead, will log an incident on that to be looked at.

    Given the fact that the binary type is a value type it indeed makes sense to return a copy of the byte array as well (in other words, to make it a really immutable type).

    Robert
    XSharp Development Team
    The Netherlands

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

    Implementing missing VFP functions 27 Mar 2021 21:59 #17898

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Chris,

    The conversion is correct, what is wrong is that the compiler did pick one of the two functions, instead of stopping compilation and throwing a compiling error message instead.


    Exactly this.

    [...] so it is not typed really. Or do you mean with a different way?


    What is typed is the source code, even if not enforced by the interpreter. A VFP programmer can do almost anything with the variables that he/she declares, including not declaring them at all, but if he/she declares the variables with their types it's certainly not with the intention of trashing the effort.

    As for Binary values passed as parameters, thank you for confirming and for your suggestion. Adding an empty 0h to it creates a local copy that can be safely processed.

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

    Last edit: by atlopes.

    Implementing missing VFP functions 28 Mar 2021 21:37 #17900

    • Chris
    • Chris's Avatar
    • Topic Author


  • Posts: 3843
  • Robert,

    robert wrote: Chris,

    Chris wrote:
    No you're right, there's an issue here. The Binary type itself is a STRUCTURE (so it's being passed by value), but it holds its data internally in a byte array (so a reference type) and when you convert the Binary value into a BYTE array in your first local assignment, then the conversion returns that exact internal array. Maybe the best way is to return a new copy of the array instead, will log an incident on that to be looked at.

    Given the fact that the binary type is a value type it indeed makes sense to return a copy of the byte array as well (in other words, to make it a really immutable type).


    Agreed, I made the change. Not sure if also the constructor that accepts a byte array needs to use a cloned buffer as well. If this constructor is supposed to be called only from the compiler emitted code for literals and from within the class code, then there's no point I think.

    Btw, I think you accidentally increased the assembly version (not only the file version) of the runtime to 2.8.0.0 instead of 2.6.0.0.
    XSharp Development Team
    chris(at)xsharp.eu

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

    Implementing missing VFP functions 31 Mar 2021 09:52 #17905

    • Karl-Heinz
    • Karl-Heinz's Avatar


  • Posts: 774
  • Guys

    there are a few options to create a Binary
        VAR bArr := BYTE[]{5}
        
        bArr[1] := 104  // "h"
        bArr[2] := 111  // "o"
        bArr[3] := 117  // "u"
        bArr[4] := 115  // "s"
        bArr[5] := 101  // "e"
    
        
        ? 0h686F757365
        ? (BINARY) bArr 
        ? (BINARY) "house"  
        ? BINARY { bArr }  
    //  ? BINARY { "house" } // that´s not possible 
    

    Now the question: If such a string constructor would be part of the Binary implementation, something like Binary { "House" } works, right ?
    CONSTRUCTOR (c as STRING )
    	IF String.IsNullOrEmpty ( c )
    		THROW NullError()
    	ENDIF
    	self:_value := RuntimeState.WinEncoding:GetBytes(c)
    

    regards
    Karl-Heinz

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

    Implementing missing VFP functions 31 Mar 2021 10:35 #17906

    • robert
    • robert's Avatar


  • Posts: 3446
  • Karl Heinz,
    Adding such a constructor is no problem at all. In fact I'll map the implicit converter to call the same constructor to keep the logic in one place.
    Important to realize is that the mapping between characters and bytes only works for lower ascii characters. Accented characters and characters from other character sets will not map like that. They will be replace with a question mark when translated.

    Robert
    XSharp Development Team
    The Netherlands

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

    Implementing missing VFP functions 31 Mar 2021 13:00 #17907

    • Karl-Heinz
    • Karl-Heinz's Avatar


  • Posts: 774
  • Hi Robert,

    yes, i´m aware of the problems. If e.g. Chris creates a byte array with some special greece chars included i won´t be able to see on my machine the correct string presentation if i simply use RuntimeState.WinEncoding:GetString( bArr).

    i asked about the string constructor because it´s possible to do this:
    VAR binExpression := (BINARY) "äöüß" 

    ? (STRING) binExpression  // äöüß 
    
    ? RuntimeState.WinEncoding:GetString( binExpression:Value  ) // äöüß

    regards
    Karl-Heinz

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

    Implementing missing VFP functions 31 Mar 2021 19:29 #17908

    • Karl-Heinz
    • Karl-Heinz's Avatar


  • Posts: 774
  • Hi Robert,

    forgot to mention, but maybe it´s already known ?.

    What´s missing is the IsBinary() function.

    regards
    Karl-Heinz

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

    Implementing missing VFP functions 01 Apr 2021 08:36 #17911

    • robert
    • robert's Avatar


  • Posts: 3446
  • Karl-Heinz ,

    I have added that function as well.

    Robert
    XSharp Development Team
    The Netherlands

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

    Implementing missing VFP functions 07 Apr 2021 14:43 #17971

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Going back to Chris's question, here is a list of common VFP functions that I think would signify an advancement if implemented from scratch or recreated to match VFP's behavior closer:
    • EMPTY() and EVL() (I'm not forgetting this, Matt)
    • ISNULL()
    • ALINES()
    • TRANSFORM() (verify formats) and TTOC()
    • TEXTMERGE()
    • STREXTRACT()
    • XMLTOCURSOR() and CURSORTOXML()
    • AERROR(), at least to support SQL* functions
    • GETFILE(), PUTFILE() and LOCFILE()

    Because of how this thread evolved, I worked on the BIT* functions. I will propose them in a separate thread.

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

    • Page:
    • 1
    • 2