Nim language has a great FFI with C/C++ which makes it easy to use C/C++ libraries. This article introduces
how to wrap const char*
in C/C++ backend.
In the Nim world, we often play with string
which handles the dirty works for us such as resizing the buffer, creating/deleting the memory which can satisfy the most of our needs. However, if we want to interact with other languages, we need the power of cstring
. Note that cstring
doesn't mean "string in C", it means compatible string(char *
in C/C++ backend, JS string in JavaScript backend and so on).
Nim doesn't provide const char *
for us. But it is easy to make our own one.
You need to import const char*
types from C/C++ backend using importc
pragmas. Then make a distinct type for it.
type
cstringConstImpl {.importc:"const char*".} = cstring
constChar* = distinct cstringConstImpl
Now, let's look at a simple example using it.
First emit
the c function, then write the function declaration(nodecl
pragmas means that we
do not generate the function declaration in the output codes). Finally we construct a constChar
object and
pass it to the fn
proc.
{.emit: """
int fn(const char* a, char b) {
return 1;
}
"""
.}
proc fn(a: constChar, b: char): int {.importc: "fn", nodecl.}
var x = constChar("abc")
doAssert fn(x, 'c') == 1
doAssert not compiles(fn("abc", 'b'))
doAssert not compiles(fn("abc".cstring, 'b'))
We could see that the fn
proc doesn't accept string
and cstring
types and only the constChar
type is allowed. After looking at the generated C codes using nim c -r --nimcache:nimcache app.nim
or nim cpp -r --nimcache:nimcache app.nim
, it does generate const char* x = "abc";
.
Lastly let's look at an example with C++ backend, it has the same story as above.
Now we need to create a new .h
file and overload the fn
functions.
fun.h
int fn(const char* a, char b) {
return 1;
}
int fn(char* a, char b) {
return 2;
}
fun.nim
proc fn(a: constChar, b: char): int {.importcpp: "$1(@)", header: "fun.h".} = discard
proc fn(a: cstring, b: char): int {.importcpp: "$1(@)", header: "fun.h".} = discard
var a = constChar("abs")
doAssert fn(a, 'b') == 1
var b = cstring("abc")
doAssert fn(b, 'b') == 2
Run the codes above using nim cpp -r fun.nim
and it works!
Reference:
https://github.com/nim-lang/Nim/issues/3720
Nim is a great language. Let's port and wrap more libraries for it.
Top comments (2)
Hello, thanks for sharing this! I was led to this post from this recent question post on const char by me on Nim Forums.
As a follow up, I am some basic questions ..
constChar
type main stream so that every C wrapper library does not need to define this. Those libraries would also need to export this type and then we will end up with type clashes if a user imports multiple of such libraries.$
,[]
and may be more low level ops.im still a noob to nim just started learning this week but i just wanna say .. you are a really amazing person and i hope you keep up with this content! thanks hopefully one day nim becomes as popular as it should be