Skip to content

Instantly share code, notes, and snippets.

@jovial
Last active August 29, 2015 14:08
Show Gist options
  • Save jovial/00fdcae18ac9bfa1a6af to your computer and use it in GitHub Desktop.
Save jovial/00fdcae18ac9bfa1a6af to your computer and use it in GitHub Desktop.
static call method hack
import macros
macro genStaticGetter(a: expr): stmt =
## generates a getter which returns a pointer to a method with
## a particular type signature.
##
## The getter has the form methodname(typ: type(param1), typ2: type(param2), ...),
## where methodname is the name of the method passed to this macro, and param#
## corresponds to the the param in position # of the method signature. # is a integer
## representing the position of the parameter.
##
## WARNING: doesn't work with generics
expectKind(a,nnkDo)
result = newNimNode(nnkStmtList)
# this seg faults for some reason
#let methDef = a.findChild(it.kind == nnkMethodDef)
let methDef = a.findChild(it.kind == nnkProcDef)
if methDef == nil:
error("couldn't find method definition")
let methIdent = methDef.findChild(it.kind == nnkIdent)
if methIdent == nil:
error("couldn't find method definition")
# convert proc to method
var temp = newNimNode(nnkMethodDef)
for child in methDef.children:
temp.add(child)
result.add temp
# stores a reference to the method we are adding currently
let procTyp = newNimNode(nnkProcTy).add(methDef.params).add(newEmptyNode())
let thisMethSym = genSym(nskLet, "thisMethod")
let identDefs = newIdentDefs(thisMethSym, procTyp, methIdent)
let letSection = newNimNode(nnkLetSection).add(identDefs)
result.add letSection
# this extracts the types for the method signature
var paramIdentDefs = newSeq[PNimrodNode](0)
let procTy = identDefs.findChild(it.kind == nnkProcTy)
let formalParams = procTy.findChild(it.kind == nnkFormalParams)
var count = 0
for child in formalParams.children:
if child.kind == nnkIdentDefs:
# FIXME: assumes type was specified!!!
if child.len < 1:
error("method must specify type of param")
let paramSym = genSym(nskParam, "param" & $count)
let paramType = newNimNode(nnkBracketExpr).add(newIdentNode(!"typedesc"), child[1])
let paramDefs = newIdentDefs(paramSym, paramType)
paramIdentDefs.add paramDefs
count.inc
#make getter
var params = newSeq[PNimrodNode](0)
params.add(newIdentNode(!"auto")) # return type
for node in paramIdentDefs:
params.add(node)
let body = newNimNode(nnkStmtList)
.add newNimNode(nnkReturnStmt).add(thisMethSym)
let getter = newProc(methIdent,params,body)
result.add getter
type A = ref object of RootObj
type B = ref object of A
type C = ref object of B
# There seems to be a compiler bug when passing a methods to a macro,
# so we declare these as procedures but they are converted to methods
# by the macro.
proc test(x: A, y: int) {.genStaticGetter.} =
echo "got A"
proc test(x: B, y: int) {.genStaticGetter.} =
test(A, type(y))(x.A, y) # calls test(A,int)
echo "got B"
proc test(x: C, y: int) {.genStaticGetter.} =
test(B, type(y))(x.B, y) # calls test(B,int)
echo "got C"
# one param version in case it's clearer
proc test2(x: A) {.genStaticGetter.} =
echo "received A"
proc test2(x: B) {.genStaticGetter.} =
test2(A)(x.A)
echo "received B"
proc test2(x: C) {.genStaticGetter.} =
# you can also split the calls
let super = test2(B)
# equivalent to:
#let super: proc(x:B) = test2
super(x.B)
echo "received C"
let testee: A = C()
test(testee,123)
test2(testee)
test(A,int)(testee.A, 5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment