Hi, Just wanted to share a little compile time function I wrote to statically get an object's hierarchy. It can be used, for example, to determine if two types have a child/parent relationship:
import macros
type
R = object of RootObj
Parent[T] = object of R
Child[T] = object of Parent[T]
proc hier(t: typedesc):seq[NimNode] {.compileTime.} =
var curr : NimNode
template update(u: untyped): untyped =
result.add(u)
curr = u.getTypeImpl
update(t.getTypeInst)
while curr.kind == nnkObjectTy and curr[1].len != 0:
case curr[1][0].kind
# type Child = object of Parent
# ^^^^^^
of nnkSym: update(curr[1][0])
# type Child[T] = object of Parent[T]
# ^^^^^^
of nnkBracketExpr: update(curr[1][0][0])
else:
discard
proc isa(t1:typedesc, t2:typedesc): bool {.compileTime.} =
t1.hier.contains(t2.getTypeInst)
static:
echo hier(Child)
echo isa(Child,RootObj)
# @[Child, Parent, R, RootObj]
# true
Foo = object of RootObj
type Foo2 = object of Foo
type Foo3 = object
static:
doAssert Foo2 is Foo
doAssert Foo isnot Foo2
doAssert Foo3 isnot Foo
isa isn't a very motivation example, how about calculating the common ancestor of two different object hierarchies?
import macros, options
type
R = object of RootObj
Parent[T] = object of R # <-- branch of R
R1 = object of R # <-- another branch of R
Child[T] = object of Parent[T]
proc hier(t: typedesc):seq[NimNode] {.compileTime.} =
...
proc ancestor(t1:typedesc, t2:typedesc): Option[NimNode] {.compileTime.} =
for i in t1.hier:
for j in t2.hier:
if i.eqIdent j:
return some(i)
static:
echo ancestor(Child,R1)
# Some(R)
Another use case is when you want to iterate only the field pairs of some parent of an object. Currently fieldPairs doesn't allow you to differentiate.
isa isn't a very motivating example
sure, but I was at least referring to title of the thread Statically Check If An Object Is A Subtype Of Another Object, for which is is enough.
Instead of common ancestor, a more interesting primitive (on which common ancestor can be built too) is parent.
type Foo0 = object of RootObj
type Foo1 = object of Foo0
type Foo2 = object of Foo1
static: doAssert Foo2.parent is Foo1
static: doAssert Foo2.parent.parent is RootObj
PR welcome for parent in typetraits.nim
(and another PR for ancestor might be worth considering)