Open sls1005 opened 2 years ago
works for C backend
type
cstringConstImpl {.importc:"const char*".} = cstring
constChar* = distinct cstringConstImpl
{.emit: """
const char* foo() {
return "hello";
}
""".}
proc foo(): constChar {.importc.} # change to importcpp for C++ backend
echo foo().cstring
ref https://dev.to/xflywind/wrap-const-char-in-the-nim-language-53no
Maybe that @xflywind examples can be in the official documentation somewhere (?) 🤔
I think cstring
is compatible with const char *
on Nim 1.6.2.
Nim info:
Nim Compiler Version 1.6.2 [Linux: amd64]
Compiled at 2023-01-11
Copyright (c) 2006-2021 by Andreas Rumpf
I'm doing a binding of libtlsh-dev
. A part of the binding is:
{.pragma: impTlsh, header: "tlsh.h".}
{.passL: "-ltlsh".}
type
Tlsh {.importcpp: "Tlsh".} = object
# A "hack" to create "new" binding
# https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-importcpp-pragma
proc tlsh_new*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.}
proc TlshObj*(): Tlsh {.importcpp: "Tlsh(@)".}
proc tlsh_free*(tlsh: ptr Tlsh) {.importcpp: "#.~Tlsh()".}
# Binding method
proc version*(tlsh: ptr Tlsh): cstring {.importcpp, impTlsh.}
# Testing code
var
vtlsh = tlsh_new TlshObj()
echo vtlsh.version()
When I compiled with cpp
and run, I got output:
3.4.4 compact hash 1 byte checksum
So I assume it's okay
Meanwhile I'm having hard time trying to pass const char *
to input.
I have a method const char* getHash(char *buffer, unsigned int bufSize) const;
, which I generated the binding proc getHash*(tlsh: ptr Tlsh, buffer: cstring, size: uint): cstring {.importcpp, impTlsh.}
. I compiled the code and I didn't see any invalid conversion
but the program crashed.
(I deleted previous comment because I was wrong doing the code)
The cstring
when compile nim cpp
is compatible with const char *
The test of @sls1005 worked on my side
Also my test with Cpp binding worked fine. The code
var
t1: Tlsh
str1: cstring = "Test string hello world"
t1.final(str1, 512)
echo t1.getHash()
echo t1.version()
-> The method that uses const char*
is perfectly fine. (in previous test, i used the wrong buffer size so the data was empty)
Really? I'm still getting
error: cannot initialize a variable of type 'NCSTRING' (aka 'char *') with an rvalue of type 'const char *'
NCSTRING T2_ = foo();
^ ~~~~~
1 error generated.
with Nim 1.6.14.
Maybe my backend C++ compiler is more strict.
Getting the same error and @ringabout's example fails with cpp backend when inside the Raspberry Pi Pico SDK, but works when running nim cpp -r example.nim
. Probably because Nim calls the compiler here, while using the SDK calls g++ from CMake. I had to modify the example to add nodecl
pragma to proc foo
.
Noticed it when wrapping JPEGDEC and trying out the cpp backend.
Output from just throwing the snippet into a random file I was working on:
/usr/bin/arm-none-eabi-g++ -DCFG_TUSB_MCU=OPT_MCU_RP2040 -DCFG_TUSB_OS=OPT_OS_PICO -DCYW43_LWIP=1 -DLIB_PICO_ASYNC_CONTEXT_THREADSAFE_BACKGROUND=1 -DLIB_PICO_BIT_OPS=1 -DLIB_PICO_BIT_OPS_PICO=1 -DLIB_PICO_CYW43_ARCH=1 -DLIB_PICO_DIVIDER=1 -DLIB_PICO_DIVIDER_HARDWARE=1 -DLIB_PICO_DOUBLE=1 -DLIB_PICO_DOUBLE_PICO=1 -DLIB_PICO_FIX_RP2040_USB_DEVICE_ENUMERATION=1 -DLIB_PICO_FLOAT=1 -DLIB_PICO_FLOAT_PICO=1 -DLIB_PICO_INT64_OPS=1 -DLIB_PICO_INT64_OPS_PICO=1 -DLIB_PICO_MALLOC=1 -DLIB_PICO_MEM_OPS=1 -DLIB_PICO_MEM_OPS_PICO=1 -DLIB_PICO_PLATFORM=1 -DLIB_PICO_PRINTF=1 -DLIB_PICO_PRINTF_PICO=1 -DLIB_PICO_RAND=1 -DLIB_PICO_RUNTIME=1 -DLIB_PICO_STANDARD_LINK=1 -DLIB_PICO_STDIO=1 -DLIB_PICO_STDIO_USB=1 -DLIB_PICO_STDLIB=1 -DLIB_PICO_SYNC=1 -DLIB_PICO_SYNC_CRITICAL_SECTION=1 -DLIB_PICO_SYNC_MUTEX=1 -DLIB_PICO_SYNC_SEM=1 -DLIB_PICO_TIME=1 -DLIB_PICO_UNIQUE_ID=1 -DLIB_PICO_UTIL=1 -DMBEDTLS_CONFIG_FILE=\"mbedtls_config.h\" -DPICO_BOARD=\"pico_w\" -DPICO_BUILD=1 -DPICO_CMAKE_BUILD_TYPE=\"Release\" -DPICO_COPY_TO_RAM=0 -DPICO_CXX_ENABLE_EXCEPTIONS=1 -DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 -DPICO_NO_FLASH=0 -DPICO_NO_HARDWARE=0 -DPICO_ON_DEVICE=1 -DPICO_RP2040_USB_DEVICE_UFRAME_FIX=1 -DPICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS=0 -DPICO_TARGET_NAME=\"inky_frame_slideshow\" -DPICO_USE_BLOCKED_RAM=0 -I/home/djazz/.choosenim/toolchains/nim-2.0.0/lib -I/home/djazz/code/nim/nim-pimoroni-pico/examples -I/home/djazz/code/nim/nim-pimoroni-pico/examples/../csource -I/usr/share/pico-sdk/src/rp2_common/hardware_claim/include -I/usr/share/pico-sdk/src/common/pico_base/include -I/home/djazz/code/nim/nim-pimoroni-pico/build/examples/generated/pico_base -I/usr/share/pico-sdk/src/boards/include -I/usr/share/pico-sdk/src/rp2_common/pico_platform/include -I/usr/share/pico-sdk/src/rp2040/hardware_regs/include -I/usr/share/pico-sdk/src/rp2_common/hardware_base/include -I/usr/share/pico-sdk/src/rp2040/hardware_structs/include -I/usr/share/pico-sdk/src/rp2_common/hardware_sync/include -I/usr/share/pico-sdk/src/rp2_common/hardware_timer/include -I/usr/share/pico-sdk/src/rp2_common/hardware_irq/include -I/usr/share/pico-sdk/src/common/pico_sync/include -I/usr/share/pico-sdk/src/common/pico_time/include -I/usr/share/pico-sdk/src/common/pico_util/include -I/usr/share/pico-sdk/src/rp2_common/pico_rand/include -I/usr/share/pico-sdk/src/rp2_common/pico_unique_id/include -I/usr/share/pico-sdk/src/rp2_common/hardware_flash/include -I/usr/share/pico-sdk/src/rp2_common/pico_bootrom/include -I/usr/share/pico-sdk/src/rp2_common/hardware_clocks/include -I/usr/share/pico-sdk/src/rp2_common/hardware_gpio/include -I/usr/share/pico-sdk/src/rp2_common/hardware_resets/include -I/usr/share/pico-sdk/src/rp2_common/hardware_pll/include -I/usr/share/pico-sdk/src/rp2_common/hardware_vreg/include -I/usr/share/pico-sdk/src/rp2_common/hardware_watchdog/include -I/usr/share/pico-sdk/src/rp2_common/hardware_xosc/include -I/usr/share/pico-sdk/src/common/pico_stdlib/include -I/usr/share/pico-sdk/src/rp2_common/hardware_uart/include -I/usr/share/pico-sdk/src/rp2_common/hardware_divider/include -I/usr/share/pico-sdk/src/rp2_common/pico_runtime/include -I/usr/share/pico-sdk/src/rp2_common/pico_printf/include -I/usr/share/pico-sdk/src/common/pico_bit_ops/include -I/usr/share/pico-sdk/src/common/pico_divider/include -I/usr/share/pico-sdk/src/rp2_common/pico_double/include -I/usr/share/pico-sdk/src/rp2_common/pico_float/include -I/usr/share/pico-sdk/src/rp2_common/pico_malloc/include -I/usr/share/pico-sdk/src/common/pico_binary_info/include -I/usr/share/pico-sdk/src/rp2_common/pico_stdio/include -I/usr/share/pico-sdk/src/rp2_common/pico_stdio_usb/include -I/usr/share/pico-sdk/src/common/pico_usb_reset_interface/include -I/usr/share/pico-sdk/src/rp2_common/pico_int64_ops/include -I/usr/share/pico-sdk/src/rp2_common/pico_mem_ops/include -I/usr/share/pico-sdk/src/rp2_common/boot_stage2/include -I/usr/share/pico-sdk/lib/tinyusb/src -I/usr/share/pico-sdk/lib/tinyusb/src/common -I/usr/share/pico-sdk/lib/tinyusb/hw -I/usr/share/pico-sdk/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include -I/usr/share/pico-sdk/lib/lwip/src/include -I/usr/share/pico-sdk/src/rp2_common/hardware_rtc/include -I/usr/share/pico-sdk/src/rp2_common/hardware_pwm/include -I/home/djazz/code/nim/nim-pimoroni-pico/build/examples/pimoroni_pico_vendor/sdcard -I/home/djazz/code/nim/nim-pimoroni-pico/src/pimoroni_pico/vendor/sdcard -I/home/djazz/code/nim/nim-pimoroni-pico/src/pimoroni_pico/vendor/fatfs -I/usr/share/pico-sdk/src/rp2_common/hardware_spi/include -I/usr/share/pico-sdk/src/rp2_common/hardware_pio/include -I/usr/share/pico-sdk/src/rp2_common/hardware_adc/include -I/usr/share/pico-sdk/src/rp2_common/pico_async_context/include -I/usr/share/pico-sdk/src/rp2_common/pico_lwip/include -I/usr/share/pico-sdk/src/rp2_common/pico_cyw43_arch/include -I/usr/share/pico-sdk/lib/cyw43-driver/src -I/usr/share/pico-sdk/lib/cyw43-driver/firmware -I/usr/share/pico-sdk/src/rp2_common/pico_cyw43_driver/cybt_shared_bus -I/usr/share/pico-sdk/src/rp2_common/hardware_dma/include -I/usr/share/pico-sdk/src/rp2_common/hardware_exception/include -I/usr/share/pico-sdk/src/rp2_common/pico_cyw43_driver/include -I/home/djazz/code/nim/nim-pimoroni-pico/build/examples/pico-sdk/src/rp2_common/pico_cyw43_driver -I/usr/share/pico-sdk/src/rp2_common/hardware_i2c/include -I/usr/share/pico-sdk/lib/mbedtls/include -I/usr/share/pico-sdk/lib/mbedtls/library -mcpu=cortex-m0plus -mthumb -O3 -DNDEBUG -std=gnu++17 -ffunction-sections -fdata-sections -fno-rtti -fno-use-cxa-atexit -w -MD -MT CMakeFiles/inky_frame_slideshow.dir/inky_frame_slideshow/nimcache/@m..@s..@ssrc@spimoroni_pico@slibraries@spico_graphics@sdrawjpeg.nim.cpp.obj -MF CMakeFiles/inky_frame_slideshow.dir/inky_frame_slideshow/nimcache/@m..@s..@ssrc@spimoroni_pico@slibraries@spico_graphics@sdrawjpeg.nim.cpp.obj.d -o CMakeFiles/inky_frame_slideshow.dir/inky_frame_slideshow/nimcache/@m..@s..@ssrc@spimoroni_pico@slibraries@spico_graphics@sdrawjpeg.nim.cpp.obj -c /home/djazz/code/nim/nim-pimoroni-pico/build/examples/inky_frame_slideshow/nimcache/@m..@s..@ssrc@spimoroni_pico@slibraries@spico_graphics@sdrawjpeg.nim.cpp
/home/djazz/code/nim/nim-pimoroni-pico/build/examples/inky_frame_slideshow/nimcache/@m..@s..@ssrc@spimoroni_pico@slibraries@spico_graphics@sdrawjpeg.nim.cpp: In function 'void atmdotdotatsdotdotatssrcatspimoroni_picoatslibrariesatspico_graphicsatsdrawjpegdotnim_Init000()':
/home/djazz/code/nim/nim-pimoroni-pico/build/examples/inky_frame_slideshow/nimcache/@m..@s..@ssrc@spimoroni_pico@slibraries@spico_graphics@sdrawjpeg.nim.cpp:1829:35: error: invalid conversion from 'const char*' to 'NCSTRING' {aka 'char*'} [-fpermissive]
1829 | colontmpD_ = cstrToNimstr(T4_);
| ^~~
| |
| const char*
/home/djazz/code/nim/nim-pimoroni-pico/build/examples/inky_frame_slideshow/nimcache/@m..@s..@ssrc@spimoroni_pico@slibraries@spico_graphics@sdrawjpeg.nim.cpp:477:61: note: initializing argument 1 of 'NimStringV2 cstrToNimstr(NCSTRING)'
477 | N_LIB_PRIVATE N_NIMCALL(NimStringV2, cstrToNimstr)(NCSTRING str_p0);
| ~~~~~~~~~^~~~~~
Since JPEGDEC's files is in my repo I could simply remove const
from the offending line in the header file.
Unfortunately, I dont think we are going to ever see const semantics working in Nim. But for anyone coming here and using the C++ backend, member
(https://nim-lang.github.io/Nim/manual_experimental.html#member-pragma) (Nim 2.2 or devel) is the way to go about it. The last example shows how to implement a functor with it
This seems to work perfectly on both C and C++ backends:
type ConstCString* {.importc: "const char*".} = object
converter toCString*(self: ConstCString): cstring {.importc: "(char*)", noconv, nodecl.}
converter toConstCString*(self: cstring): ConstCString {.importc: "(const char*)", noconv, nodecl.}
proc `$`*(self: ConstCString): string = $(self.toCString())
{.emit: """
#ifdef __cplusplus
extern "C"
#endif
const char * f() {
return "123";
}
""".}
proc f(): ConstCString {.importc.}
var s: cstring = f()
echo s #123
@daniel-j
Noticed it when wrapping JPEGDEC and trying out the cpp backend. Output from just throwing the snippet into a random file I was working on:
clearly some compilers are more strict on const char*
(such as clang etc.). I'm going to open a pull request which should add a conversion for cstrToNimstr; see also https://github.com/nim-lang/Nim/issues/12703 => https://github.com/nim-lang/Nim/pull/23371
const char*
is returned by some C/C++ functions, but it (usually) can't be represented in Nim. Sometimes we usecstring
for it, which may cause problems because many C++ compilers insist that they're not the same thing.Example
Current Output
Possible Solution
{.importcpp: "('0)$1(@)", nodecl.}
to ask the compiler do the convertion.Additional Information
I've tried
cast[cstring](foo())
, or evencast[ptr char](foo())
, both showed me the same error.