If anyone knows an on-line available version of Josuttis book on STL...
Printable View
If anyone knows an on-line available version of Josuttis book on STL...
I found this link:
http://books.google.com/books?id=n9V...bnail#PPR14,M1
You can browse through a lot of the book, it seems, but as for downloading it, nothing I can find.
Well when I looked for it almost a year ago it wasn't there.
Too bad they don't have an HTML version, so you can refer people to links to specific sections.
If you haven't used much STL or templates in the past, it helps to read it slowly and give yourself time to absorb it. I read it a couple of times already, to pick up the things I forgot after the first read.Quote:
Originally Posted by manav
It does seem to assume you already have basic C++ skills, know about templates, STL containers, etc.
I just thought of another question:
Seeing as I want to improve my iterators, I would set up communication between the iterator and the base class who create the iterator. They would probably communicate via protected/private members.
This, of course, requires friend functions or a friend class to access those private or protected members. It's not possible to declare a friend from a template type from what I understand, so the iterator cannot directly make the base class a friend.
What I was thinking then, was to use a sort of helper class, or communication class. But the question is...
How would I go about making a customizable helper class? Or does it not need to be customized? The helper class would delegate the functions calls between the iterator and the base. Both the base and the iterator could make it friend, if it knew the actual name of this class (not passed as a template type, that is).
So... anyway, suggestions, if you wouldn't mind, as to how to accomplish this.
I was thinking I could use it to enhance the relationship between an iterator and its base class. An iterator should be used to specify a range within the base class, and not a range within the data stored in the class. So if it resizes, and data is moved, the iterator would be invalidated. The base class notifies the iterator of this and allows the iterator to set a new position within the new memory region. Or it could simply tell the iterator that it is now invalidated, to catch mistakes.
Another would be that the iterator can ask the parent for the value at pos X, allow for greater customization of the iterator (it could work better, with more classes). And the best of all is that it could ask the parent to set data, as well. This is especially useful in a ranged iterator which emulates iterator behaviour (since there's not 1 element to change, there's several).
I was thinking of specializing std::backward_copy too. But I would need to specialize a version which uses my iterators, which again would leave the work with the iterators (the iterator would have to do the work!). So by better communication with the base class, it could avoid doing
*dst++ = *src++;
Would would be inefficient.
Actually, you can, and it is really just a syntax issue, but I think it is supposed to be fixed in the next standard. Every compiler I've used has some way around the issue. This should work for GCC and MSVC:Quote:
It's not possible to declare a friend from a template type from what I understand, so the iterator cannot directly make the base class a friend.
Code:#if defined(__GNUC__)
#define TYPE_FRIENDLY(Type_t) friend class typify<Type_t>::type
#endif
#if defined(_MSC_VER)
#if (_MSC_VER >= 1300)
#define TYPE_FRIENDLY(Type_t) friend typename Type_t
#endif
#endif
Use a template class for the coupler. You can declare an instance of a template class using a type passed as a parameter to a template class as a friend, no problem.Quote:
How would I go about making a customizable helper class? Or does it not need to be customized? The helper class would delegate the functions calls between the iterator and the base. Both the base and the iterator could make it friend, if it knew the actual name of this class (not passed as a template type, that is).
Read up on the observer pattern--where the iterator would be registered implicitly as part of obtaining the iterator.Quote:
So... anyway, suggestions, if you wouldn't mind, as to how to accomplish this.
I was thinking I could use it to enhance the relationship between an iterator and its base class. An iterator should be used to specify a range within the base class, and not a range within the data stored in the class. So if it resizes, and data is moved, the iterator would be invalidated. The base class notifies the iterator of this and allows the iterator to set a new position within the new memory region. Or it could simply tell the iterator that it is now invalidated, to catch mistakes.
That's what all iterators do after a fashion--even this not range iterator, ranged iterator of yours. A vector based iterator need not do this, but an iterator for a table based container, for example, must do this. (The 'X' in most cases is "my position".)Quote:
Another would be that the iterator can ask the parent for the value at pos X, allow for greater customization of the iterator (it could work better, with more classes). And the best of all is that it could ask the parent to set data, as well. This is especially useful in a ranged iterator which emulates iterator behaviour (since there's not 1 element to change, there's several).
It would probably be faster if this iterator of yours did do most of the work--with an appropriate allocator/sharing the allocation object of the container object. If the iterator is used to overwrite existing data the container object need not be involved, but if the iterator is used to extend data, append/insert additional data, spawning an instance of a list and inserting all the new data in the target container object would be much better than calling the container object for each insertion--at least for vector based containers.Quote:
I was thinking of specializing std::backward_copy too. But I would need to specialize a version which uses my iterators, which again would leave the work with the iterators (the iterator would have to do the work!). So by better communication with the base class, it could avoid doing
*dst++ = *src++;
Would would be inefficient.
Soma
There's no need for anything templated here. The iterators only need to be friends of the specific instantiation they belong to.
Code:template <typename T>
class foo
{
class inner;
friend class inner;
class inner
{
friend class foo<T>;
};
};
Only, the iterators are free classes and not nested and do not belong to any specific class.
It's supposed to be a generic iterator.
On a side note, does Microsoft's implementation of STL really not care at all between the difference of constant iterators and non-constant iterators?
It really seems as it just treats them as mere classes that emulates the behaviour of a pointer.Code:_OutIt __CLRCALL_OR_CDECL _Copy_backward_opt(_InIt _First, _InIt _Last, _OutIt _Dest,
random_access_iterator_tag, _Nonscalar_ptr_iterator_tag, _Range_checked_iterator_tag)
{ // copy [_First, _Last) backwards to [..., _Dest), arbitrary iterators
// if _OutIt is range checked, this will make sure there is enough space for the copy
// Last and first iterators are constant, while _Dest is not! _Last - _First will produce a new const_iterator,
// which in turn cannot be subtracted from a non-constant iterator.
_OutIt _Result = _Dest - (_Last - _First);
_Copy_backward_opt(_First, _Last, _CHECKED_BASE(_Dest),
forward_iterator_tag(), _Nonscalar_ptr_iterator_tag(), _Range_checked_iterator_tag());
return _Result;
}
What to do about this...?
So you want to friend a typedef? Ah, that's different.Quote:
Only, the iterators are free classes and not nested and do not belong to any specific class.
It's supposed to be a generic iterator.
No, that's incorrect. _Last - _First will produce a signed integer (the iterator's difference_type), and subtracting that from _Dest therefore produces the same type as _Dest.Quote:
_Last - _First will produce a new const_iterator
Yep, that's what I was after.
Yes, with a normal iterator, perhaps, but not with my iterator class (which I'm trying to pass to std::backward_copy).Quote:
No, that's incorrect. _Last - _First will produce a signed integer (the iterator's difference_type), and subtracting that from _Dest therefore produces the same type as _Dest.
Although I don't remember why I cancelled the plans to add an overloaded operator - that returns a uint32_t (in32_t).
That seems kind of stupid. An iterator shouldn't return an integer (or should it?). I have a Iterator::Difference non-member for that. To find the difference between two iterators (or how far apart they are).
Perhaps my iterators are just too incompatible, in case I could need to transform them into an STL iterator...
Then the iterator is broken, sorry. If you don't conform to the concept, don't expect it to work with the algorithms.Quote:
Yes, with a normal iterator, perhaps, but not with my iterator class.
The requirement is that for
Iterator it1, it2;
it1 - it2 == distance between the iterators
and typeof(it1 - it2) == iterator_traits<Iterator>::difference_type.
Yes, that's what I'm struggling with.
I think I'm just going to make a member to create an STL iterator from it since STL's iterators are just so inheritably unsafe. Not to mention they'll be incompatible with my algorithms anyway.
So conversion to/from STL iterators may be necessary...
I wonder if just constructing iterator/const_iterator and giving it a pointer will suffice?
Huh? You mean string::iterator? No, the only ways to obtain valid iterators to a container are the member functions (begin(), end(), and sometimes find() or similar) and modification of existing iterators.Quote:
I wonder if just constructing iterator/const_iterator and giving it a pointer will suffice?
My understanding: He wants 'template <> class container', 'template <> class iterator', and a third class that functions as a bidirectional proxy, and he wants anyone to be able to provide a different 'template <> class container' that will work and work well with his implementation of 'template <> class iterator' by utilizing a specific instantiation/implementation of the proxy. Because he assumes that the proxy may need access to private details he wants to be able to declare any instantiation/implementation of the proxy as a friend of 'template <> class iterator'. The standard lacks the syntax specifics to declare a type passed as a template parameter as a friend so he can't just take the proxy class as a parameter to his implementation. A simple and portable way around the issue is in naming the proxy 'template <> class proxy' which would still allow any customization by means of template specialization.Quote:
There's no need for anything templated here. The iterators only need to be friends of the specific instantiation they belong to.
Of course, I may have misunderstood what he wants.
Soma
I think I'll get to adding something like that later, but it's about like that.
Say that CStringEx::resize is called and resizes the internal buffer. The function will now attempt to notify any iterators bound to it of this change and allow that iterator to update its members to reflect that.
Another example is that CIterator::operator * is called. The iterator then attempts to notify the base class that "we are going to change this for this." The base class will know what to do.
Another example is specialization of std::backward_copy and std::copy.
Instead of doing
*dst++ = *src++;
It could call CIterator::backward_copy or CIterator::copy, which in turn would call CStringEx::backward_copy or CStringEx::copy and let it copy the entire range at once.
Presumably, this communication is done by calling protected or private members. Therefore, the classes may need to be friends of each other or specific functions.
But...
An even better way is a proxy between the classes - just like the traits.Code:template<typename T> void foo(T class)
{
friend class T; // Doesn't work
}
By doing this, I could specify the class that acts as proxy and call public methods in that class.
CIterator::copy calls CProxy::copy.
CProxy::copy could then call CStringEx::copy.
If I wanted different behaviour, I could make a new class, derive from CProxy and overload the functions where I wanted different behaviour.
This also adds an advantage that I can keep communication in free functions. Like
CIterator::copy would call CProxy::copy which would call ::copy_helper.
This would work better with free functions.
But regarding the iterator, I need to do:
But my iterators are incompatible with STL, so I'm providing functions to get an STL iterator instead:Code:std::copy_backward(vIndex, vCopyEnd, vCopyStart);
Now the question is: how do I implement them? Can I simply do such as:Code:std::copy_backward(vIndex.GetSTLIterator(), vCopyEnd.GetSTLIterator(), vCopyStart.GetSTLIterator());
Or must it be in the lines of:Code:std::iterator<type> it(m_pPos);
return it;
?Code:Template typename std::basic_string<VecT>::const_iterator CConstIteratorTmpl::GetSTLIterator()
{
std::basic_string<VecT> t;
typename std::basic_string<VecT>::const_iterator it = t.begin();
it._Myptr = this->m_pCurPos;
return it;
}
O_o
Anyway, as for your iterator problems: your conceptual iterator implementation is a problem because it violates the nature of iterators... primarily because you seem to be trying too hard to avoid traditional iterators.
Compatibility with the STL iterators implies certain constructs. That is all. There is no magic. You can certainly make that code work, but that will not change the coding burden. Trying to fit a incompatible implementation into the normal iterator interface will remain a problem. Eventually, if you want STL compatibility, you will have to write it.Quote:
Now the question is: how do I implement them?
Code:std::iterator<type> it(m_pPos);
return it;
In all honesty, you need to provide for the minimum of a traditional bidirectional iterator implementation for your class first; then you can write your global iterator, your "not a range iterator, ranged iterator", "bidirectional observer iterator thing", and others in terms of the traditional iterator and container interfaces.
What exactly is incompatible about the default iterator returned when you use 'begin()'--whatever it may be called?
Soma
Here is the base class for my iterators:
The incompability is this line in STL:Code:template<typename VecT, typename VecTraits, typename Base, typename BaseIterator, typename DerefType> class CIteratorBase:
public boost::addable<CIteratorBaseTmpl, uint32_t>,
public boost::subtractable<CIteratorBaseTmpl, uint32_t>,
public boost::addable<CIteratorBaseTmpl>,
public boost::subtractable<CIteratorBaseTmpl>
//public std::iterator<std::random_access_iterator_tag, VecT>
{
public:
CIteratorBase();
CIteratorBase(DerefType* pStartPos, const VecT* pRangeStart, const VecT* pRangeEnd);
CIteratorBase(const CConstIterator<VecT, VecTraits, Base>& rSrc);
CIteratorBase(const CIterator<VecT, VecTraits, Base>& rSrc);
CIteratorBase& operator ++ ();
CIteratorBase operator ++ (int);
CIteratorBase& operator -- ();
CIteratorBase operator -- (int);
CIteratorBase& operator += (uint32_t nPos);
CIteratorBase& operator -= (uint32_t nPos);
CIteratorBase& operator += (const CIteratorBaseTmpl& vPos);
CIteratorBase& operator -= (const CIteratorBaseTmpl& vPos);
DerefType& operator * () const;
CIteratorValue<uint32_t, DerefType, VecTraits, Base, CIteratorBase, DerefType>* operator -> ();
bool valid();
friend bool operator < <> (const CIteratorBaseTmpl& rLeft, const CIteratorBaseTmpl& rRight);
friend bool operator == <> (const CIteratorBaseTmpl& rLeft, const CIteratorBaseTmpl& rRight);
friend bool operator > <> (const CIteratorBaseTmpl& rLeft, const CIteratorBaseTmpl& rRight);
friend bool operator != <> (const CIteratorBaseTmpl& rLeft, const CIteratorBaseTmpl& rRight);
CIteratorBase& operator = (const CIteratorBase& rSrc);
void SetSafe();
friend uint32_t Difference <> (const CIteratorBaseTmpl& v1, const CIteratorBaseTmpl& v2);
protected:
/*template<typename T> */void Copy(const CIteratorBaseTmpl& rObj);
DerefType* m_pCurPos;
const VecT* m_pRangeStart;
const VecT* m_pRangeEnd;
bool bUnsafe;
CIteratorValue<uint32_t, DerefType, VecTraits, Base, CIteratorBaseTmpl, DerefType> m_Value;
#ifdef _DEBUG
void DebugChecks() const;
#endif
};
As I explained, using operator - on my iterator will cause it to return an object of itself, not the distance between them.Code:_OutIt _Result = _Dest - (_Last - _First);
OK, so it might be fine, but again, it's mixing iterators. _Last and _First are constant. They are incompatible with _Dest which is non-constant.
STL is incredibly unsafe. It completely disregards the const and assigns anyway.
Even trying to fit an iterator into this design gives incredible amount of headaches.
I didn't even want a const_iterator at first but I had to due to const functions.
There I need to provide conversion.
STL is working in extremely unsafe ways that just won't be compatible with my implementations, I can almost guarantee that.
And since you asked... my iterator functions:
The iterators are completely different types. They are NOT compatible.Code:// Iterators
typedef Iterators::CIterator<T, Traits, CTmplStringBase> iterator;
typedef Iterators::CConstIterator<T, Traits, CTmplStringBase> const_iterator;
iterator begin();
iterator end();
//iterator EndOfString();
const_iterator const_begin() const;
const_iterator const_end() const;
Eh, it sounds like you misunderstand const correctness. Whether _Last and _First are constant or not, (_Last - _First) should be fine since neither _Last nor _First should be modified under the usual semantics of operator-. Likewise, _Dest - (_Last - _First) should be acceptable even if _Dest is non-const and both _Last and _First are const. How is this incredibly unsafe?Quote:
OK, so it might be fine, but again, it's mixing iterators. _Last and _First are constant. They are incompatible with _Dest which is non-constant.
STL is incredibly unsafe. It completely disregards the const and assigns anyway.
EDIT:
Gah, I hate this poor terminology. What you mean is: "_Last and _First are iterators to const objects. They are incompatible with _Dest which is an iterator to a non-const object.
STL is incredibly unsafe. It completely disregards the const and assigns anyway."
But of course, this is still nonsense. The difference between two (random access) iterators is a distance (an integer). Therefore, it does not matter whether they point to const or non-const objects, so long as they point to objects in the same range (or one past the end of the range). Subtracting an integer from a (random access) iterator results in an iterator that is further back in the range. This is all consistent with pointer arithmetic.
However, by defining an operator- that does not follow the normal semantics for iterators, you have introduced an incompatibility. It is not the STL that is unsafe, but your code that is surprising (or even shocking).
Well, nevermind. It makes your head boggle any way you look at it.
I'm still unsure of the recommended solution, however.
An iterator is not a pointer, but the STL seems to treat them like it.
It's just incompatible with my current implementation.
Iterators are a generalisation of pointers. Pointers are a subset of the set of random access iterators.Quote:
An iterator is not a pointer, but the STL seems to treat them like it.
This isn't how my implementation treats it. An iterator is not a pointer and will never be.
An iterator is similar to a pointer in the way that it can be used to walk from a starting location towards an end.
This is where we differ and typically why my iterators won't work with STL.
I think that you are confusing the superset with the subset. An iterator is not a pointer. A pointer is an iterator. In particular, a pointer is a random access iterator.Quote:
An iterator is not a pointer and will never be.
An iterator is similar to a pointer in the way that it can be used to walk from a starting location towards an end.
I suppose you do not have to provide random access iterator semantics. You can say: my iterators are input/output/forward/reverse/bidirectional iterators. Yes, operator- is provided, but it has nothing to do with the operator- used with random access iterators.Quote:
This is where we differ and typically why my iterators won't work with STL.
It may not be very nice and may be a rather error prone for those who assume that it is a random access iterator, but it can be done.
Sigh... So many choices.
Should non-const - const iterator be possible? Returning a non-const iterator from a const iterator is probably impossible, but returning an int, or the distance between them is not a pretty option to me.
But then again, in order to use and modify the data, it still needs to create another iterator from this int, so in that sense, there's nothing wrong with letting operator - return an int.
But then again, this blurs the borders between a const_iterator and iterator. The const_iterator promises that as long as you use it, none will modify the memory it points to. So extracting a distance between a const and non-const and then assigning it to a non-const seems like it's a workaround to this problem. In effect, the const_iterator seems like it isn't doing its job properly...
I'm getting lost in the const waters here... I don't know what to assume or take stance to.
What does (iter2 - iter1) mean, where iter1 and iter2 are of your iterator types?Quote:
Should non-const - const iterator be possible? Returning a non-const iterator from a const iterator is probably impossible, but returning an int, or the distance between them is not a pretty option to me.
Or in other words:Code:public boost::subtractable<CIteratorBaseTmpl, uint32_t>,
public boost::subtractable<CIteratorBaseTmpl>
Where CIteratorBaseTmpl is:Code:CIteratorBaseTmpl operator - (CIteratorBaseTmpl, uint32_t);
CIteratorBaseTmpl operator - (CIteratorBaseTmpl, CIteratorBaseTmpl);
(Probably looking to reform that long loads of templates, but not until I get this stable and working.)Code:# define CIteratorBaseTmpl CIteratorBase<VecT, VecTraits, Base, BaseIterator, DerefType>
So it returns another iterator of same type with the new addr (original - other).
Confusion trail:
Code:CStringEx t = "blablabla";
CStringEx::iterator it1 = t.begin();
CStringEx::const_iterator it2 = t.const_begin();
CStringEx::iterator t3 = it - it2; // IMPOSSIBLE? (CASTS AWAY CONST) (TWO DIFFERENT TYPES)
Code:CStringEx t = "blablabla";
CStringEx::iterator it1 = t.begin();
CStringEx::const_iterator it2 = t.const_begin();
int32_t t3 = it - it2;
CStringEx::iterator it4 = t.begin() + t3; // WORKAROUND AROUND CONST?
Code:CStringEx t = "blablabla";
CStringEx::iterator it1 = t.begin();
CStringEx::const_iterator it2 = t.const_begin();
int32_t t3 = it - it2;
CStringEx::iterator it4 = t.begin() + t3; // CONST (T2) NOT DOING ITS JOB?
You could answer this please: what does (iter2 - iter1) mean, where iter1 and iter2 are of your iterator types?
Alright, it means you get a new iterator at the position (iter2 - iter1). It's the same sense as pointers, but it would be wrapped in an iterator container.
Code:CStringEx t = "1234";
CStringEx::const_iterator it1 = t.const_begin() + 3;
CStringEx::const_iterator it2 = t.const_begin() + 2;
CStringEx::const_iterator it3 = t2 - t1;
cout << *it1; // 4
cout << *it2; // 3
cout << *it3; // 1
But pointer1-pointer2 does not give a pointer, it gives the distance between the pointers.
You're treating your iterators as array indexes.
Yes, the idea behind the iterator is that it represents a position within a container or object.
So if iterator 1 is at position 5, and iterator 2 is at position 3, then you might want to get something at position 2, so you subtract the iterators and thus get (5- 3) = 2, position 2. But all positions in the object is represented as iterators, so instead of the integer 2, you get the iterator at position 2.
Like, don't break the line. All ways seem to have complications, as listed above.
This operator is inherently nonsensical. Iterators are not indices. You don't get an iterator when you subtract two iterators.Quote:
CIteratorBase& operator -= (const CIteratorBaseTmpl& vPos);
The const_iterator is doing its job properly. You cannot use it to modify any object in the range. You can use it to get a distance, and then use this distance with an iterator that can modify the range, but that's not a problem since in the context of the other iterator, the objects in the range are non-const.Quote:
But then again, this blurs the borders between a const_iterator and iterator. The const_iterator promises that as long as you use it, none will modify the memory it points to. So extracting a distance between a const and non-const and then assigning it to a non-const seems like it's a workaround to this problem. In effect, the const_iterator seems like it isn't doing its job properly...
Why use this kind of "iterator" when you can use a size_type (or size_t) to represent the position (index)?Quote:
Yes, the idea behind the iterator is that it represents a position within a container or object.
So if iterator 1 is at position 5, and iterator 2 is at position 3, then you might want to get something at position 2, so you subtract the iterators and thus get (5- 3) = 2, position 2. But all positions in the object is represented as iterators, so instead of the integer 2, you get the iterator at position 2.
Well, we do not think alike. But even if it were to return int32_t, what about the promise of const? What should const_iterator allow and what should it not? It's constant, which means it musn't allow the data to be modified. All of the member are constant.
If it returns an integer, then it allows for workaround what the const. But... if the object you call begin() or const_begin() from isn't const, I suppose that's OK, since you can ask the object which owns the iterator to return a non-const iterator and allow you to modify its data.
The idea is that an index is not an integer. 1 or 5 may not be valid indexes. Only an iterator is a valid index. That was the idea anyway.
It makes no such promise. That would be quite impossible.Quote:
The const_iterator promises that as long as you use it, none will modify the memory it points to.
The only thing it promises is that no one can modify the element it points to through this particular iterator, the same as a pointer-to-const.
I don't see where your problem is.
Just confusion in thinking, that's all. I will change it to return distance instead and see how it works out.
If an index supplied is invalid, what's wrong with throwing std::out_of_range?Quote:
The idea is that an index is not an integer. 1 or 5 may not be valid indexes. Only an iterator is a valid index. That was the idea anyway.
Oh, I will be doing that alright.
You are putting the burden of constant correctness on the wrong code! You can't protect the contents of an object by making it merely difficult, and you can't make it impossible.
Also, returning an integer versus returning an object is irrelevant to your 'const' concerns if you are going to provide a 'difference' function and an ability to convert an 'iterator' to an 'const_iterator'.
Soma
I realized.
But I never claimed to make any difference function to convert between const_iterator/iterator.Quote:
Also, returning an integer versus returning an object is irrelevant to your 'const' concerns if you are going to provide a 'difference' function and an ability to convert an 'iterator' to an 'const_iterator'.
I was admitting I was planning one to convert to a STL iterator.
A 'difference' function to obtain the mathematic distance between two instances could be written--unless you also have changed the meaning of '+' and '==', and if I can't obtain an 'const_iterator' from an 'iterator' your interface is useless and horribly broken because I couldn't call 'const_iterator' code from any 'iterator' code.Quote:
But I never claimed to make any difference function to convert between const_iterator/iterator. I was admitting I was planning one to convert to a STL iterator.
Soma
Your fears are, fortunately, unfounded.
A const_iterator can be built from an iterator.
Initially, I HAD a Difference function that returned the distance between the two iterators. It was a friend function and subtracted the pointers directly.
But I've changed the behaviour now, more to STL like.
Alright, the first test is a success! I have successfully implemented a custom iterator and re-written a stand-alone Replace function to use iterators and std::copy_backward and std::copy.
More tests to be done, of course.
So I imagined.Quote:
Your fears are, fortunately, unfounded.
Now if we could just break you of the habit of sticking a 'C' in front of things...Quote:
But I've changed the behaviour now, more to STL like.
Congratulations! Admit it: now that your coding with it, the STL, you love it. ^_^Quote:
Alright, the first test is a success! I have successfully implemented a custom iterator and re-written a stand-alone Replace function to use iterators and std::copy_backward and std::copy.
More tests to be done, of course.
Soma
Now way :)
Classes are classes and therefore must begin with C :p
Sure, it's good to be able to use STL, but I'm still going to provide interfaces around them in my classes!Quote:
Congratulations! Admit it: now that your coding with it, the STL, you love it. ^_^
Now I'm going to have to overload the std::backward_copy and std::copy, and that probably means the second stage of the plan, because the current implementation only works if the buffer does not need to be resized...
That is how they should work! I don't know about you, but if I swapped the bits around I would want it to warn/crash/raise an exception by default, not just push additional data onto the string. I might make a programming mistake. Just grab a 'back_insert_iterator<your_string<?>>' with 'back_inserter(instance)'. (Assuming you have the relevant 'push_back()' method.)Quote:
Now I'm going to have to overload the std::backward_copy and std::copy, and that probably means the second stage of the plan, because the current implementation only works if the buffer does not need to be resized...
Soma
But... it should work with std::copy even if there's no space! It calls the overloaded class operators! Darn!
More debugging needed -_-
Or maybe it really doesn't work... I'll just have to find out.
EDIT: Duh, of course it doesn't!
It does the work via iterators, not the string class's operators!
Er... what? O_oQuote:
But... it should work with std::copy even if there's no space!
Soma
Still unused to iterators and std::XXX family...
Of course it's an error.
I need to allocate proper space first or forward operator * calls to the string class!
At least read how std::copy is supposed to work, and realize that you are doing something that nobody expects. In the standard algorithm nothing is supposed to be resized; it's stated in one of the preconditions.
So correct copying would be:Quote:
There is enough space to hold all of the elements being copied. More formally, the requirement is that [result, result + (last - first)) is a valid range. [1]
I assume they do it this way so as to allow the containers to manage their own memory, or if we apply Occam's razor, std::copy does little more than perform assignments in a loop. It shouldn't be allocating buffers for anything because that makes copying more work than it needs to be. Do you want to throw exceptions when you copy?Code:vector<o> foo;
// ... use foo ...
list<o> bar( foo.size( ) );
copy( foo.begin( ), foo.end( ), bar.begin( ) );
So do it this way, yes?
Yes. I have to make sure to resize the string to proper size. Only, it invalidates all iterators, which can be pretty annoying. I'll have to manually fix that until I can fix the new implementation.
BTW, the TYPE_FRIENDLY macro only seems to work for VS (at least I think it does - I get no compile error). But for GCC, it does not.
But there is a special iterator, that is produced by back_inserter. So copy could also ask the receiver to make more room:
So, perhaps Elysia needs something akin to the back_inserter?Code:vector<o> foo;
//.. use foo ...
//list<o> bar( foo.size( ) ); //not necessary
copy( foo.begin( ), foo.end( ), back_inserter(bar) );
No, what I need is resize. Because if I try to to copy into a range which does not exist, my iterators will throw an exception. No way around that.
Therefore, resize, then copy.
The biggest problem is that resize invalidates iterators, which is frustrating.
Therefore I'm going ahead with the proxy project, to keep communications with iterators and base classes. Thereby I can notify the iterators when doing something that will invalidate them and fix them up.
Code:// Oops! Will throw an exception if buffer is not big enough!
typename StrType::const_iterator vCopyEnd = strSrc.const_begin() + (strSrc.const_end() - vTemp);
Er... yes, it does, but it may not work for a particular version. (I just tested what I had posted with a few versions.) If it compiles, it works. It only changes the relationship of an object not how it is compiled.Quote:
BTW, the TYPE_FRIENDLY macro only seems to work for VS (at least I think it does - I get no compile error). But for GCC, it does not.
1): What version of GCC do you have? Where did you get it? (Please don't say "latest" or some other nonsense, with something like GCC even the least digit matters.)
2): Do you actually have the 'typify' meta-function in scope? If you don't you need it in scope, or at least one like it. (Boost I think spells 'typify' as 'wrap'.)
If your object provides the appropriate interface 'back_inserter(?)' will return an iterator that will extend the range. That is to say, if you have provided a STL interface, you need only call that function and it will generate a kind of proxy for you.Quote:
Because if I try to to copy into a range which does not exist, my iterators will throw an exception. No way around that.
Soma
Code:template
<
typename type_F
>
class typify
{
public: typedef type_F type;
};
Ah, of course. I was not aware of how you had to do that.
Now it compiles.
Is this all about creating a replace_all function? Or is the user going to use all your iterators to gain more power, for example, to replace all substrings "XXX" with a list of strings in a vector, one by one?
One thing to consider is that while iterators are invalidated at resizing a string, indices aren't.
For now, they are focused around the Replace function since it's the first one I have to get working, but after that, they will be used for a lot more.
Find already uses the iterators, as well.
And while indices are nice, they lack the power of iterators and the flexibility.
I don't want any mix/match. I'm going all-out iterators, with the possibility of an implicit conversion indice -> iterator (integer constructor in iterators).
(I think you are talking to me.)Quote:
Ah, of course. I was not aware of how you had to do that.
Now it compiles.
To be honest, I have a tendency to assume '#include <anything_relevant>' when posting bits and bobs. There was no real reason for you to assume that the class wasn't part of the STL.
Soma
Perhaps not, but I still wouldn't be sure how the macro was intended to work.
I haven't done much classes and put typedefs inside them while referring to those outside.
Gah, another problem.
First, let's inspect the new Proxy class:
It requires 3 template arguments: the base class and and the iterator between whom it shall delegate communication. And lastly also the type of data the classes are working with.Code:#define Template template<typename Base, typename Iterator, typename Type>
#define CStringExProxyTmpl CStringExProxy<Base, Iterator, Type>
Template class CStringExProxy
Alright, so I should go ahead and create a friend for this class in the base class:
And right away we hit a problem!Code:typedef Iterators::CIterator< T, CStringExProxy<CTmplStringBaseTmpl, T, Iterators::CIterator> > iterator;
The iterator class must take the Proxy to be used as a template argument, so as we see, we get circular references! This is no go...
So how about passing the types to be used to the iterator class instead of one big type?
Well, it would have worked fine, so long as the name of the class to be used as proxy wasn't passed!Code:#define CIteratorBaseTmpl CIteratorBase<VecT, Proxy, DerefType, Base>
Template class CIteratorBase:
public boost::addable<CIteratorBaseTmpl, uint32_t>,
public boost::subtractable<CIteratorBaseTmpl, uint32_t>,
public std::iterator<std::random_access_iterator_tag, VecT>
{
public:
TYPE_FRIENDLY(Proxy<Base, VecT, CIteratorBaseTmpl>);
What's a good solution on this? Hard-code the class name? Or is there, yet again, some special syntax that allows me to append more template types to another template type?
A good solution is to simply use the proxy class as its own meta-function. The container class needs to store an instance of a proxy class. The iterator needs to store an instance of the proxy class. The proxy class must provide a special kind of interface to communicate between the two. This interface must be set at compile-time. The interface must be typed so that the result is type safe. The proxy must be able to be adjusted based on the instantiation of the container and iterator types. This, believe it or not is fairly simple.Quote:
What's a good solution on this?
1): You provide a typed interface for the proxy by using a template class and you provide a default non-implementation of this class named 'template <?> proxy<void, ...>'. (That is: if the proxy class template requires four types the name of the base interface is 'template <> proxy<void, void, void, void>'.)
2): You provide the proxy class a nested meta-function named 'apply' that takes the same number of parameters as the target proxy template. This goes for the real implementations and the non-implementation.
3): In any reference to the iterator class you pass the non-implementation of the proxy class.
4): Inside the iterator class you use the nested 'apply' meta-function to obtain the target type of the proxy you really want. (That is: with the above in mind, the type of the proxy you want would by something like 'typename proxy_T::template apply<char, my_string<char, my_iterator<char, proxy<>>>>::type'.)
Now, you may think that this isn't type safe, but it is. The instances of the proxy template are unrelated by inheritance. Any instance of the iterator class will have to obtain a real proxy instance because the non-implementation isn't implemented. (To be honest, this is only a way to simplify certain code. The specialization isn't in any way needed.) Once you have the 'typedef' for this real instance you use that 'typedef' in every place you would have used the parameter that would have been passed directly to the template.
Believe it or not, you lose nothing by doing it this way. You see, you setup the proxy class to forward the parameters to the nested 'apply' meta-function. Remember the actual container type, iterator type, and element type, are specified by indirection; any number of additional proxy specific parameters can be used. (Technically, this means partial specialization of the iterator on the types associated with the target proxy template is impossible. In reality this is nearly never relevant and can be mitigated simply and removed entirely by adding an additional layer of abstraction.)
No special syntax, but you can code the support manually. Recursively nest the 'apply' meta-function as referenced above. (This isn't as difficult as you might think.) This alone doesn't solve the problem.Quote:
Or is there, yet again, some special syntax that allows me to append more template types to another template type?
Soma
This, I think, is something I haven't done before so I'm not sure I can follow quite right. Examples would be appreciated. I typically find them to say a lot more than words.
Thanks.
EDIT:
I just did some tests with nested templates and the following seems to work!
Code:template<typename A, typename B, typename C> class CA {};
template<typename A, template<typename A2, typename B2, typename C2> class B, typename C> class CB
{
friend typename B<A, int, C>;
};
void Help()
{
CB<int, CA, int> a;
}
That has problems of its own, but if you find it works for your scenario... go for it. (The problem is, the proxy class is the one that needs the friend listing. By using that sort of model your force any iterator or container classes to require a precise number of parameters. I suppose, with the right meta-function you might get around it...)Quote:
I just did some tests with nested templates and the following seems to work!
Well, just in case:
That should explain some of what I was discussing.Code:template
<
typename container_F = void,
typename iterator_F = void,
typename element_F = void
>
class proxy_wh
{
template
<
typename container_FR = container_F,
typename iterator_FR = iterator_F,
typename element_FR = element_F
>
class apply
{
typedef proxy_wh<container_FR, iterator_FR, element_FR> type;
};
};
template
<
typename container_F,
typename element_F,
typename proxy_F = proxy_wh<void, void, void>
>
class iterator_wh
{
typedef typename proxy_F::template apply<container_F, iterator_wh<container_F, element_F, proxy_F>, element_F> proxy_type;
template
<
typename container_FR = container_F,
typename element_FR = element_F,
typename proxy_FR = proxy_F
>
class apply
{
typedef iterator_wh<container_FR, element_FR, proxy_FR> type;
};
};
Soma
That sure is a tricky and complex code, though.
Even coding with as many templates I have now, it gives a lot of nightmares.
I think I'll stick to nested templates for the time being, at least. One little change destroys literally hundreds of lines of code.
EDIT:
GCC supports TR1, or doesn't it? std::tr1::shared_ptr won't work...
GCC does, but does your GCC? You need at least 4.2.
Pain >_<
All the include directories are missing from the compiler, so whatever I do, I get hundreds of errors.
since you seem to be already having problems with gcc tell me do you also have this problem now or before
why gcc would expect unqualified-id and what is that id :confused:Quote:
Originally Posted by gcc
That typically just means that GCC encountered something it didn't like or was expecting.
yeah posted here
Gives me the errors:Code:return this->OperatorMinusMinusPost< /*CIteratorTmpl*/Iterators::CIterator<VecT, Proxy, Base> >();
...with GCC, but compiles in Visual Studio.Code:error: expected primary-expression before '>' token
error: expected primary-expression before ')' token
Why? I don't understand how GCC works or what it expects.
OperatorMinusMinusPost is a member function inherited from the template base class. But it is also a template member function:
Anyone knows what the "correct" syntax is?Code:template<typename T> T OperatorMinusMinusPost();
What are Iterators, CIterator, and the three arguments?
You might have to write
return this->template OperatorM...
They are:
And base:Code:namespace Iterators
{
// ...
template<typename VecT,
template<typename ProxyBase, typename Iterator, typename Type> class Proxy,
typename Base> CIteratorTemplate class CIterator:
public CIteratorBase<VecT, Proxy, Base>,
// ...
I don't understand what syntax GCC is expecting.Code:namespace Iterators
{
// ...
template<typename VecT,
template<typename ProxyBase, typename Iterator, typename Type> class Proxy,
typename Base> class CIteratorBase:
public std::iterator<std::random_access_iterator_tag, VecT>
{
// ...
protected:
// ...
template<typename T> T OperatorMinusMinusPost();
Well, as I said, try
OperatorMinusMinusPost is a dependent name and it refers to a template, so you need the template keyword.Code:return this->template OperatorMinusMinusPost< /*CIteratorTmpl*/Iterators::CIterator<VecT, Proxy, Base> >();
Seems to work. Again, it seems VS's compiler is too advanced and too good for its own good, seeing as it can compile without those words.
"Too advanced and too good"?
Well, perhaps. But I truly wish there was a way to switch it off and make it compile only strictly compliant code, so that such surprises wouldn't happen. In fact, since the compiler is able to compile the code without the help, it should be exceptionally good at pointing out where the typename and template keywords are missing.
But to the best of my knowledge, there's no such option.
Troll.Quote:
Again, it seems VS's compiler is too advanced and too good, seeing as it can compile without those words.
Soma
Yes, as in VS's compiler is advanced enough to be able to compile code without those keywords.
The standard requires them only to help with compiler implementation if I'm not mistaken.
But, this is not a good thing, since it doesn't enforce nor warn about it.
There is a way, but unfortunately, Microsoft headers are full of extensions, so it won't compile without microsoft extensions. It's really a shame.Quote:
Well, perhaps. But I truly wish there was a way to switch it off and make it compile only strictly compliant code, so that such surprises wouldn't happen. In fact, since the compiler is able to compile the code without the help, it should be exceptionally good at pointing out where the typename and template keywords are missing.
But to the best of my knowledge, there's no such option.
Which is just stupid. GCC has
which enables all extensions for the file even if they're disabled on the command line. Which makes sense, because it's completely absurd that the windows.h header doesn't compile if I set VC++ to strict compliance mode.Code:#pragma GCC system_header
So very true and I wish it were so, but I have absolutely NO way of progammatically enabling and disabling extensions.
No. It isn't. It may appear that way, but in reality it is an artifact of a broken compiler. This artifact wasn't accomplished with intent; it wasn't done to make users lives easier.Quote:
Yes, as in VS's compiler is advanced enough to be able to compile code without those keywords.
The standard requires them because the C++ language has several ambiguous uses leading to confusion or wrong results. Without the 'template' keyword the statement is wrong because the statement can be interpreted as a much more complex statement that is missing an element.Quote:
The standard requires them only to help with compiler implementation if I'm not mistaken.
This is a nonsensical reason. They use 'pragma' everywhere to turn off and on other warnings and errors. They didn't do it because supporting broken code is not something they want to do.Quote:
There is a way, but unfortunately, Microsoft headers are full of extensions, so it won't compile without microsoft extensions. It's really a shame.
Soma
So you say. I don't agree.
But all code compiles fine in VS. Apparently, it compiles the correct code.Quote:
The standard requires them because the C++ language has several ambiguous uses leading to confusion or wrong results. Without the 'template' keyword the statement is wrong because the statement can be interpreted as a much more complex statement that is missing an element.
It's not nonsense. Compile the headers without extensions and you'll get loads of compile errors.Quote:
This is a nonsensical reason. They use 'pragma' everywhere to turn off and on other warnings and errors. They didn't do it because supporting broken code is not something they want to do.
Which is irrelevant; the accounts are in the history.Quote:
So you say. I don't agree.
It only compiles as you apparently want it to be compiled. A conforming compiler will interpret that statement as missing a key element because the standard says it is missing an element.Quote:
But all code compiles fine in VS. Apparently, it compiles the correct code.
It is nonsense.Quote:
It's not nonsense. Compile the headers without extensions and you'll get loads of compile errors.
1): Nothing is stopping them from supporting C and C++ correctly.
2): As I've said, they use 'pragma' everywhere to turn off and on other warnings and errors.
Edit: "They didn't do it because supporting broken code is not something they want to do." Just to clear that up: they want to support the existing code base, but not new broken code.
Soma