haxscramper / hcparse

High-level nim bindings for parsing C/C++ code
https://haxscramper.github.io/hcparse-doc/src/hcparse/libclang.html
Apache License 2.0
37 stars 2 forks source link

meta: dealing with language differences between C++ and nim #18

Open haxscramper opened 2 years ago

haxscramper commented 2 years ago

Additional related links

haxscramper commented 2 years ago

Const is a huge PITA - I dynlib deals with the inconsistencies by generating code like (it just ignores const-ness of the argument)

typedef N_CDECL_PTR(NCSTRING, tyProc__cTCodr69cPNr9chtH7apXJYg) (NCSTRING s);
Dl_4294968796_ = (tyProc__cTCodr69cPNr9chtH7apXJYg) nimGetProcAddr(TM__xDZurH9b9cD5SoY3HyXHwqcw_2, "wave_unescapeIncludeToken");

and then calling it, but if I try to do a simple importc it seems that I either have to header: the procedure import, or make sure the header is never imported, otherwise I get a conflicting types error.

wave_c_api.h defines types, I import them using header:, but at the same time for regular procedures it is more problematic.

haxscramper commented 2 years ago
[...]/@mnim_cxx_const_main.nim.c:63:15: error: conflicting types for ‘get_cstring_link’; have ‘void(char *)’
   63 | N_CDECL(void, get_cstring_link)(NCSTRING str);
      |               ^~~~~~~~~~~~~~~~
/home/test/.choosenim/toolchains/nim-1.6.0/lib/nimbase.h:201:44: note: in definition of macro ‘N_CDECL’
  201 | #    define N_CDECL(rettype, name) rettype name
      |                                            ^~~~
In file included from [...]: note: previous declaration of ‘get_cstring_link’ with type ‘void(const char *)’
    6 | void get_cstring_link(const char* cstring);
      |      ^~~~~~~~~~~~~~~~

nim_cxx_const_main.nim

const
  h = "lib.h"
  l = "liblib.so"

type
  str {.importc: "struct str", header: h.} = object
    field: cint

echo str()

proc get_cstring_dl(str: cstring) {.dynlib: l, importc: "get_cstring_dl".}

get_cstring_dl(nil)

proc get_cstring_link(str: cstring) {.cdecl, importc: "get_cstring_link".}

get_cstring_link(nil)

xmake.lua

target("lib")
    set_kind("shared")
    add_files("lib.c")
    set_targetdir(os.scriptdir())

target("main")
    set_kind("binary")
    add_deps("lib")
    add_files("nim_cxx_const_main.nim")
    add_links("lib")
    set_targetdir(os.scriptdir())

lib.c

void get_cstring(const char* cstring) {}
void get_cstring_link(const char* cstring) {}

lib.h

struct str {
    int field;
};

void get_cstring_dl(const char* cstring);
void get_cstring_link(const char* cstring);

Reproduce with xmake


nodecl fixes that for C backend, but in C++ I get ‘get_cstring_link’ was not declared in this scope int T4_ = get_cstring_link(((NCSTRING) NIM_NIL));

haxscramper commented 2 years ago

I think this could've been trivially solved if Const[T] {.importcpp: "const '*0".} worked correctly, but right now it

  1. Does not work (generates const '*0 T1_;)
  2. Implicitly promotes file to the C++ backend, failing compilation

Can this be fixed by making importcpp: work the same way in procs and type imports?

haxscramper commented 2 years ago

I can wrap each type twice - as a regular object and as a const. This approach is of course insane, since I can also have ptr T, ptr ptr T, ref T and so on, but this way nim would generate correct procedure prototypes.