I'd like to make my own ref. So far my design looks like:
type
# root object, the header with refcount/RTTI is in a negative offset
MyRoot* {.pure, inheritable.} = object
# distinct so it can get a destructor
MyRef*[T] = distinct ptr T
# ... =destroy hooks etc. (not relevant here)
# trick to allow member access
template `.`*[T](t: MyRef[T]; field: untyped): untyped =
(ptr T)(t).field
# then I declare my object graph like
type
Foo = MyRef[FooObj]
FooObj = object of MyRoot
# ...
Bar = MyRef[BarObj]
BarObj = object of FooObj
# ...
It works for the most part, except now I have a problem where I can't pass Bar to Foo parameters even though BarObj is derived from FooObj. The "best" solution I've found so far is a converter-based hack:
# needs lent to avoid unnecessary refcount adjustment, but I'm not getting
# a ** in C for some reason without byref
converter toFoo[T: FooObj](r {.byref.}: MyRef[T]): lent Foo {.inline.} =
cast[ptr ptr Foo](unsafeAddr r)[][]
but a) I have to declare it for every single type, b) the converters greatly slow down compilation (from 6s -> 8s), so it's not too usable in practice.
Is there a better way?
As for your question: I think the current ways for smart-pointer-forwarding are underdeveloped. I would go with a public x: T field and change accesses from obj.field to obj.x.field.
To clarify, member accesses work fine with the `.` template, it's just that I have to manually upcast things that I didn't have to with ref, e.g.
type
Bar = ref object of RootObj
Foo = ref object of Bar
proc baz(a: Bar) =
discard
baz(Foo()) # I want this to work with MyRef too
I use a lot of inheritance so if I were to manually do the upcasts I'd have to add hundreds of them. (I actually did it for the performance comparison, it's very ugly.)