I'm trying to implement some macros that inject method to python class, interfaced by nimpy. I achieve this by some ugly macros:
import nimpy, std/macros, ast_pattern_matching, sequtils
let py = pyBuiltinsModule()
macro pyInjectMethod*(obj, memFuncs: untyped): untyped =
obj.expectKind nnkIdent
memFuncs.expectKind nnkStmtList
result = newNimNode(nnkStmtList)
for memFunc in memFuncs.children:
memFunc.matchAst:
of nnkProcDef(`func_name` @ nnkIdent, _, _, `params` @ nnkFormalParams, _, _, _):
discard
var params_updated : NimNode = params
paramsUpdated.del(1)
var call = newNimNode(nnkCall)
call.add(func_name, obj)
for i, x in params.pairs:
if i == 0: continue
call.add(x[0])
let
wrapedStatements = quote do:
`memFunc`
`call`
wrapFunc = newProc(
params=toSeq(paramsUpdated.children),
body=wrapedStatements)
result.add(quote do:
let f = `wrapFunc`
`obj`.`funcName` = f)
else:
raise newException(FieldDefect, "Input data wrong")
template pyClass*(bases: untyped, members: untyped): untyped =
let b = bases
when b is PyObject:
py.`type`("_", (b, ), toPyDict(members)).to(proc(): PyObject{.gcsafe.})()
else:
py.`type`("_", b, toPyDict(members)).to(proc(): PyObject{.gcsafe.})()
So the question is, is there a better approach? Thanks a lot!Maybe you can refer to this issue: https://github.com/yglukhov/nimpy/issues/246
With such macros, we can write class inheritance more concisely:
import nimpy, strutils, macros, ast_pattern_matching, sequtils
let py = pyBuiltinsModule()
discard py.exec("""
class Foo:
...
""".dedent())
let fcls = pyGlobals()["Foo"]
var o = pyClass((fcls,), {"x": 3.14})
pyInjectMethod(o):
proc add(self: PyObject, a: float) =
self.x = (self.x).to(float) + a
proc sum(self: PyObject) =
echo "The sum is ", self.x
for i in 1..100:
discard o.add(i)
discard o.sum()
it's not necessarily nicer than your macro, and i believe it to be rather fragile, but you can achieve a similar thing with a template:
import sugar
template setmethod(x:PyObject, name: untyped, body: proc) =
let f = () => body(x)
x.name = f
o.setmethod(sum) do (self:PyObject):
echo "The sum is ", self.x