I'm working on exactly the same problem as @andrea did a while ago (registering procs at compile-time) and I'm running into the same difficulty: How can I capture the content of a compile time variable at the end of the entire compilation and turn it into a runtime variable.
Imagine there is a remote.nim module (omitting ids & mapping):
import macros
var registeredProcs {.compileTime.} = newSeq[string]()
macro remote*(n: untyped): untyped =
let procName = n[0]
registeredProcs &= $procName
echo registeredProcs
result = n
proc square(x: float): float {.remote.} = x * x
proc cubic(x: float): float {.remote.} = x * x * x
# this compiles, but it doesn't really work
const procs = registeredProcs
proc genericCall*(id: string, x: float): float =
echo "looking up ", id, " in ", procs
for funcId in procs:
if id == funcId:
echo "would call id: ", funcId
# locally in this module it works fine
discard genericCall("square", 1)
discard genericCall("cubic", 1)
The problem is that external usage doesn't seem to work. I.e., if there is a user.nim:
import remote as remote_module
proc userSquare(x: float): float {.remote.} = x * x
proc userCubic(x: float): float {.remote.} = x * x * x
discard genericCall("userSquare", 1)
discard genericCall("userCubic", 1)
I can see that the usages of {.remote.} on client side do modify the registeredProcs, but I can't seem to capture them as well in the runtime variable procs. Any ideas how to approach this? I feel like I need to run some static code after everything else is compiled, but that is probably impossible.
How can I capture the content of a compile time variable at the end of the entire compilation and turn it into a runtime variable.
I think this behavior will change soon https://github.com/nim-lang/Nim/pull/6041 but as a workaround, every call to remote could store in a file the content of registeredProcs and then use slurp.
An easy workaround to not add genericCall - passing procs as the first argument:
# remote.nim, only changes
# making exported
var registeredProcs* {.compileTime.} = newSeq[string]()
# ...
proc genericCall*(procs: seq[string], id: string, x: float): float =
# ...
# user.nim
import remote as remote_module
proc userSquare(x: float): float {.remote.} = x * x
proc userCubic(x: float): float {.remote.} = x * x * x
const procs = registeredProcs
discard procs.genericCall("userSquare", 1)
discard procs.genericCall("userCubic", 1)
Maybe you can make procs in genericCall bound to the current module's symbol, then you can get rid of passing it as an argument.
You should be able to just write some code to add to a runtime seq or table. Something like this:
import macros
# no need for "{.compile.}"
var registeredProcs = newSeq[string]()
macro remote*(n: untyped): untyped =
let procName = n[0]
# This template will add the procs to whatever runtime variable you want,
# so you can put a table inside here as well :)
template addRegProc(procName, n) =
registeredProcs &= procName
echo registeredProcs
n
result = getAst(addRegProc($procName, n))
proc square(x: float): float {.remote.} = x * x
proc cubic(x: float): float {.remote.} = x * x * x
proc genericCall*(id: string, x: float): float =
echo "looking up ", id, " in ", registeredProcs
for funcId in registeredProcs:
if id == funcId:
echo "would call id: ", funcId
# locally in this module it works fine
discard genericCall("square", 1)
discard genericCall("cubic", 1)
With that code, your user.nim file compiles and runs as expected.