How can I get the name of procedure in Nim?
Asked Answered
I

2

7

I am trying to write a macro for debug print in the Nim language. Currently this macro adds filename andline to the output by instantiationInfo().

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()

currently output:

debug_print.nim:9: foo bar

I would like to add the name of the procedure (or iterator) of the place where the macro was called.

desired output:

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

How can I get the name of procedure (or iterator) in Nim, like __func__ in C?

Incoming answered 8/2, 2018 at 13:28 Comment(0)
L
7

At runtime you can do getFrame().procname, but it only works with stacktrace enabled (not in release builds).

At compile-time surprisingly I can't find a way to do it. There is callsite() in macros module, but it doesn't go far enough. It sounds like something that might fit into the macros.LineInfo object.

A hacky solution would be to also use __func__ and parse that back into the Nim proc name:

template procName: string =
  var name: cstring
  {.emit: "`name` = __func__;".}
  ($name).rsplit('_', 1)[0]
Limicoline answered 8/2, 2018 at 13:55 Comment(1)
some adjustment is needed, eg: for proc bar_baz() I got: bar_baz_9c8JPzPvtM9azO6OB23bjc3Q_3 (NOTE the trailing _3 when multiple functions are are in a module, and the leading _ if function name contains underscore)Triable
T
2

building on answer from @def- but making it more robust to handle edge cases of functions containing underscores, and hashes containing trailing _N or not also using more unique names as otherwise macro would fail if proc defines a variable 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()

NOTE: this still has some issues that trigger in complex edge cases, see https://github.com/nim-lang/Nim/issues/8212

Triable answered 5/7, 2018 at 19:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.