Faux closures: a facility for creating closure-like non-closure procs
This code is extracted from the error-handling proposal where it's used to implement `Try` blocks (Faux closures are created there in order to assign raises lists to them). The desktop team faced a similar problem where the `spawn` API doesn't support closures, but the restriction can be easily worked-around with faux closures.
This commit is contained in:
parent
61d5cfc376
commit
32b86bfd1f
|
@ -0,0 +1,70 @@
|
|||
import
|
||||
tables, macros
|
||||
|
||||
macro fauxClosureAux(procName: untyped, procDef: typed): untyped =
|
||||
type ParamRegistryEntry = object
|
||||
origSymbol: NimNode
|
||||
newParam: NimNode
|
||||
|
||||
var params = initTable[string, ParamRegistryEntry]()
|
||||
|
||||
proc makeParam(replacedSym: NimNode,
|
||||
registryEntry: var ParamRegistryEntry): NimNode =
|
||||
if registryEntry.newParam == nil:
|
||||
registryEntry.origSymbol = replacedSym
|
||||
registryEntry.newParam = genSym(nskParam, $replacedSym)
|
||||
return registryEntry.newParam
|
||||
|
||||
let realClosureSym = procDef.name
|
||||
|
||||
proc registerParamsInBody(n: NimNode) =
|
||||
for i in 0 ..< n.len:
|
||||
let son = n[i]
|
||||
case son.kind
|
||||
of nnkIdent, nnkCharLit..nnkTripleStrLit:
|
||||
discard
|
||||
of nnkSym:
|
||||
if son.symKind in {nskLet, nskVar, nskForVar,
|
||||
nskParam, nskTemp, nskResult}:
|
||||
if owner(son) != realClosureSym:
|
||||
n[i] = makeParam(son, params.mgetOrPut(son.signatureHash,
|
||||
default(ParamRegistryEntry)))
|
||||
of nnkHiddenDeref:
|
||||
registerParamsInBody son
|
||||
n[i] = son[0]
|
||||
else:
|
||||
registerParamsInBody son
|
||||
|
||||
let
|
||||
fauxClosureName = genSym(nskProc, $procName)
|
||||
fauxClosureBody = copy procDef.body
|
||||
|
||||
registerParamsInBody fauxClosureBody
|
||||
|
||||
var
|
||||
procCall = newCall(fauxClosureName)
|
||||
fauxClosureParams = @[newEmptyNode()]
|
||||
|
||||
for hash, param in params:
|
||||
var paramType = getType(param.origSymbol)
|
||||
if param.origSymbol.symKind in {nskVar, nskResult}:
|
||||
if paramType.kind != nnkBracketExpr or not eqIdent(paramType[0], "var"):
|
||||
paramType = newTree(nnkVarTy, paramType)
|
||||
|
||||
fauxClosureParams.add newIdentDefs(param.newParam, paramType)
|
||||
procCall.add param.origSymbol
|
||||
|
||||
result = newStmtList(
|
||||
newProc(name = fauxClosureName,
|
||||
params = fauxClosureParams,
|
||||
body = fauxClosureBody),
|
||||
|
||||
newProc(name = procName,
|
||||
procType = nnkTemplateDef,
|
||||
body = procCall))
|
||||
|
||||
macro fauxClosure*(procDef: untyped): untyped =
|
||||
let name = procDef.name
|
||||
procDef.name = genSym(nskProc, $name)
|
||||
result = newCall(bindSym"fauxClosureAux", name, procDef)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import
|
||||
os, threadpool,
|
||||
../stew/faux_closures
|
||||
|
||||
template sendSignal(data: string) =
|
||||
echo data
|
||||
|
||||
template spawnAndSend(exprToSend: untyped) =
|
||||
proc payload {.fauxClosure.} =
|
||||
let data = exprToSend
|
||||
sendSignal data
|
||||
|
||||
spawn payload()
|
||||
|
||||
proc main(x: string) =
|
||||
spawnAndSend:
|
||||
var i = 10
|
||||
x & $(23 + i)
|
||||
|
||||
main("tests")
|
||||
sleep(2)
|
||||
|
Loading…
Reference in New Issue