Hello,
I am trying to write some Nim code that interfaces with some legacy C++ code using std::list. The API has a way to return a std::list object, and I am attempting to fetch elements from that returned object.
I am facing a roadblock when attempting to get the value pointed by an std::list::iterator type variable.
Here's a minimal example: https://play.nim-lang.org/#ix=3RP6
when not defined(cpp):
{.error: "This file needs to be compiled with the cpp backend.".}
import std/[strformat]
{.push header: "<list>".}
type
List*[T] {.importcpp: "std::list".} = object
# https://nim-lang.github.io/Nim/manual.html#importcpp-pragma-importcpp-for-objects
ListIter*[T] {.importcpp: "std::list<'0>::iterator".} = object
ListConstIter*[T] {.importcpp: "std::list<'0>::const_iterator".} = object
SizeType* = csize_t
converter ListIterToListConstIter*[T](x: ListIter[T]): ListConstIter[T] {.importcpp: "#".}
# https://nim-lang.github.io/Nim/manual.html#importcpp-pragma-importcpp-for-procs
proc initList*[T](): List[T] {.importcpp: "std::list<'*0>()", constructor.}
# https://en.cppreference.com/w/cpp/container/list
proc len*(l: List): SizeType {.importcpp: "#.size()".}
proc insert*[T](v: var List[T], pos: ListConstIter[T], val: T): ListIter[T] {.importcpp: "insert".}
proc cBegin*[T](v: List[T]): ListConstIter[T] {.importcpp: "cbegin".}
{.pop.} # pop header: "<list>"
var
l = initList[cint]()
echo &"length of list = {l.len()}"
discard l.insert(cBegin[cint](l), 100)
echo &"length of list = {l.len()}"
echo &"l begin = {cBegin[cint](l)}"
# echo &"l begin = {cast[addr cint](cBegin[cint](l))}" # doesn't work: error: use of deleted function ‘NimMainModule()::<unnamed union>::<constructor>()’
# echo &"l[0] = {(cast[addr cint](cBegin[cint](l)))[]}" # doesn't work: Error: expression has no address
You will be able to compile and run that after you set the Compilation target to C++.
But it will fail if you uncomment the last or second-to-last line in that code.
Can someone help me write a Nim code equivalent to this example on https://www.geeksforgeeks.org/listbegin-listend-c-stl/ ?
From the above C++ example:
int main()
{
// declaration of list container
list<int> mylist{ 1, 2, 3, 4, 5 };
// using begin() to print list
for (auto it = mylist.begin(); it !=
mylist.end(); ++it)
cout << ' ' << *it;
return 0;
}
I am failing to do an equivalent of *it from the above snippet in Nim code.
Thanks!
proc `*`[T](it: ListIter[T]):T {.importcpp: "(*#)", nodecl.}
@sls1005 That worked! Thank you!
btw do you canonically name that proc as *?
I was able to call it using this style only:
echo &"l[0] = {begin[cint](l).`*`}"
echo &"l[0] = {`*`begin[cint](l)}"
This feels more intuitive to me:
proc getItem*[T](it: ListIter[T]): T {.importcpp: "(*#)", nodecl.}
echo &"l[0] = {begin[cint](l).getItem()}"
I have share the full working code here for future reference for me or anyone: https://play.nim-lang.org/#ix=3RPo
var it = l.begin()
echo *it
@sls1005
{.importcpp: "(*#)".}
I cannot find this in the manual. What should I search for to look for specifically this syntax? I looked at https://nim-lang.github.io/Nim/manual.html#noalias-pragma-importcpp-pragma.
It's an unary operator.
Duh, :facepalm:
Thanks again!
Answering this to myself..
From https://nim-lang.github.io/Nim/manual.html#importcpp-pragma-importcpp-for-procs,
A hash # symbol is replaced by the first or next argument.
So with (*#), when the `` * `` proc is called with an iter var, say it, that translates to (*it) (or that's what I think it happens).
I had assumed that the importcpp pragma takes only types and function names as arguments. TIL that it also accepts the C++ * operator 🤯.
Thanks for that tip! This works too.
proc `[]`*[T](it: ListIter[T]): T {.importcpp: "(*#)", nodecl.}
echo &"l[0] = {l.begin()[]}"
@sls1005 I just tried out this, and that works too:
proc `[]`*[T](it: ListIter[T]): T {.importcpp: "*#".}