I tried to improve the above first draft. With the hints in
[https://stackoverflow.com/questions/38029742/nim-reflect-on-types-field-types-at-compile-time](https://stackoverflow.com/questions/38029742/nim-reflect-on-types-field-types-at-compile-time)
I got the following working code. It does not require typetraits any longer
and is more type safe: If arguments of the clicked procs do not match, I seems
to get indeed compile time errors as desired. I found no way to avoid the
additional template. I is required to get the typedescs and pass them to the
macro. Indeed passing a ref typedesc to the macro seems not to work, so I have
to use type(widget[]) in the template to pass the concrete type of the widget
to the macro. With just type(widget) only something like ref object seems to be
passed. But I want of course Window, Button... for type safety. (Garbage
collector seems to work also fine with the GTK widgets now. But of course I
still have to do much testing.)
# plain test for high level gi based GTK3 Nim wrapper
# https://github.com/StefanSalewski/nim-gi2
import gtk, glib, gobject
import macros
import strutils
#import typetraits
type
ParObj = object
x: float
type
XButton = ref object of Button
x: int
# TODO: will be moved to library module!
let Quark = g_quark_from_static_string("NimGIQuark")
# TODO: will be moved to library module!
proc initWithArgv*() =
var
cmdLine{.importc.}: cstringArray
cmdCount{.importc.}: cint
gtk.gtk_init(cmdCount, cmdLine)
proc clicked(button: XButton; arg: ParObj) =
echo arg.x
echo button.x
proc clicked2(button: Button; arg: string) =
echo arg
proc clicked3(button: Button; arg: int) =
echo arg
proc bye(w: Window; arg: string) =
mainQuit()
echo arg
var ProcID: int
template xxx(t: typed): typedesc =
type(t)
# TODO: this macro will be moved to library module!
macro mconnect(widget: Widget; signal: string; p: untyped; arg: typed;
widgett: typedesc; argt: typedesc): typed =
inc(ProcID)
let wt = getType(widgett)[1] # widget type
let at = getType(argt)[1] # argument type
echo treerepr(wt)
echo treerepr(at)
let signalName = ($signal).replace("-", "_") # maybe we should just use
plain proc names
let procNameCdecl = newIdentNode("connect_for_signal_cdecl_" & signalName
& $ProcID)
let procName = newIdentNode("connect_for_signal_" & signalName & $ProcID)
let scName = newIdentNode("sc" & signalName)
result = quote do:
proc `procNameCdecl`(button: ptr Object00 , data: pointer) {.cdecl.} =
var h: pointer = g_object_get_qdata(button, Quark)
`p`(cast[ref `wt`](h), cast[`at`](data))
proc `procName`(self: ref `wt`; p: proc (self: ref `wt`, arg: `at`);
a: `at`) =
var ar: ref `at`
new(ar)
deepCopy(ar[], a)
GC_ref(ar)
`scName`(self, `procNameCdecl`, cast[pointer](ar[]))
`procName`(`widget`, `p`, `arg`)
template connect(widget: Widget; signal: string; p: untyped; arg: typed) =
mconnect(widget, signal, p, arg, type(widget[]), type(arg))
proc work =
var window: Window = newWindow(WindowType.topLevel)
window.setTitle("First Test")
window.setBorderWidth(10)
var
box = newBox(Orientation.vertical, 0)
button1: XButton
button2 = newButton("Wrapper")
button3 = newButton("Int button")
button4 = newButton("Newer used")
parObj: ParObj
parObj.x = 3.1415
initButton(button1)
button1.setLabel("Nim GI")
button1.x = 99
connect(button1, "clicked", clicked, parObj)
connect(button3, "clicked", clicked3, 1234567)
connect(window, "destroy", bye, "Bye")
button2.connect("clicked", (proc(button: Button; arg: string) = echo
arg), "Bye")
box.add(button1)
box.add(button2)
box.add(button3)
echo "removing button2"
box.remove(button2)
echo "and add again"
box.add(button2)
echo "done"
echo "removing button2"
box.remove(button2)
echo "and add again"
box.add(button2)
echo "done"
assert(button4 != nil)
window.add(box)
window.showAll
let p = button1.getParent
assert(p == box)
proc main() =
initWithArgv()
work()
GC_fullCollect()
gtk.gtkMain()
main()