Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get the name of procedure in Nim?

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?

like image 907
mjy Avatar asked Feb 08 '18 13:02

mjy


People also ask

How do you use procedures in Nim?

For the most part, Nim uses what it calls procedures, using the proc keyword. It seems to be one of an elite few languages to use this keyword, while others might use function or func or method or def or even sub.

How do you call a proc function in Nim?

Procs Procedures in Nim are declared using procand require that their parameter and return types be annotated. After the types and parameters, an =is used to denote the start of the function body. Another thing to note is that procedures have uniform function call syntax, which means that they can called as both foo(a, b)or a.foo(b).

How do I retrieve the length of a string in Nim?

A string's length can be retrieved with the builtin len procedure; the length never counts the terminating zero. Accessing the terminating zero is an error, it only exists so that a Nim string can be converted to a cstring without doing a copy. The assignment operator for strings copies the string.

What happens if a reference points to nothing in Nim?

If a reference points to nothing, it has the value nil. A procedural type is a (somewhat abstract) pointer to a procedure. nil is an allowed value for a variable of a procedural type. Nim uses procedural types to achieve functional programming techniques. proc greet(name: string): string = "Hello, " & name & "!"


2 Answers

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]
like image 104
def- Avatar answered Oct 14 '22 05:10

def-


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

like image 35
timotheecour Avatar answered Oct 14 '22 03:10

timotheecour