macro loadTables*(
varName: untyped, varTable: untyped, resourceType: typed,
objvExpr: untyped): untyped =
let loadProc = ident("load" & $resourceType)
result = quote do:
let `varTable` = cast[TTable](clData)
let `varName` = `varTable`.`loadProc`(interp, `objvExpr`)
if `varName`.isNil:
return MSG_ERROR
I am experimenting with macros in Nim. When I call this macro like this :
loadTables(ctx1, ttable, "CTX", obj1)
It works, but when I call this macro twice like this :
loadTables(ctx1, ttable, "CTX", obj1)
loadTables(ctx2, ttable, "CTX", obj2)
I have this error :template/generic instantiation of `loadTables` from here Error: redefinition of 'ttable'; previous declaration here: utils.nim(37, 12)
I found a solution by defining my cast[] before calling my macro:
let ttable = cast[TTable](clData)
loadTables(ctx1, ttable, "CTX", obj1)
loadTables(ctx2, ttable, "CTX", obj2)
But I liked the design of my first macro.
Can someone tell me how to solve this?
The problem is that your macro expands to something like:
let ttable = cast[TTable](clData)
...
let ttable = cast[TTable](clData)
This redeclaration is not allowed, you can avoid it by using block scope:
macro loadTables*(
varName: untyped, varTable: untyped, resourceType: typed,
objvExpr: untyped): untyped =
let loadProc = ident("load" & $resourceType)
result = quote do:
block:
let `varTable` = cast[TTable](clData)
let `varName` = `varTable`.`loadProc`(interp, `objvExpr`)
if `varName`.isNil:
return MSG_ERROR
Then it'll expand into two scopes:
block:
let ttable = cast[TTable](clData)
...
block:
let ttable = cast[TTable](clData)
...
Or use gensym to generate always unique identifier.
Or, if you're not using ttable outside the macro call (which I'm guessing is the case since you declare it in the macro) you can just do:
macro loadTables*(
varName: untyped, varTable: untyped, resourceType: typed,
objvExpr: untyped): untyped =
let loadProc = ident("load" & $resourceType)
result = quote do:
let ttable = cast[TTable](clData)
let `varName` = ttable.`loadProc`(interp, `objvExpr`)
if `varName`.isNil:
return MSG_ERROR
Thank you both.
If I understand correctly, with a block:, the variable is only accessible within that block, same for genSym. But I need to access the ttable variable after the macro calls.
I may have found a solution with when not declared() (https://nim-lang.org/docs/system.html#declared,untyped):
result = quote do:
when not declared(`varTable`):
let `varTable` = cast[TTable](clData)
let `varName` = `varTable`.`loadProc`(interp, `objvExpr`)
if `varName`.isNil:
return MSG_ERROR
loadTables(ctx1, ttable, "CTX", obj1)
loadTables(ctx2, ttable, "CTX", obj2) # works...
Can you tell me what you think about it ?Finally I decided to declare ttable as a variable outside the macro rather than inside it. This way, ttable remains accessible and usable throughout the rest of my code after the macro invocations. I was afraid to use when not declared, not too sure of myself at all.
Thanks to everyone.
a block can have a “return”
let something = block:
let a = 2
let b = 3
2 + 3
echo $something # 5
this would allow something like
var ttable = cast[TTable](clData)
ttable = loadTables(ctx1, ttable, "CTX", obj1)
ttable = loadTables(ctx2, ttable, "CTX", obj2)