Thank you very much, dawkot. So I've ended up with something like this
import macros
import strutils
import strformat
export actors
############################
### Entities
############################
type
ent* = object
id*: int
age*: uint8
var
entStash = newSeqOfCap[ent](256)
proc newEntity*(): ent =
var id_last {.global.} = 1
if entStash.len > 0:
result.id = entStash[0].id
result.age = entStash[0].age+1
entStash.del(0)
else:
let id = id_last
id_last += 1
result.id = id
result.age = 0
proc release*(entity: ent) =
add(entStash, entity)
##############################
### Components
##############################
type Storage[T] = seq[T]
proc newStorage[T](size: int): seq[T] = newSeq[T](size)
proc components[T](x: var seq[T]): var seq[T] = x
macro getComponentPretty(t: untyped): untyped =
let tName = $NimNode(t) # name of the component
let pragma = "{.inline.}"
let source = fmt("""
proc {toLowerAscii(tName[..0]) & tName[1..tName.high]}(self: ent): ptr
{tName} {pragma} =
self.getComponent {tName} """)
result = parseStmt(source)
template reg(t: typedesc, size = 256) =
var storage {.used.} = newStorage[t]size
proc getStorage(_: typedesc[t]): var Storage[t] {.inline.} =
storage
proc getComponent(self: ent, _: typedesc[t]): ptr t {.inline.} =
addr storage.components[self.id]
getComponentPretty(t)
type
ComponentMotion = object
x, y: float
ComponentObject = object
name: string
reg ComponentMotion
reg ComponentObject
var entity = newEntity()
var cMotion = entity.componentMotion
var cObject = entity.componentObject
cMotion.x = 20
cObject.name = "Pixeye"
echo getStorage(ComponentMotion)[1].x
Run
It wasn't easy to understand all that template/macros magic... if there is any
more elegant way of doing the code below?
macro getComponentPretty(t: untyped): untyped =
let tName = $NimNode(t) # name of the component
let pragma = "{.inline.}"
let source = fmt("""
proc {toLowerAscii(tName[..0]) & tName[1..tName.high]}(self: ent): ptr
{tName} {pragma} =
self.getComponent {tName} """)
result = parseStmt(source)
template reg(t: typedesc, size = 256) =
var storage {.used.} = newStorage[t]size
proc getStorage(_: typedesc[t]): var Storage[t] {.inline.} =
storage
proc getComponent(self: ent, _: typedesc[t]): ptr t {.inline.} =
addr storage.components[self.id]
getComponentPretty(t)
Run
The reason why I need getComponentPretty() is to take components from storage
in a pretty way like this:
var cMotion = entity.componentMotion
var cObject = entity.componentObject
Run
instead of this
var cMotion = en.getComponent(ComponentMotion).x = 10
var cObject = en.getComponent(componentObject).x = 10
Run
Please excuse if my code is naive, I'm learning : )