I want to run the following code but get an error cannot evaluate at compile time: NI_MAXHOST.
Is there any workaround?
var
NI_MAXHOST* {.
importc,
header: "<netdb.h>",
.}: cint
var
cstring_buf: array[0 .. NI_MAX_HOST.int, char]
...
Nim's array requires a const (compile-time) length value, and a var's value is only known at runtime. Use a seq instead (Nim's resizable array type). Also, char in Nim is a unicode character, not a byte, so naming your var cstring_buf might be confusing. You should probably use cchar (C char) instead to interface with C or name your var string_buf if you wanted unicode.
var cstring_buf = newSeq[cchar](NI_MAXHOST) # returns a `seq[cchar]`
@filwit: Yes. var is runtime-value. But, I would like to clarify it. There is no way to use importc'ed "#define" symbol as if const?
About Unicode, I will check it. But cchar would be more clear.
The type char represents an 8-bit character. For Unicode characters, use type Rune from the unicode module.
To access C constants at compile time, you need an intermediate code generation step. The easiest is to write a separate program, such as:
import macros
macro emit_const(name: untyped, tp: untyped, hdr: string): stmt =
parseStmt(
"var " & $name & " {.importc, header: \"" & $hdr & "\".}: " & $tp & "\n" &
"echo \"const " & $name & "*: " & $tp & " = \"," & $name & "\n"
)
emit_const NI_MAXHOST, cint, "<netdb.h>"
and run it with nim c -r --verbosity:0 --hints:off genconst.nim >osconsts.nim (or whatever file names you prefer). You can also try and preprocess netdb.h with c2nim, but that may not be portable (depending on whether c2nim can actually make sense of netdb.h on any given OS installation). Note that either approach means that you'll probably want to use some build tool, such as make, to build your program.
(The above code uses parseStmt() as a workaround because quasiquotes do not seem to work properly in pragmas.)
It just isn't possible otherwise. The Nim compiler would have to be able to completely parse include files.
You can also use staticExec to avoid using a separate build tool, but that adds overhead each time you compile.
@Araq That would be awesome!
Edit: Though I'm not sure how you'll add the necessary dependency management to staticExec().
Like so:
proc staticExec*(command: string, input = "", cache = ""): string {.
magic: "StaticExec".} = discard
## Executes an external process at compile-time.
## if `input` is not an empty string, it will be passed as a standard input
## to the executed program.
##
## .. code-block:: nim
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## "\nCompiled on " & staticExec("uname -v")
##
## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
## this proc inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL
## <nimc.html#passl-pragma>`_.
##
## If ``cache`` is not empty, the results of ``staticExec`` are cached within
## the ``nimcache`` directory. Use ``--forceBuild`` to get rid of this caching
## behaviour then. ``command & input & cache`` (the concatenated string) is
## used to determine wether the entry in the cache is still valid. You can
## use versioning information for ``cache``:
##
## .. code-block:: nim
## const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0")