I would like to redefine it as below, would it cause any problems?
proc `==`*[T](x, y: ref T): bool =
x[] == y[]
Yes, for once if you have a ref object with cycles it can lead to an infinite loop or stack overflow.
The other issue would be that it goes against the default semantics of ref objects in Nim, which a lot of code relies on, assuming you want to replace it in system.nim.
I don't think it's an issue if you concretely define it this way for your own ref type assuming it will not form cycles.
Even so, you have to perform nil-checks.
proc `==`*[T](x, y: ref T): bool =
if unlikely(x.isNil()):
return y.isNil()
elif unlikely(y.isNil()):
return x.isNil()
else:
return x[] == y[]
One more optimisation
proc `==`*[T](x, y: ref T): bool =
if system.`==`(x, y): return true
if unlikely(x.isNil()):
return y.isNil()
elif unlikely(y.isNil()):
return x.isNil()
else:
return x[] == y[]
Thanks, I guess the solution then is something like:
template value_equality[T](_: type[T]) =
proc `==`*(x, y: T): bool =
if system.`==`(x, y):
true
elif unlikely(x.isNil()):
y.isNil()
elif unlikely(y.isNil()):
x.isNil()
else:
x[] == y[]
type Person = ref object
name: string
value_equality Person
echo Person(name: "Jim") == Person(name: "Jim") # => true
Use system.`==` to prevent recursive calls, as in the first branch.
proc `==`*[T](x, y: ref T): bool =
if system.`==`(x, y):
true
elif unlikely(x.isNil()):
y.isNil()
elif unlikely(y.isNil()):
x.isNil()
else:
system.`==`(x[], y[])
And if the first branch is not true, they cannot both be nil.
proc `==`*[T](x, y: ref T): bool =
if system.`==`(x, y):
return true
elif x.isNil() or y.isNil():
return false
else:
return system.`==`(x[], y[])
Thanks, I guess the solution then is something like
I don't encourage that solution either. ;-)
I tried to write a macro to have a pragma for the type definition. When used as a pragma, the untyped parameter is different though and I am not sure how to adapt it.
import std/macros
macro defineEquality(typedef: untyped): untyped =
result = quote do:
`typedef`
for statement in typedef:
if statement.kind == nnkTypeSection:
for i in 0 ..< statement.len:
if statement[i].kind == nnkTypeDef:
var tnode = statement[i]
if tnode[2].kind == nnkRefTy:
# When the type section is given in a macro block
var node = tnode.findChild(it.kind == nnkIdent)
# When the macro is given as a pragma expression
if node == nil and tnode[0].kind == nnkPragmaExpr:
node = tnode[0].findChild(it.kind == nnkIdent)
else:
echo "Parsing Error"
let nameOfType = node
result = result.add(
quote("@") do:
proc `==`*(x, y: `@nameOfType`): bool =
if system.`==`(x, y):
return true
if x.isNil() or y.isNil():
return false
else:
return system.`==`(x[], y[])
)
when isMainModule:
type
MyType = object
value: int
# type MyTypeRef = ref MyType
defineEquality:
type MyTypeRef = ref MyType
# type MyTypeRef {.defineEquality.} = ref MyType
var
first, second: MyTypeRef
a = MyType(value:2)
ptrA = MyTypeRef(value:a.value)
ptrB = MyTypeRef(value:a.value)
doAssert first.isNil() == true
doAssert second.isNil() == true
doAssert ptrA.isNil() == false
doAssert ptrB.isNil() == false
# Two ref objects are equal iff they are both nil or reference to the same object
doAssert (first == second) == true
doAssert (ptrA == second) == false
doAssert (ptrA == ptrB) == true