I could make it fail in 64bit with the right values.
Basically. There was a big thread about it and Xojo decided they won’t fix it.
I could make it fail in 64bit with the right values.
Basically. There was a big thread about it and Xojo decided they won’t fix it.
In another thread we stumbled upon a behavior which is confusing many people:
var m1,m2 as MemoryBlock
m1 = "foo"
m2 = "foo"
m1 = m2 // true (? this is surprising)
m1 is m2 // false
The surprising part is that usually in Xojo, when you compare two objects with the = operator, it tells you if the objects are the same object (e.g. one object with two references to it). In this case, MemoryBlocks implement the Operator_Compare operator, so the equals operator (and > and < ) are actually comparing the Contents of the memoryBlocks.
FWIW, you did not prove this in the other thread. Did you actually run the code you posted here?
Yes! tested in Xojo 2025R3, 2019R1.1, and RealStudio 2011. All give same results.
To make sure it’s not some weird aliasing of string constants, I did this test:
It probably has compare operator on it given this result.
Sadly then I do not think Xojo has anyway to compare the reference if there is Compare operator on it ?? ……..perhaps if you cast to object it avoids the compare operator overload and does the reference compare….am not sure, like if Object(m1) = Object(m2) then
The Is operator compares object identity. Is — Xojo documentation
This is why I prefer to use If (Obj Is Nil) = False Then instead of If Obj <> Nil Then. Usually, I’m not going to compare the contents of an object against nil using Operator_Compare.
@Thom_McGrath I used to worry about this myself, but I think that comparisons against Nil are special, and the compiler will never invoke Operator_Convert in that case. I can’t find a way to falsify this claim.
class Class1
Public Function Operator_Convert() As Object
' always return nil
return nil
End Function
var c1 as new Class1
if c1 = nil then
break // this never happens
else if c1 is nil then
break // this never happens
end if
You can get it to fail if you use an intermediary variable that is a different Class
class Class1
Public Function Operator_Convert() As Ptr
' always return nil
return nil
End Function
var c1 as new Class1
var p as Ptr = c1 // this will be nil
But I think in the one-liner case, comparing any object = nil is safe - Operator_Convert() will never be invoked.
Do you have a counter-example?
I’m still waiting for an explanation of why this isn’t a compiler bug. To venture a guess, I bet Operator_Compare is turning both MemoryBlocks into strings and doing that comparison.
Well you’re using Operator_Convert instead of Operator_Compare. Compare is definitely invoked against nil.
The code that triggered this was simply:
Var Instance As New TestClass
If Instance <> Nil Then
Break
Else
Break
End If
Inside Operator_Compare, you must use Is or else you trigger a stack overflow.
If I recall correctly - though I’m not very confident of this - Operator_Convert is only called for assignments, not for comparisons. So that would mean MemoryBlock would have to have an Operator_Compare(Other As MemoryBlock) As Integer function.
You can try it yourself. Create a class with a Operator_Convert() As String function, then try If Instance = "Some String" Then and you’ll get a compile error because no comparison operator has been defined, even though there is a way to compare the two with a conversion.
Interesting point - what should Operator_Compare() return against a nil object?
If you return -1 or +1, then the equals (=) and not equals (<>) operators work fine and return False as expected. But then the less-than (<) or greater-than (>) comparisons would return a bogus True value.
Public Function Operator_Compare(other as Class1) As Integer
// by definition, self is not nil, so if other is nil, we aren't equal
if other is Nil then
return -1
end if
End Function
var c1 as new Class1
var c2 as Class1 = nil
if c1 = c2 then // False (as expected)
if c1 > c2 then // False (makes sense, I suppose)
if c1 < c2 then // True (this is...unexpected)
Perhaps the right answer is to throw an exception:
Public Function Operator_Compare(other as Class1) As Integer
if other is nil then
raise new RuntimeException("Can't compare instance of Class1 to nil")
end if
End Function
In fact, here is how Xojo handles comparing a non-nil MemoryBlock against a Nil one:
var m1,m2 as MemoryBlock
m1 = "foo"
m2 = nil
m1 = m2 : False
m1 < m2 : False
m1 > m2 : True
m1 is m2 : False
I can sort of see the logic here - it follows what you would get with string comparisons, e.g a non-empty string is always greater than and never less than, an empty string.
In my opinion, this is so exceedingly ambiguous that it should be a compiler error. Object references do not have quantitative differences; they do not exist on a scale or within a range.
Of course, this is probably the result of (again, in my opinion) inappropriate Object_Compare shenanigans coercing those poor MemoryBlocks into strings.
No, Operator_Compare will not coerce the memory blocks into strings. And object comparison has its uses. DateTime is a perfect example. What you’re seeing is how Xojo decided to compare two memory blocks. In any sort of object comparison, you need to consider nil. I see nothing here that doesn’t make sense.
Yes, it’s a perfect example because the value of the object exists on a well-defined scale. Not so with MemoryBlocks. They’re arbitrary collections of bytes. Why compare them as strings? Why not compare them as extremely large floating point values? Or by how many of the bytes are 0x4F?
Maybe they aren’t comparing them as strings but byte by byte?
Let’s check the documentation!
Oh wait, it’s boilerplate garble that reveals no insights.
Operator_Compare(value As MemoryBlock) As Integer
Defines the following comparison operators for the MemoryBlock class: =, <, >, <=, >=, <>. It compares the passed MemoryBlock to the Self instance.
Operator_Compare returns an integer whose meaning is as follows: < 0 means that Self is less than the passed parameter, 0 means that Self is equal to the passed parameter, and > 0 means that Self is greater than the passed parameter.
Actually this statement is wrong, since Other is nil, there isn’t an instance to call a recursive Operator_Compare on. So <> ends up being perfectly safe inside Operator_Compare since it falls back to an identity comparison.
Some more Array oddities:
Although an Array behaves like an Object (an instance of a Class), it’s really not:
var x as Array // error
var x() as integer = New Array(1,2,3,4,5) // error
var x() as integer = Array(1,2,3,4,5) // OK
var x() as Double
x.append 1
x.append 2
x.append 3 // this works: you can append an Integer to an array of Double
var z() as Double = Array(1,2,3)
// Error: Type mismatch error. Expected Double(), but got Int64()
var z() as Double = Array(1, 2.0 ,3)
// this works - as long as one of the items is a Double