I have a C signature of a function defined as a type (callback):
typedef void (*TraceLogCallback)(int logType, const char *text, va_list args);
and now I want to implement this callback in Nim:
import times, strutils, strformat
type
va_list* {.importc: "va_list", header: "<stdarg.h>".} = object
TraceLogCallback* = proc (logType: int32; text: string; args: va_list) {.cdecl.}
proc logCustom*(msgType: int32; text: string; args: va_list) {.cdecl.} =
var msg: string
let timeStr = now().format("yyyy-MM-dd hh:mm:ss")
msg.add fmt"[{timeStr}] "
case msgType
of 1: msg.add "[INFO] : "
of 2: msg.add "[ERROR]: "
of 3: msg.add "[WARN] : "
of 4: msg.add "[DEBUG]: "
else: discard
writeLine(stdout, msg)
# How to interface with va_list args?
# vprintf(text, args);
flushFile(stdout)
assert logCustom is TraceLogCallback
echo "Success"
This callback is then passed into a C function, Nim signature:
proc setTraceLogCallback*(callback: TraceLogCallback) {.cdecl, importc: ...}
How would I print args? I know there's {.varargs.} pragma but it doesn't seem to allow any interaction
text: string? a char* is a cstring!
As for your question, you need to wrap va_arg and friends, see for example https://docs.microsoft.com/de-de/cpp/c-runtime-library/reference/va-arg-va-copy-va-end-va-start?view=msvc-160
Thanks for cstring, completely slipped my mind!
And thanks for the hint about trying to wrap C functions, this worked!
As I understand it, wrapping va_arg and friends would require the function definition in C to be with 3 dots something like
typedef void (*TraceLogCallback)(int logType, const char *text, ...);
so that later in Nim we can use va_arg functions with {.varargs.} pragma
proc logCustom*(msgType: int32; text: string;) {.varargs, cdecl.} = ...
In my case wrapping vprintf seems simpler and maybe the only way because C function requires an explicit va_list argument. Implementation for reference:
type va_list* {.importc: "va_list", header: "<stdarg.h>".} = object
proc vprintf(format: cstring, args: va_list) {.cdecl, importc, header: "stdio.h"}
proc logCustom*(msgType: int32; text: cstring; args: va_list) {.cdecl.} =
var msg: string
let timeStr = now().format("yyyy-MM-dd hh:mm:ss")
msg.add fmt"[{timeStr}] "
case msgType
of 1: msg.add "[INFO] : "
of 2: msg.add "[ERROR]: "
of 3: msg.add "[WARN] : "
of 4: msg.add "[DEBUG]: "
else: discard
msg.add text
msg.add "\n"
vprintf(msg.cstring, args)