Is there a clean way to interface with some C headers that use some C pre-processor concatenation tricks?
Here's a snippet of how my target C API works:
#define FLASH_AREA_IMAGE_PRIMARY FLASH_AREA_ID(image_0)
Where image_0 is the "label" the developer wants and FLASH_AREA_ID is a C macro that concatenates the label with some prefix. I'm at a loss on how to emulate this in Nim. I've tried a template but it seems it'll require a macro to do generically at compile time. Tbh, I don't know Nim macros well enough to solve it that way and I've been putting off learning them in Nim. Should I just dive in? Or is there an easier way with importcpp?
In the meantime I'm just doing:
{.emit: """
#include <storage/flash_map.h>
#define ImagePrimary FLASH_AREA_ID(image_0)
#define ImageSecondary FLASH_AREA_ID(image_1)
""".}
var ImagePrimary* {.importc: "$1", nodecl.}: uint8
var ImageSecondary* {.importc: "$1", nodecl.}: uint8
But different system configurations won't work with that method (the labels won't exist and it'll have compile time errors).
Maybe there is a cleaner solution, though considering you are interfacing with C API that uses preprocessor hacks ...
Yah, it's a bit of a pain, but overall they've tried to make the C preprocessor hacks consistent at least. Alright time to dive into Nim macros. I was gonna ask how to create a {.emit: ... but magically its right there in the macros module. :-)
What about
template flashAreaID(x:untyped):untyped = `flashAreaId x`
const
flashAreaIdImg0 = 1
flashAreaIdImg1 = 2
flashAreaIdScratch = 3
const imagePrimary = flashAreaID(Img0)
?@shirleyquirk unfortunately it needs to tweak the importc pragma so I had to use a macro.
Not too bad... but took me forever to figure out how to get the typename. Even then I'm not sure it's the best way to do it, but everything else blows up on me.
Here's what I came up with:
macro CDefineInvoke*(defineName: untyped, name: varargs[untyped]) =
let macroInvokeName = newLit( defineName.repr & "(" & name.repr & ");")
result = quote do:
{.emit: `macroInvokeName`.}
macro CDefineDeclareCall*(name: untyped, macroInvocation: untyped, retType: typedesc) =
let macroInvokeName = newLit( macroInvocation.repr )
let rt = parseExpr(retType.repr)
result = quote do:
var `name` {.importc: `macroInvokeName`, nodecl.}: `rt`
macro CDefineDeclareVar*(name: untyped, macroRepr: untyped, retType: typedesc) =
let macroInvokeName = newLit( macroRepr.repr )
let rt = parseExpr(retType.repr)
result = quote do:
var myMacroVar {.importc: `macroInvokeName`, nodecl.}: `rt`
var `name`* = myMacroVar
Usage like:
{.emit: """
#define Img0 1133
#define Img1 2266
#define DEVICE_DECLARE(a, b) int AAA = (a); int BBB = (b);
#define DEVICE_NAME_ID(n) n
#define DEVICE_NAME_GET(n) n
""".}
CDefineInvoke(DEVICE_DECLARE, Img0, Img1)
CDefineDeclareCall(device_img_void, DEVICE_NAME_SET(Img0, null)): int
CDefineDeclareCall(device_img0, DEVICE_NAME_GET(Img0)): int
CDefineDeclareVar(device_img1, DEVICE_NAME_ID(Img1)): int
echo "device_img0: ", $device_img0
echo "device_img1: ", $(device_img1)
echo "device_img2: ", $(device_img2)
assert device_img0 == 1133
assert device_img1 == 2266
assert device_img2 == 2
It appears to be working. :fingers-crossed: