I'm trying to wrap ROS(robot operating system) which has iterators that yield non constructible objects, i.e. dereferencing the iterator returns an object which only has a private constructor.
I've tried to use this format for wrapping c++ iterators in a nim iterator:
type
CxxIterator*[T,U]{.importcpp: "'0::iterator",nodecl.} = object
proc inc*[T,U](it: var CxxIterator[T,U]): void {.importcpp: "(++#)".}
proc `==`*[T,U](a,b:CxxIterator[T,U]): bool {.importcpp: "(# == #)".}
proc `[]`*[T,U](it: CxxIterator[T,U]):U {.importcpp: "(*#)".}
proc cxBegin(self:View):CxxIterator[View,Message] {.header:"rosbag/view.h",importcpp:"(#.begin())".}
proc cxEnd(self:View):CxxIterator[View,Message] {.header:"rosbag/view.h",importcpp:"(#.end())".}
iterator cxxitems*[T](obj:T):Message{.inline.} =
#mixin cxBegin,cxEnd
var start = obj.cxBegin()
var fin = obj.cxEnd()
while start != fin:
yield start[]
inc start
but Nim emits:
{
rosbag::MessageInstance const msg;
while(1){
/*...*/
}
}
which results in error: no matching function for call to ‘rosbag::MessageInstance::MessageInstance()’
For comparison, here's how they should be used in c++:
rosbag::View view(bag);
BOOST_FOREACH(rosbag::MessageInstance const m, view)
{...}
or with c++11
for(rosbag::MessageInstance const m: rosbag::View(bag))
{...}
And here is the ROS source for the real-world iterators: view.h message_instance.h
I've put together a minimal example here: playground version:
please forgive my shitty c++, I know there are bugs in the equality operators:
#include <iterator>
#include <cstddef>
struct Data
{
int m_data[20];
};
class DataReference
{
friend class View;
public:
int inspect() {return m_data->m_data[index];}
int index;
private:
Data const* m_data;
DataReference(Data const& data,int idx) : m_data(&data), index(idx) {}
};
class View
{
public:
struct Iterator
{
using iterator_category = std::forward_iterator_tag;
using pointer = DataReference*;
using reference = DataReference&;
Iterator(pointer ptr) : m_ptr(ptr) {}
reference operator*() const { return *m_ptr; }
Iterator& operator++() { (m_ptr->index)++; return *this; }
Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; }
friend bool operator!= (const Iterator& a, const Iterator& b) { return (a.m_ptr->index != b.m_ptr->index); };
private:
pointer m_ptr;
};
Iterator begin() {
DataReference* tmp = new DataReference(*m_data,0);
return Iterator(tmp);
}
Iterator end() {
DataReference* tmp = new DataReference(*m_data,20);
return Iterator(tmp);
}
View(Data const& d) : m_data(&d){}
private:
Data const* m_data;
};
type
Data{.importc.} = object
m_data: array[20,cint]
DataReference{.importc.} = object
View{.importc,nodecl.} = object
proc initView(d: Data):View{.importcpp:"View(#)".}
proc inspect(d: DataReference):int{.importcpp.}
type
CxxIterator*[T,U]{.importcpp: "'0::Iterator",nodecl.} = object
proc inc*[T,U](it: var CxxIterator[T,U]): void {.importcpp: "(++#)".}
proc `!=`*[T,U](a,b:CxxIterator[T,U]): bool {.importcpp: "(# != #)".}
proc `[]`*[T,U](it: CxxIterator[T,U]):U {.importcpp: "(*#)".}
proc cxbegin(self:View):CxxIterator[View,DataReference] {.importcpp: "#.begin()".}
proc cxend(self:View):CxxIterator[View,DataReference] {.importcpp: "#.end()".}
proc main() =
var
d:Data
ii:cint
for i in d.m_data.mitems:
i = ii
inc ii
var v{.nodecl.} = initView(d)
var it = v.cxbegin()
while it != v.cxend():
echo it[].inspect()
inc it
main()
The above code works, but what I want is:
for x in initView(d):
echo x.inspect()
is there anyway to do that?
There's always a macro for that, isn't there.
import macros,std/genasts
macro cxxiter(x:ForLoopStmt):untyped =
let
it = x[0]
iter = x[1][1]
body = x[2]
result = genAst(it,iter,body):
var
v{.nodecl.} = iter
itr = v.cxbegin()
while itr != v.cxend():
let it = itr[]
body
inc itr