This version does type-checking (correctly pointing to procedure definition), 
and allows that simple method invocation syntax.
    
    
    type
      BinaryTree*[V; cmp: static[proc(a, b: auto): bool]] = object
    
    # different ways of providing a procedure
    # an `echo` is added to each of them, to distinguish
    
    const less1 = proc(a, b: int): bool = (echo 1; a < b)
    type T1 = BinaryTree[int, less1]
    var tree1: T1
    
    const less2 = proc(a, b: int): bool = (echo 2; a < b)
    var tree2: BinaryTree[int, less2]
    
    proc less3(a, b: int): bool = (echo 3; a < b)
    type T3 = BinaryTree[int, less3]
    var tree3: T3
    
    proc less4(a, b: int): bool = (echo 4; a < b)
    var tree4: BinaryTree[int, less4]
    
    var tree5: BinaryTree[int, proc(a, b: int): bool = (echo 5; a < b)]
    
    import macros
    proc desymbolize(node: NimNode): NimNode =
      result = node
      while result.kind == nnkSym:
        result = result.getImpl
    # the macro can be quite short, if to restrict to just one of formats above
    macro compare(v: typed): auto =
      var p=v.getImpl[1]
      var ty: NimNode
      if p.kind == nnkSym:
        p = p.getImpl
        expectKind p, nnkTypeDef
        expectKind p[2], nnkBracketExpr
        expectKind p[2][2], nnkSym
        ty = p[2][1]
        p = p[2][2]
      else:
        ty = p[1]
        p = p[2]
        if p.kind == nnkSym:
          p = p.getImpl
        else:
          p = p[0]
      let prm = params(desymbolize p)
      if prm.len != 2 or prm[1].len != 4 or prm[1][2] != ty:
        error("Wrong comparison procedure provided, signature should be " &
              "``proc(a, b: " & $ty & "): bool``.\n", p.desymbolize)
      if p.kind == nnkProcDef:
        p = p[0]
      p
    template compare*[T: BinaryTree, V](bt: T, a, b: V): bool =
      compare(bt)(a,b)
    
    # testing
    var n = 20
    echo tree1.compare(n, 30)
    echo tree2.compare(n, 30)
    echo tree3.compare(n, 30)
    echo tree4.compare(n, 30)
    echo tree5.compare(n, 30)
    
    
    Run

Reply via email to