Hello,
I'm rather new to Nim and I'm currently experimenting with ways to interact with C++. I really like the language and the ease of interacting with C++, but I noticed some weirdness taking place when using imported C++ template types as return types of non-imported procs. As a toy model I used the following code:
type
Map {.importcpp: "std::map", header: "<map>".} [T,U] = object
proc cInitMap(T: typedesc, U: typedesc): Map[T,U] {.importcpp: "std::map<'*1,'*2>()", nodecl.}
# This one alone works.
proc initMap[T, U](): Map[T, U] =
cInitMap(T, U)
# This one causes an error in N_NIMCALL
var x: Map[cstring, cint] = initMap[cstring, cint]()
This code manages to get up to the step of generating C++ code, but does not compile with the following error in g++:
c:\nim\nimcache\cppgentest.cpp:11:50: error: macro "N_NIMCALL" passed 3 arguments, but takes just 2
N_NIMCALL(std::map<NCSTRING, int> , initmap_94036)(void);
^
c:\nim\nimcache\cppgentest.cpp:51:50: error: macro "N_NIMCALL" passed 3 arguments, but takes just 2
N_NIMCALL(std::map<NCSTRING, int> , initmap_94036)(void) {
^
c:\nim\nimcache\cppgentest.cpp:11:57: error: expected constructor, destructor, or type conversion before ';' token
N_NIMCALL(std::map<NCSTRING, int> , initmap_94036)(void);
^
c:\nim\nimcache\cppgentest.cpp: In function 'int N_NIMCALL()':
c:\nim\nimcache\cppgentest.cpp:57:9: error: cannot convert 'std::map<char*, int>' to 'int' in return
return result;
^
c:\nim\nimcache\cppgentest.cpp: In function 'void cppgentestInit000()':
c:\nim\nimcache\cppgentest.cpp:102:26: error: 'initmap_94036' was not declared in this scope
x_94058 = initmap_94036();
^
In the generated cpp file, the offending line is the following:
N_NIMCALL(std::map<NCSTRING, int> , initmap_93036)(void);
What seems to be happening is that the preprocessor macro N_NIMCALL defined in nimbase.h cannot cope with a rettype which is a C++ template with more than one argument, as the arguments of the template are separated by commas. This then leads to the macro seeing three arguments
std::map<char*,
int>,
initmap_94036
instead of the two arguments expected. It then crashes and burns. This failure propagates and in turn causes initmap_94036 to not be defined. The same behaviour happens for all calling convention macros defined in nimbase.h. We get something similar for template return types with two or more arguments.
Just using cInitMap works fine, as it doesn't use a calling convention macro such as N_NIMCALL.
Now, I'd like to ask if I was doing anything obviously wrong or unsupported in the above, or if this behaviour is indeed unintended / a bug (in which case I would file an issue on GitHub). Are there any obvious workarounds for this in Nim?
Though I'm fairly new to Nim, if this turns out to be a bug I would very much like to help, if I can.
You can try to add
#define COMMA ,
and replace the offending comma(s) with the preprocessor macro. If this works, the nim compiler should probably emit code with this protection.