我如何在 nim 中创建不同过程的查找 table?
How can I create a lookup table of different procedures in nim?
在 python 中,函数是“第一个 class 公民”,可以作为参数传递给 functions/methods。
假设我想在 python 中开始编写一个基本的远程过程调用 (rpc) 库,我可能会首先创建一个将函数名称映射到实际函数对象的字典:
rpc = {} # string: function
def register_rpc(name, function):
""" Register a function as a RPC """
rpc[name] = function
def sum_nums(a, b):
""" Sum numbers """
return a + b
register_rpc("sum_nums", sum_nums) # register sum_nums as RPC
print(rpc["sum_nums"](10, 15))
我可以在 Nim 中接近这个。问题是我必须在查找 Table 中明确定义 proc
的参数和参数类型,并且这也必须与 register_rpc
过程的定义相匹配。这是我的半等价 Nim 代码:
import tables
var rpc = initTable[string, proc(a, b: int): int]() # explicitly defined procedure arguments/types
# Procedure arguments/types must match Table's proc definition or else I can't register a RPC
proc register_rpc(p: proc(a, b: int): int, n: string): bool =
#[ Register a function as a RPC ]#
rpc[n] = p
true
# proc definition matches Table/register_rpc
proc sum_nums(a, b: int): int =
#[ Sum numbers ]#
a + b
discard register_rpc(sum_nums, "sum_nums")
echo rpc["sum_nums"](10, 15)
有什么方法可以创建一个 register_rpc
过程,而不必显式定义 proc
参数及其类型?我怎样才能让我的 Table
也匹配这个?我昨天问了一个似乎半相关的问题:
但是我无法将 untyped
类型用于 Table
。
我是否必须重载 register_rpc
过程以涵盖所有不同的 type
场景?如何在不必显式定义 proc
arguments/types 的情况下创建查找 table?
nim 不支持这种动态处理方式。 procs 的类型必须在编译时知道,这是事实。虽然你当然可以做一些如此可怕和令人不快的事情,比如:
import tables
var table = initTable[string, pointer]()
table.add("add", cast[pointer](proc(a: int, b: int): int =
a + b
))
table.add("negate", cast[pointer](proc(a: int): int =
-a
))
assert cast[proc(a: int, b: int): int {.cdecl.}](table["add"])(10, 20) == 30
但老实说,如果你不小心施放了错误的过程,你就完蛋了。在这种情况下,我们需要安全性,一种区分指针的方法。这就是抽象的用武之地。
import macrocache, options
const counter = CacheCounter("counter")
func typeID(tp: type): int =
const id = counter.value
static: counter.inc
id
{.pragma: dynProc, cdecl, noSideEffect, gcsafe, locks:0.}
type
DynamicProc* = object
id: int
value: pointer
func initDynamicProc*[T](p: T): DynamicProc =
DynamicProc(id: T.typeID, value: cast[pointer](p))
template ID*(d: DynamicProc): int = d.id
func get*(d: DynamicProc, tp: type): Option[tp] =
if tp.typeID != d.id:
none(tp)
else:
some(cast[tp](d.value))
var safeTable = initTable[string, DynamicProc]()
safeTable.add("add", initDynamicProc(proc (a, b: int): int {.dynProc.} =
a + b
))
assert safeTable["add"].get(proc (a, b: int): int {.dynProc.}).get()(10, 20) == 30
assert safeTable["add"].get(proc (b: int) {.dynProc.}) == none(proc (b: int) {.dynProc.})
它仍然很冗长,但是这次你保证了它的安全。 dynProc
需要 pragma 来修复类型匹配,因为具有不同 pragma 的类型被认为是不同的类型。
在 python 中,函数是“第一个 class 公民”,可以作为参数传递给 functions/methods。
假设我想在 python 中开始编写一个基本的远程过程调用 (rpc) 库,我可能会首先创建一个将函数名称映射到实际函数对象的字典:
rpc = {} # string: function
def register_rpc(name, function):
""" Register a function as a RPC """
rpc[name] = function
def sum_nums(a, b):
""" Sum numbers """
return a + b
register_rpc("sum_nums", sum_nums) # register sum_nums as RPC
print(rpc["sum_nums"](10, 15))
我可以在 Nim 中接近这个。问题是我必须在查找 Table 中明确定义 proc
的参数和参数类型,并且这也必须与 register_rpc
过程的定义相匹配。这是我的半等价 Nim 代码:
import tables
var rpc = initTable[string, proc(a, b: int): int]() # explicitly defined procedure arguments/types
# Procedure arguments/types must match Table's proc definition or else I can't register a RPC
proc register_rpc(p: proc(a, b: int): int, n: string): bool =
#[ Register a function as a RPC ]#
rpc[n] = p
true
# proc definition matches Table/register_rpc
proc sum_nums(a, b: int): int =
#[ Sum numbers ]#
a + b
discard register_rpc(sum_nums, "sum_nums")
echo rpc["sum_nums"](10, 15)
有什么方法可以创建一个 register_rpc
过程,而不必显式定义 proc
参数及其类型?我怎样才能让我的 Table
也匹配这个?我昨天问了一个似乎半相关的问题:
但是我无法将 untyped
类型用于 Table
。
我是否必须重载 register_rpc
过程以涵盖所有不同的 type
场景?如何在不必显式定义 proc
arguments/types 的情况下创建查找 table?
nim 不支持这种动态处理方式。 procs 的类型必须在编译时知道,这是事实。虽然你当然可以做一些如此可怕和令人不快的事情,比如:
import tables
var table = initTable[string, pointer]()
table.add("add", cast[pointer](proc(a: int, b: int): int =
a + b
))
table.add("negate", cast[pointer](proc(a: int): int =
-a
))
assert cast[proc(a: int, b: int): int {.cdecl.}](table["add"])(10, 20) == 30
但老实说,如果你不小心施放了错误的过程,你就完蛋了。在这种情况下,我们需要安全性,一种区分指针的方法。这就是抽象的用武之地。
import macrocache, options
const counter = CacheCounter("counter")
func typeID(tp: type): int =
const id = counter.value
static: counter.inc
id
{.pragma: dynProc, cdecl, noSideEffect, gcsafe, locks:0.}
type
DynamicProc* = object
id: int
value: pointer
func initDynamicProc*[T](p: T): DynamicProc =
DynamicProc(id: T.typeID, value: cast[pointer](p))
template ID*(d: DynamicProc): int = d.id
func get*(d: DynamicProc, tp: type): Option[tp] =
if tp.typeID != d.id:
none(tp)
else:
some(cast[tp](d.value))
var safeTable = initTable[string, DynamicProc]()
safeTable.add("add", initDynamicProc(proc (a, b: int): int {.dynProc.} =
a + b
))
assert safeTable["add"].get(proc (a, b: int): int {.dynProc.}).get()(10, 20) == 30
assert safeTable["add"].get(proc (b: int) {.dynProc.}) == none(proc (b: int) {.dynProc.})
它仍然很冗长,但是这次你保证了它的安全。 dynProc
需要 pragma 来修复类型匹配,因为具有不同 pragma 的类型被认为是不同的类型。