In the last days I created the requested macro for

[https://forum.nim-lang.org/t/5418](https://forum.nim-lang.org/t/5418)

[https://github.com/StefanSalewski/gintro/blob/master/gintro/gimpl.nim#L308](https://github.com/StefanSalewski/gintro/blob/master/gintro/gimpl.nim#L308)

which printed out looks like code below and is used like in 
[http://ssalewski.de/gintroreadme.html#_a_listview_example_using_a_celldatafunction](http://ssalewski.de/gintroreadme.html#_a_listview_example_using_a_celldatafunction)

Testing is currently only possible with nimble install gintro@#head

Is there something what I should improve? I may consider rewriting it without 
use of parseStmt() with only AST manipulation, but will there be a large 
benefit? One question is the use of ` if arg == nil:` for testing existence of 
optional argument. Because of 
[https://github.com/nim-lang/Nim/issues/12579](https://github.com/nim-lang/Nim/issues/12579)
 I wonder if that test will continue to work, or should I rewrite it?
    
    
    ### CellDataFunc,
    # see 
https://developer.gnome.org/gtk3/stable/GtkTreeViewColumn.html#gtk-tree-view-column-set-cell-data-func
    
    # the low level GTK interface:
    # proc cellDataFunc(column00: ptr TreeViewColumn00; renderer00: ptr 
CellRenderer00;
    #   model00: ptr TreeModel00; iter: TreeIter; data: pointer) {.cdecl.}
    
    # unset -- the macro can do this already
    proc unsetCellDataFunc*(column: TreeViewColumn; renderer: CellRenderer) =
      setCellDataFunc(column, renderer, nil, nil, nil)
    
    # set a function with this parameter list and one more optional argument
    # proc cellDataFunc(column: TreeViewColumn; renderer: CellRenderer; model: 
TreeModel; iter: TreeIter)
    # if there is no optional arg, then we make it a pointer with nil value.
    # if arg is a ref, then we apply GC_ref() and pass it
    # if arg is a ptr, then we pass it. (but we never free that object)
    # if arg is a value type, then we replace it with a copy of a ref type to 
guaranty lifetime
    macro setCellDataFunc*(column: TreeViewColumn; renderer: CellRenderer; fn: 
untyped = nil; arg: typed = nil): untyped =
      
      if fn == nil: # unset
        result = parseStmt("setCellDataFunc($1, $2, nil, nil, nil)" % [$column, 
$renderer])
        return
      
      var CdfID {.compileTime, global.}: int # unique id for our generated cell 
data functions
      inc(CdfID)
      var ats: string # the type of optional argument as string (arg type 
string)
      var argStr: string # the value of optional argument as string
      var atsfix: string # we have to prefix with "ref " if arg is avalue type
      if arg == nil: # no optional argument, so fake a nil pointer
        argStr = "nil"
        ats = "pointer"
      else:
        argStr = $(arg.toStrLit)
        ats = $getTypeInst(arg).toStrLit
        
        if getTypeInst(arg).typeKind != ntyRef and getTypeInst(arg).typeKind != 
ntyPtr:
          atsFix = "ref "
      
      let procName = "celldatafunc_" & $CdfID # our init code
      let procNameCdecl = "celldatafunc_cdecl_" & $CdfID # the proc that is 
called from GTK
      var procNameDestroy: string # the proc that GTK calls when we unset the 
cellDataFunc
      # first we generate the destroy proc
      var r0s: string
      
      # GTK should unref the optional argument later, so we generate a destroy 
proc which GTK can call
      if arg != nil and getTypeInst(arg).typeKind != ntyPtr:
        procNameDestroy = "celldatafunc_destroy" & $CdfID
        r0s ="""
    proc $1(p: pointer) {.cdecl.} =
      # echo "GC_Unref(arg)"
      GC_Unref(cast[$2](p))
    """ % [$procNameDestroy, atsFix & ats]
      else:
        procNameDestroy = "nil"
        r0s = ""
    
    # now we generate the proc which GTK will call for each cell of the 
treeview. g_object_get_qdata() is
    # the GTK function which retrieves the Nim proxy objects from the plain GTK 
data objects.
      var r1s = """
    proc $1(column00: ptr TreeViewColumn00; renderer00: ptr CellRenderer00;
                      model00: ptr TreeModel00; iter: TreeIter; pdata: pointer) 
{.cdecl.} =
      let column = cast[TreeViewColumn](g_object_get_qdata(column00, Quark))
      let renderer = cast[CellRenderer](g_object_get_qdata(renderer00, Quark))
      let model = cast[TreeModel](g_object_get_qdata(model00, Quark))
    """ % [$procNameCdecl]
      if arg == nil:
        r1s.add """
      $1(column, renderer, model, iter) # call the user provided Nim proc
    """ % [$fn]
      elif getTypeInst(arg).typeKind == ntyRef or getTypeInst(arg).typeKind == 
ntyPtr:
        r1s.add """
      let a = cast[$2](pdata)
      $1(column, renderer, model, iter, a) # call the user provided Nim proc
    """ % [$fn, ats]
      else: # arg was a value type, but we pass a copy of ref type
        r1s.add """
      let a = cast[ref $2](pdata)[]
      $1(column, renderer, model, iter, a) # call the user provided Nim proc
    """ % [$fn, ats]
    
    # this is basically only a code block which is called for initialization, 
mostly
    # making a deep copy of optional argument when argumant is not a ref
      var r2s: string
      if arg == nil:
        r2s ="""
    proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
        setCellDataFunc($3, $4, $5, nil, nil)
    $1(column, renderer, $6)
    """ % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, 
$procNameDestroy]
      elif getTypeInst(arg).typeKind == ntyPtr:
        r2s ="""
    proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
        setCellDataFunc($3, $4, $5, cast[pointer](pdata), $7)
    $1(column, renderer, $6)
    """ % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, 
$procNameDestroy]
      elif getTypeInst(arg).typeKind == ntyRef:
        r2s ="""
    proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
        if not pdata.isNil: # or pdata is pointer):
          GC_ref(pdata)
        setCellDataFunc($3, $4, $5, cast[pointer](pdata), $7)
    $1(column, renderer, $6)
    """ % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, 
$procNameDestroy]
      else: # arg is value object
        r2s ="""
    proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
        var ar: ref $2
        new(ar)
        deepCopy(ar[], pdata)
        GC_ref(ar)
        setCellDataFunc($3, $4, $5, cast[pointer](ar), $7)
    $1(column, renderer, $6)
    """ % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, 
$procNameDestroy]
      
      result = parseStmt(r0s & r1s & r2s)
    
    
    Run

Reply via email to