宏通过其定义生成函数实现?
Macro to generate function implementation by its definition?
是否可以自动编写远程函数的实现?
如果是手写的话,它看起来像下面的代码。同时拥有函数声明和实现很重要。
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
proc multiply(a, b: int): int =
rcall("multiply", a, b, int)
我想把它自动化写成
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
remotefn multiply
并且 remotefn
宏应该查看函数定义并将其实现生成为 rcall("multiply", a, b, int)
,这可能吗?
Would it be possible to automate writing the implementation ...
是的,使用带有 typed
参数(然后是 getTypeImpl
)
的 nim 宏,可以自动执行几乎任何事情
import std/macros
macro dumpImpl(arg: typed): untyped =
echo arg.getTypeImpl().treeRepr()
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
proc multiply(a, b: int): int =
rcall("multiply", a, b, int)
dumpImpl multiply
表明multiply
类型(函数签名)具有以下结构:
ProcTy
FormalParams
Sym "int"
IdentDefs
Sym "a"
Sym "int"
Empty
IdentDefs
Sym "b"
Sym "int"
Empty
Empty
虽然重要的是要记住重载过程不能仅根据名称轻松解决(因为,好吧,有 许多 实现)。最明显的选择是仍然使用 typed
参数,但传递一些参数来消除函数调用的歧义
import std/macros
macro dumpImpl(arg: typed): untyped =
echo arg.treeRepr()
proc overload(a: string) = discard
proc overload(a: int) = discard
dumpImpl overload
# ClosedSymChoice - lists all possible overloads with their respective symbols
# Sym "overload"
# Sym "overload"
dumpImpl overload("123")
#Call
# Sym "overload"
# StrLit "123"
dumpImpl overload(123)
#Call
# Sym "overload"
# IntLit 123
作为一个小的(个人)旁注——当你谈论 nim 宏时,问题应该不是“如果这可能吗?”而是“执行此操作的最佳方法是什么?”。它可能需要一些宏技巧的知识,但几乎可以实现任何东西。
EDIT1(添加实现代码示例,在评论中回复问题):
import std/[macros]
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
macro remotefn(fn: typed) =
let fname = fn.str_val()
# `quote do` generates hygienic identifiers - i.e. all new
# variables/functions introduced by it are unique and not visible in
# global. To fix this you need to explicitly create identifier for your
# procedure using `ident`
let
multId = ident("multiply") # < Function name identifier
# Same goes for variables - nim does have a name-based overloading, so
# you need to make sure function arguments use the same identifiers as
# the original one
aId = ident("a")
bId = ident("b")
result = quote do:
proc `multId`(`aId`, `bId`: int): int =
rcall(`fname`, 1, 1, int)
echo result.toStrLit()
proc multiply(a, b: int): int
remotefn multiply
是否可以自动编写远程函数的实现?
如果是手写的话,它看起来像下面的代码。同时拥有函数声明和实现很重要。
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
proc multiply(a, b: int): int =
rcall("multiply", a, b, int)
我想把它自动化写成
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
remotefn multiply
并且 remotefn
宏应该查看函数定义并将其实现生成为 rcall("multiply", a, b, int)
,这可能吗?
Would it be possible to automate writing the implementation ...
是的,使用带有 typed
参数(然后是 getTypeImpl
)
import std/macros
macro dumpImpl(arg: typed): untyped =
echo arg.getTypeImpl().treeRepr()
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
proc multiply(a, b: int): int =
rcall("multiply", a, b, int)
dumpImpl multiply
表明multiply
类型(函数签名)具有以下结构:
ProcTy
FormalParams
Sym "int"
IdentDefs
Sym "a"
Sym "int"
Empty
IdentDefs
Sym "b"
Sym "int"
Empty
Empty
虽然重要的是要记住重载过程不能仅根据名称轻松解决(因为,好吧,有 许多 实现)。最明显的选择是仍然使用 typed
参数,但传递一些参数来消除函数调用的歧义
import std/macros
macro dumpImpl(arg: typed): untyped =
echo arg.treeRepr()
proc overload(a: string) = discard
proc overload(a: int) = discard
dumpImpl overload
# ClosedSymChoice - lists all possible overloads with their respective symbols
# Sym "overload"
# Sym "overload"
dumpImpl overload("123")
#Call
# Sym "overload"
# StrLit "123"
dumpImpl overload(123)
#Call
# Sym "overload"
# IntLit 123
作为一个小的(个人)旁注——当你谈论 nim 宏时,问题应该不是“如果这可能吗?”而是“执行此操作的最佳方法是什么?”。它可能需要一些宏技巧的知识,但几乎可以实现任何东西。
EDIT1(添加实现代码示例,在评论中回复问题):
import std/[macros]
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
macro remotefn(fn: typed) =
let fname = fn.str_val()
# `quote do` generates hygienic identifiers - i.e. all new
# variables/functions introduced by it are unique and not visible in
# global. To fix this you need to explicitly create identifier for your
# procedure using `ident`
let
multId = ident("multiply") # < Function name identifier
# Same goes for variables - nim does have a name-based overloading, so
# you need to make sure function arguments use the same identifiers as
# the original one
aId = ident("a")
bId = ident("b")
result = quote do:
proc `multId`(`aId`, `bId`: int): int =
rcall(`fname`, 1, 1, int)
echo result.toStrLit()
proc multiply(a, b: int): int
remotefn multiply