I've been feeling the itch to code, however, and so I'm going to give a quick update on the SGL in the hopes that it'll push me over the edge and I'll start back up again.
It turns out that learning generics at the same time as designing a complicated series of generic containers wasn't that good an idea. The whole equality issue I encountered is a prime example of why: I did it right initially, but a misunderstanding in how generics work resulted in my replacing my correct Equals functions with incorrect versions that still did the job. I later corrected my misunderstanding, but the incorrect Equals remained. Later attempts to perform trickery then failed, because Equals wasn't implemented right, and I concluded that such trickery was impossible when in reality it was quite trivial to fix.
All told, creating those writeups last week were a god send. They made me realize how shoddy my initial design was, and I have since cleaned it up quite a bit. The actual use of the iterators remains largely unchanged, but now you can do this:
(RandomAccessIterator begin, RandomAccessIterator end)
where Itr : RandomAccessIterator
//Some algorithm specialized for RandomAccessIterators
(BidirectionalIterator begin, RandomAccessIterator end)
where Itr : BidirectionalIterator
//Same algorithm, but using the more generic BidirectionalIterator
By doing this, I have also made it possible for the compiler to infer all the type arguments on its own. To see why, consider what the functions would have been before:
(Itr begin, Itr end)
where Itr : BidirectionalIterator
//All I can do is use the Bidirectional version, no random access optimizations :(
In both versions, the function has two generic parameters, T and Itr. When infering those parameters, the compiler only looks at the actual function arguments...it doesn't consider the where clauses. In the second version, T is only present in the where clause, so the compiler has to be told explicitly what type to subsitute in. Now that I can specify the specific type of iterator directly [rather than delegating it to the where clause], that is no longer an issue.
The other change this has made possible is the addition of iterator adaptors. For instance, I now have a BackInsertIterator that mimics the behavior of std::back_insert_iterator. How it works will have to wait for another day, though, since I haven't gotten to the chapter on generic containers.
Why not? Because when I went to write up that section, I realized my containers were a horrible mess. I wrote them first, so that I had something to iterate over, but the radical changes to my iterator design only propagated backwards so far as was neccessary to keep things compiling properly. The end result was a nightmare, with a horrible hodgepodge of classes and interfaces and general crap that needs to be gotten rid of.
If this post does the trick, it should only take a day or so to get right. Then we can continue our discussion of the SGL.
In the mean time, I need to figure out some way to get snazzy screenshots out of this project so that my journal is a little less boring.
*edit: PS, I've implemented a bunch of the generic algorithms required by the C++ standard. I'm rather proud of the fact that I can actually copy the code from an actual implementation, fix the stylistic differences [* -> Value, in particular], and end up with a fully working function. I don't, of course, because that would be stealing, and most of them are near impossible to read anyhow. But the fact that I could makes me feel all proud of my little generic iterators.