Hi y'all!
I wrote a macro for conveniently declaring widget types when using traitor, it is used to create a type that conforms to the interface built with traitor with the corresponding member variables. The widget type is generic, where the parameter is the type of the display that is used, and with the display also comes the type of color that is used. In the type definition I use
fgcolor*: typeof(T.color)
which works fine when used directly in the code, but does cause Error: no field symbol when used within a macro.
Here is the reduced code with the macro:
import std/macros
import std/genasts
import traitor
type
AnyDisplay*[T] = concept display
display.color is T
TestDisplay = ref object
color: int
type
Widgetable* = distinct tuple[
setPos: proc(a: Atom, x,y: int) {.nimcall.},
getx: proc(a: Atom): int {.nimcall.},
]
implTrait Widgetable
macro implementWidget*(ast: untyped): untyped =
var
name = ast[0][0][0][1]
self = newIdentNode("self")
result = newNimNode(nnkStmtList)
result.add(ast)
var
base = genAst(name, self) do:
type
Widget*[T: AnyDisplay] = ref object
fgcolor*: typeof(T.color)
display*: T
x: int
y: int
# add variables from base to resulting type
if result[0][0].kind == nnkTypeSection:
if result[0][0][0].kind == nnkTypeDef:
if result[0][0][0][2].kind == nnkRefTy:
if result[0][0][0][2][0].kind == nnkObjectTy:
if result[0][0][0][2][0][2].kind == nnkRecList:
for rec in base[0][2][0][2]:
result[0][0][0][2][0][2].add(rec)
var
fg = newIdentNode("fg")
bg = newIdentNode("bg")
x = newIdentNode("x")
y = newIdentNode("y")
# add procs needed for beeing valid "Widgetable" trait
result.add genAst(name, self, fg, x, y) do:
proc setfgColor*[T](self: var name[T], fg: auto) =
self.fgcolor = fg
proc setPos*[T](self: var name[T], x,y: int) =
self.x = x
self.y = y
proc getx*[T](self: name[T]): int =
return self.x
# call macro that generates trait converter
result.add genAst(name) do:
emitConverter name, Widgetable
echo result.repr
implementWidget:
type
Label*[T] = ref object
text: string
var
disp: TestDisplay
foo: Label[TestDisplay]
And here is the code without the macro, using the code that is printed with echo result.repr at the end of macro:
import std/macros
import std/genasts
import traitor
type
AnyDisplay*[T] = concept display
display.color is T
TestDisplay = ref object
color: int
type
Widgetable* = distinct tuple[
setPos: proc(a: Atom, x,y: int) {.nimcall.},
getx: proc(a: Atom): int {.nimcall.},
]
implTrait Widgetable
type
Label*[T] = ref object
text: string
fgcolor*: typeof(T.color)
display*: T
x: int
y: int
proc setfgColor*[T](self: var Label[T]; fg: auto) =
self.fgcolor = fg
proc setPos*[T](self: var Label[T]; x, y: int) =
self.x = x
self.y = y
proc getx*[T](self: Label[T]): int =
return self.x
emitConverter Label, Widgetable
var
disp: TestDisplay
foo: Label[TestDisplay]
I don't understand why the code works when I just copy the output of the macro, but not when macro itself is used ...
Firstly typeof(T.color) is invalid cause T is a typedesc it does not have a color field. typeof(default(T).color) should work but Nim's generics always fight you to the death so this does not work.
If we define template color(T: typedesc): untyped = typeof(default(T).color) to delay resolution a bit, then do fgColor*: color(T) we get the macro to compile.
Hi Beef,
your help is appreciated and valuable as always, and your suggestion solves my problem, thanks a lot!
Interestingly using typeof(T.color) outside the macro does indeed compile and work, maybe a bug in the compiler? But of course using default() does make much more sense :)