proc fn():int {.importc:"vfn(@)", header:vfn.h, varargs.}
macro wrapper(args:varargs[expr]):stmt =
result = newNimNode(nnkStmtList)
var call = newNimNode(nnkCall)
call.add(newIdentNode("fn"))
# this macro chooses which fn to call
# but this code is simplified for example
for arg in args.children:
echo arg.kind
if (arg.kind == nnkIntLit or arg.kind == nnkStrLit):
call.add(arg)
else:
call.add(newIdentNode($arg.symbol))
result.add(call)
var result:int = wrapper(23, "astring")
Error: value of type 'int' has to be discarded
# pointing to the line: var result:int = wrapper(23, "astring")
My macro works well for procs without return types, but I suspect I'm mucking up the AST when it comes to assignment.
Bonus points if you can solve my original problem without macros. The C interface I'm wrapping has a function for 1 argument, and another for N arguments. I want to unify the API on the nim side.
Solving this with plain old overloading is trivial:
proc fn(): cint {.importc: "vfn", header: "some.h", varargs.}
proc fn(a: cint): cint {.importc: "fn1cint", header: "some.h", varargs.}
proc fn(a, b: cstring): cint {.import: "fn2cstring", header: "some.h", varargs.}
Thanks for the reply. I'm unsure how to use bindSym on anything other than string constants. Is this what nnkBind is for?
Unfortunately, the problem is that the c "function" I'm wrapping can take N arguments of any of strings, floats, or ints, which is why I'm building the function call using a macro using varargs[epxr], and why overloading won't work. I've attempted to make the changes you outlined, but still get the same error.
proc fn():int {.importcpp:"vfn(@)", header:vfn.h, varargs.}
macro wrapper(args:varargs[expr]):stmt =
result = newCall(bindSym"SomeFn")
for arg in args.children:
echo arg.kind
if (arg.kind == nnkIntLit or arg.kind == nnkStrLit):
result.add(arg)
else:
result.add(newIdentNode($arg.symbol))
var result:int = wrapper(23, "astring")
For your example it would be bindSym"fn". But I don't get your problem, you only have to use varargs as a pragma:
proc fn():int {.importc: "vfn", header:"vfn.h", varargs.}
# compiles:
discard fn("foo", "bar", 585'f32, 34.cint)
proc fn(required_argument:cstring):int {.importcpp: "vfn_fast(#)", header:"vfn.h".}
proc fn():int {.importcpp: "vfn", header:"vfn.h", varargs.}
Error: Attempt to read from nil?
or this?
proc fn(required_argument:cstring):int {.importcpp: "vfn_fast(#)", header:"vfn.h".}
proc fn(required_argument:cstring):int {.importcpp: "vfn(@)", header:"vfn.h", varargs.}
Error: Pragmas are only allowed in the header of a proc
You cannot importcpp a nullary function without a pattern. This should work:
proc fn(required_argument:cstring):int {.importc: "vfn_fast", header:"vfn.h".}
proc fn():int {.importc: "vfn", header:"vfn.h", varargs.}
proc fn():int {.importc: "vfn", header:"vfn.h", varargs.}
and any variations I can think of, always yield the error "Attempt to read from nil?"
I think the issue is because both versions of this function I'm wrapping require at least one parameter, but I'm unsure how to enforce this without generating an error about ambiguous proc definitions.
Thanks for all your responses; I've learned a few things today.
Well in this case a macro works:
proc fn1(arg: cstring): int {.importc: "fn_fast", header: "vfn.h", cdecl.}
proc fnV(arg: cstring): int {.importc: "vfn", header: "vfn.h", varargs, cdecl.}
macro fn(args: varargs[untyped]): untyped =
if args.len == 1:
result = newCall(bindSym"fn1")
else:
result = newCall(bindSym"fnV")
for arg in args.children:
result.add(arg)
var result: int = fn(23, "astring")