Hi,
How can I find out the original type of an object that has been promoted/demoted/casted (what is the right term here?) to a parent type without testing for each individual type with the of operator?
import macros
macro test(a: typed): untyped =
# how to get "T2" here?
type
T1 = ref object of RootObj
T2 = ref object of T1
var a = T1(T2())
test a
I am not sure what the result of your test() should be and for what you will use that result.
You can use type(x) to get the static base type of an object and name from typetraits module to get the typename: Following code works, but I don't know if it can help you:
type
A = ref object of RootObj
B = ref object of A
i: int
var b = new B
var s = newSeq[A]()
s.add(new A)
s.add(new B)
echo s[0] of A
echo s[0] of B
echo s[1] of A
echo s[1] of B
var x: type(s[1])
assert x is A # it is not B!
import typetraits
echo type(s[1]).name
echo type(x).name
$ ./t
true
false
true
true
A
A
Sorry Stefan, my original question was indeed lacking background info of what I am actually trying to achieve.
I'm trying to port a data model which was originally implemented in Lua to Nim. This consists of a tree of nodes, of which the leaves are heterogenous containers for a relatively large number of types. (tens, and growing)
My first implementation used object hierarchies, but I ran unto problems caused by mixing generics and multimethods. Araq explained that combining those is currently not a good idea, so I am looking for a way to drop the methods. Using object variants turned out to be cumbersome, so I am now back for a second try using object inheritance.
This basically works now, but I find myself writing long case statements handling all the possible object types in a few places, so I am looking to generalize things to avoid code repetition.
I will try to compile a compact example of my current implementation for comments.
Here's an extract of my code.
My issue is with the proc `$`(s: SNodeAny): string = function.
Basically, it is just a long list of if statements (could be a case as well, doesn't matter) to check if the object is of some type, and then apply the $ operator on the casted type. I feel this could be replaced by a two line macro or template, but for this I need to be able to deduce the original type of the passed SNode before I can cast and apply the $ operator.
My real code now includes 4 of these kind of functions where I need to check for every possible node kind, cast it, and apply some kind of generic function to it.
import strutils
type
IPv4Address = distinct string
IPv6Address = distinct string
MacAddress = distinct string
Interval = distinct float
# ... many more distinct types
type
SNodeAny = ref object of Rootobj
id: string
parent: SNodeAny
SNodeGroup = ref object of SNodeAny
children: seq[SNodeAny]
SNodeParam = ref object of SNodeAny
SNode[T] = ref object of SNodeParam
val: T
proc newSNode[T](id: string, val: T): SNodeAny =
SNode[T](id: id, val: val).SNodeAny
proc newSNodeGroup(id: string, children: openarray[SNodeAny]): SNodeAny =
var s = SNodeGroup(id: id)
for c in children.items:
s.children.add c
c.parent = result
return s
proc `$`(v: IPv4Address): string = v.string
proc `$`(v: IPv6Address): string = v.string
proc `$`(v: MacAddress): string = v.string
proc `$`(v: Interval): string = $ v.float
proc `$`(s: SNodeAny): string =
if s of SNode[bool]:
result = $ s.SNode[:bool].val
if s of SNode[int]:
result = $ s.SNode[:int].val
if s of SNode[IPv4Address]:
result = $ s.SNode[:IPv4Address].val
if s of SNode[IPv6Address]:
result = $ s.SNode[:IPv6Address].val
if s of SNode[MacAddress]:
result = $ s.SNode[:MacAddress].val
if s of SNode[Interval]:
result = $ s.SNode[:Interval].val
# many more of those
proc dump(s: SNodeAny, depth: int = 0) =
var l = repeat(" ", depth)
l.add s.id
if s of SNodeParam:
l.add "=" & $ s
echo l
if s of SNodeGroup:
for c in s.SNodeGroup.children.items:
dump(c, depth+1)
var s = newSNodeGroup("system", [
newSNodeGroup("identification", [
newSNode("name", "device 123"),
newSNode("location", "rack J2"),
newSNode("weight", 42.1),
newSNode("enabled", true),
newSNode("address", IPv4Address("10.0.0.3")),
])
])
s.dump
That is indeed too difficult for me to answer. Mr Felsing once suggested to use a proc in combination with methods to solve a similar issue, see
https://forum.nim-lang.org/t/3128
I hope some of the smart devs can answer your question -- maybe after the holidays.
Thanks for pointing me to that thread, that explains some of the details I did not yet grasp.
For now I'm good with a bit of redesign: I store a ref to object with some proc pointers for that specific type in each node. If this is solvable with a macro I can save a pointer per object though, which would be nice.