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()
    
    

Reply via email to