I want to define a var in c with "char x[] = {'h', 'e', 'l', 'l', 'o', 0};" to make the varaible allocate on stack, and doesnt refrence any global variable. (The code will be remote execute with CreateRemoteThread).
So I wrote a macro and using importc to import the varaible, the code is:
import strutils, sequtils, macros
macro make_c_char_array*(name: untyped, s: static[string]): untyped =
var ca = s.mapIt(it.int)
ca.add 0
let cs = "char $#[] = {$#};" % [$name, ca.join(", ")]
nnkStmtList.newTree(
nnkPragma.newTree(
nnkExprColonExpr.newTree(
newIdentNode("emit"),
newLit(cs)
)
)
)
proc main() =
make_c_char_array(x, "hello")
let x {.importc, nodecl.}: cstring
echo x
when isMainModule:
main()
But it doesn't compile, the c compiler say the variable x redefinition, here is the generated c code:
N_LIB_PRIVATE N_NIMCALL(void, main__a5352_248)(void) {
NCSTRING x;
tyArray__nHXaesL0DJZHyVS07ARPRA T1_;
char x[] = {104, 101, 108, 108, 111, 0};
x = (NCSTRING)0;
nimZeroMem((void*)T1_, sizeof(tyArray__nHXaesL0DJZHyVS07ARPRA));
T1_[0] = cstrToNimstr(x);
echoBinSafe(T1_, 1);
}
I hope the nodecl pragma should remove the line NCSTRING x; and x = (NCSTRING)0;. Is there something wrong with my code, or how can I do?
Why not use var x: array[6, char] = ['h', 'e', 'l', 'l', 'o', '\0'], Nim does allocate arrays on the stack just like C.
To answer your question, it's a quirk that .nodecl doesn't work for local variables iirc. So you need to use:
let x {.importc, nodecl.}: cstring
proc main() =
make_c_char_array(x, "hello")
echo x
But as I said, all of this is completely unnecessary to begin with.
The code var x: array[6, char] = ['h', 'e', 'l', 'l', 'o', '\0'] doesn't work either. Here is the generated c code:
static NIM_CONST tyArray__9bPFPkkEEeeNM9bKgiV8Q49cg TM__7Wo7NjlPCKzJJsFsMhIJow_2 = {104, 101, 108, 108, 111, 0} ;
......
N_LIB_PRIVATE N_NIMCALL(void, main__a5353_4)(void) {
tyArray__9bPFPkkEEeeNM9bKgiV8Q49cg x;
nimZeroMem((void*)x, sizeof(tyArray__9bPFPkkEEeeNM9bKgiV8Q49cg));
nimCopyMem((void*)x, (NIM_CONST void*)TM__7Wo7NjlPCKzJJsFsMhIJow_2, sizeof(tyArray__9bPFPkkEEeeNM9bKgiV8Q49cg));
printf(((NCSTRING) (x)));
}
I finnally using the following code:
var x {.noinit}: array[6, char]
x[0] = 'h'
x[1] = 'l'
...
x[5] = '\0'
And using a macro to generate the code:
macro make_nim_char_array*(name: untyped, s: static[string]) =
var s = s
s.add '\0'
var stmts =
nnkStmtList.newTree(
nnkVarSection.newTree(
nnkIdentDefs.newTree(
nnkPragmaExpr.newTree(
name,
nnkPragma.newTree(
newIdentNode("noinit")
)
),
nnkBracketExpr.newTree(
newIdentNode("array"),
newLit(s.len),
newIdentNode("char")
),
newEmptyNode()
)
)
)
for i in 0..<s.len:
let v = nnkAsgn.newTree(
nnkBracketExpr.newTree(
name,
newLit(i)
),
newLit(s[i])
)
stmts.add v
stmts
The code is work fine when the c compiler doesn't optimize to using movdqu or vmovdqu.
I hope I can using``#pragma optimize("", off)`` to disable compiler optimization at function level, but it doesn't work, becasue the nim compiler doesn't generate these directive around the generated c function.
Great! using pragma global works.
The assembler code is same for both make_nim_char_array and make_c_char_array with msvc compiler flag /O2.