C++11 like Smart pointers. It always uses the shared allocator.


ConstPtr[T] = distinct SharedPtr[T]
Distinct version of referencing counting smart pointer SharedPtr[T], which doesn't allow mutating underlying object.
SharedPtr[T] = object
  val: ptr tuple[value: T, atomicCounter: int]
Shared ownership reference counting pointer
UniquePtr[T] = object
  val: ptr T
Non copyable pointer to object T, exclusive ownership of the object is assumed.


proc `$`[T](p: ConstPtr[T]): string {.inline.}
proc `$`[T](p: SharedPtr[T]): string {.inline.}
proc `$`[T](p: UniquePtr[T]): string {.inline.}
proc `=`[T](dest: var SharedPtr[T]; src: SharedPtr[T])
proc `=`[T](dest: var UniquePtr[T]; src: UniquePtr[T]) {.error.}
proc `=destroy`[T](p: var SharedPtr[T])
proc `=destroy`[T](p: var UniquePtr[T])
proc `[]`[T](p: ConstPtr[T]): lent T {.inline.}
proc `[]`[T](p: SharedPtr[T]): var T {.inline.}
proc `[]`[T](p: UniquePtr[T]): var T {.inline.}
proc isNil[T](p: ConstPtr[T]): bool {.inline.}
proc isNil[T](p: SharedPtr[T]): bool {.inline.}
proc isNil[T](p: UniquePtr[T]): bool {.inline.}
proc newConstPtr[T](val: sink T): ConstPtr[T]
proc newSharedPtr[T](val: sink T): SharedPtr[T] {.nodestroy.}
proc newUniquePtr[T](val: sink T): UniquePtr[T] {.nodestroy.}


converter convertConstPtrToObj[T](p: ConstPtr[T]): lent T {.inline.}
converter convertSharedPtrToObj[T](p: SharedPtr[T]): var T {.inline.}
converter convertUniquePtrToObj[T](p: UniquePtr[T]): var T {.inline.}