I am writing a JSON class for C++11, see http://github.com/nlohmann/json. My central data structure is a class wrapping the JSON value types (null, array, object, string, bool, number) in a union and offering it in via a nice C++ interface. As arrays (implemented via std::vector
) and objects (std::map
) come with their own iterators, I implemented a "wrapper" iterator which delegates calls to operator++
or operator->
to the respective member variables. Furthermore, I implemented two additional functions std::string key()
to access keys of a JSON object and reference value()
as an alias to operator*()
.
So far, so good (see https://github.com/nlohmann/json/blob/master/src/json.hpp for the complete source code)...
Then I wanted to implement reverse_iterator
and const_reverse_iterator
. And here the problems begin.
- If I realize them via
using reverse_iterator = std::reverse_iterator<iterator>;
andusing const_reverse_iterator = std::reverse_iterator<const_iterator>;
, everything is fine, but the functionskey()
andvalue()
are not available withreverse_iterator
orconst_reverse_iterator
objects. - If I implement my own class
reverse_iterator
likeclass reverse_iterator : public std::reverse_iterator<typename basic_json::iterator>
, I need to implement the whole class again. It is not sufficient to give an implementation forkey()
andvalue()
, but also foroperator++()
and all the other stuff which I hope to have gotten for free using thestd::reverse_iterator
adaptor.
I spent quite some time searching for answers, but all references I found either scratching the surface of incomplete toy examples or come to the conclusion that iterators are hard work and one should move to Boost...
So here are my questions:
- How can I create a
reverse_iterator
from my customized classiterator
so that it inherits as much functions as possible? - If inheriting behavior beyond the standard stuff does not work automatically, how can I write a
reverse_iterator
without repeating myself completely?
Any help is greatly appreciated!
Unfortunately, I did not receive an answer, so here is what I did. Maybe an ugly solution provokes someone to post something nicer :-)
Background
So the first thing I learned is that
std::reverse_iterator
does nothing more than to encapsulate a "normal" iterator (calledcurrent
, accessible viabase()
) and to establish the relationship of the reverse iterator being "one element more to the left" than the "normal" iterator.(image from cppreference.com)
Generic solution
As long as the "normal" iterator has the standard interface and does not use any additional functions, the lines
are sufficient to automagically convert your own iterator class into a reverse iterator.
And with the member functions
reverse iteration is possible, for instance code like
works.
Adding more functionality to iterators
As I wrote in the question, I extended the
iterator
class with two additional members,key()
andvalue()
. The former allows quick access to the key of a JSON object during iteration. The latter is an alias to writeit.value()
rather than*it
. Unfortunately, the above approach does not inherit these function toreverse_iterator
.To enrich the user-defined reverse iterators, we need to inherit from
std::reverse_iterator<iterator>
and delegate calls to the base iterator. Unfortunately, I found no other approach than to do this manually. For the mentioned functions, this looks as follows:The most tricky part is that you need to implement the "off-by-one" relationship manually:
base()
.And with that, we're nearly done. Nearly, because of the
...
in the code above. What is left to do is to delegate all other calls to functions likeoperator++
to the base class. I found now way to let someone else to this boring delegation.So the class contains code like
(Note the definition of
base_iterator
.)That's it. We now have user-defined reverse iterators which allow us to code
Cleaning up
In a discussion on Github, gregmarr proposed to combine the
reverse_iterator
and theconst_reverse_iterator
classes into a single template class likeThis allows to write
and to be happy.
Full example
See here for the complete code.
I'd still like to see a solution that avoids repeating most functionality, but this one is good enough for me. And as I haven't found anything better in quite a while, I decided to share it.