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?
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.
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).
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.
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 & "!"
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]
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With