如何在Nim中获取程序名称?

时间:2018-02-08 13:28:20

标签: macros nim

我正在尝试用Nim语言编写一个用于调试打印的宏。 目前,此宏通过filenamelineinstantiationInfo()添加到输出中。

import macros  
macro debugPrint(msg: untyped): typed =
  result = quote do:
    let pos = instantiationInfo()
    echo pos.filename, ":", pos.line, ": ", `msg`

proc hello() =
  debugPrint "foo bar"
hello()

目前输出:

debug_print.nim:9: foo bar

我想添加调用宏的地方的过程(或迭代器)的名称。

期望的输出:

debug_print.nim:9(proc hello): foo bar

如何在Nim中获取过程(或迭代器)的名称,如C中的__func__

2 个答案:

答案 0 :(得分:6)

在运行时,您可以执行getFrame().procname,但它仅适用于启用了堆栈跟踪(不适用于发布版本)。

在编译时令人惊讶的是,我找不到办法。宏模块中有callsite(),但它还远远不够。这听起来像是适合macros.LineInfo对象的东西。

一个hacky解决方案是使用__func__并将其解析回Nim proc名称:

template procName: string =
  var name: cstring
  {.emit: "`name` = __func__;".}
  ($name).rsplit('_', 1)[0]

答案 1 :(得分:1)

以@ def-的答案为基础,但使其更健壮地处理包含下划线的函数的极端情况以及包含尾随_N的哈希 还使用更多唯一的名称,否则如果proc定义了变量name

,宏将失败
import strutils

proc procNameAux*(name:cstring): string =
  let temp=($name).rsplit('_', 2)
    #CHECKME: IMPROVE; the magic '4' chosen to be enough for most cases
    # EG: bar_baz_9c8JPzPvtM9azO6OB23bjc3Q_3
  if temp.len>=3 and temp[2].len < 4:
    ($name).rsplit('_', 2)[0]
  else:
    # EG: foo_9c8JPzPvtM9azO6OB23bjc3Q
    ($name).rsplit('_', 1)[0]

template procName*: string =
  var name2: cstring
  {.emit: "`name2` = __func__;".}
  procNameAux(name2)

proc foo_bar()=
  echo procName # prints foo_bar

foo_bar()

注意:在复杂的情况下,这仍然会触发一些问题,请参见https://github.com/nim-lang/Nim/issues/8212