
Hi All, here is my review of the circular buffer class. What is your evaluation of the design? --------------------------------------- The design is good. People can easily learn to use it. There are some miner things that I would like see changed, like double versions of push_back and push_front. I don't understand why both set_capacit and resize are provided and I would like to see them merged if possible. I also think push_back should throw an exception if no element is inserted. This behavior is somewhat different push_back() from a normal std container. If there are similar cases then I think they should be looked at again too. What is your evaluation of the implementation? ----------------------------------------------- I haven't looked closely at the source code. What is your evaluation of the documentation? ---------------------------------------------- very good once the exception-safety issues are solved What is your evaluation of the potential usefulness of the library? ---------------------------------------------------------------- I'm in doubt. The library has efficiency as its hallmark. The rationale says that it gives efficient FIFO queue. So I tried to compare it with std::deque, and here is my results of an /O2 build with vc71: $ ./cbuffer_vs_deque.exe circular_buffer<int>: 2.15 s deque<int>: 2.84 s circular_buffer<string>: 20.59 s deque<string>: 18.64 s The test program is attached; I might have made an error, but if I have not, then I don't see the claim as true. In that case I need to see some more evidence about why the speed advantages should be so good. It also make me wonder if there is any need for circular_buffer_space_optimized. Did you try to use the library? With what compiler? Did you have any problems? ---------------------------------------------------------------------------- ------ yes, with vc7.1; Comeau 4.3.0 could not compile it. It worked fine on vc7.1 How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? ---------------------------------------------------------------------------- ------------------- A couple of hours with both documentation and code use. Are you knowledgeable about the problem domain? ----------------------------------------------------- I don't have experience with embedded applications; I have written stuff in which performace was critical and in which specially designed datastructures was necessary. Do you think the library should be accepted as a Boost library? --------------------------------------------------------------- Yes if the author can convince me that we really get more performance. No otherwise. best regards Thorsten begin 666 cbuffer_vs_deque.cpp M(V1E9FEN92!"3T]35%]$25-!0DQ%7T-"7T1%0E5'(#$-"@T*(VEN8VQU9&4@ M(F)O;W-T+V-I<F-U;&%R7V)U9F9E<BYH<' B#0HC:6YC;'5D92 \8F]O<W0O M<')O9W)E<W,N:'!P/@T*(VEN8VQU9&4@/&)O;W-T+W1H<F5A9"]T:')E860N M:'!P/@T*(VEN8VQU9&4@/&1E<75E/@T*(VEN8VQU9&4@/'-T<FEN9SX-"B-I M;F-L=61E(#QI;W-T<F5A;3X-"@T*=7-I;F<@;F%M97-P86-E(&)O;W-T.PT* M=7-I;F<@;F%M97-P86-E('-T9#L-"@T*;75T97@@<75E=65?;75T97@[#0IC M;VYS="!I;G0@<75E=65?<VEZ92 @(" @/2 U,# [#0IC;VYS="!I;G0@=&]T M86Q?96QE;65N=',@/2!Q=65U95]S:7IE("H@,3 P,# P.PT*#0IT96UP;&%T M93P@='EP96YA;64@475E=64@/@T*:6YL:6YE(&)O;VP@:7-?9G5L;"@@8V]N M<W0@475E=64F(&,@*0T*>PT*(" @(')E='5R;B!C+G-I>F4H*2 ]/2!Q=65U M95]S:7IE.PT*?0T*#0IT96UP;&%T93P@='EP96YA;64@475E=64@/@T*8VQA M<W,@<F5A9&5R#0I[#0H@(" @='EP961E9B!T>7!E;F%M92!1=65U93HZ=F%L M=65?='EP92!4.PT*(" @(%%U975E)B @<3L-"B @(" -"G!U8FQI8SH-"B @ M("!R96%D97(H(%%U975E)B!Q("D@.B!Q*'$I('L@?0T*(" @( T*(" @('9O M:60@;W!E<F%T;W(H*2@I#0H@(" @>PT*(" @(" @("!I;G0@<F5A9%]E;&5M M96YT<R ](# [#0H@(" @(" @( T*(" @(" @("!W:&EL92@@<F5A9%]E;&5M M96YT<R \('1O=&%L7V5L96UE;G1S("D@(" -"B @(" @(" @>PT*(" @(" @ M(" @(" @:68H("%Q+F5M<'1Y*"D@*2 @( T*(" @(" @(" @(" @>PT*(" @ M(" @(" @(" @(" @('L-"B @(" @(" @(" @(" @(" @(" @;75T97@Z.G-C M;W!E9%]L;V-K(&QO8VLH(&UU=&5X("D[#0H@(" @(" @(" @(" @(" @(" @ M(%0@=" ]('$N8F%C:R@I.PT*(" @(" @(" @(" @(" @(" @("!Q+G!O<%]B M86-K*"D[#0H@(" @(" @(" @(" @(" @?0T*(" @(" @(" @(" @(" @("LK M<F5A9%]E;&5M96YT<SL-"B @(" @(" @(" @('T-"B @(" @(" @(" @(&5L M<V4-"B @(" @(" @(" @(" @("!R96%D7W1H<F5A9"@I+3YY:65L9"@I.PT* M(" @(" @("!]#0H@(" @?0T*(" @( T*(" @('-T871I8R!T:')E860J)B!R M96%D7W1H<F5A9"@I#0H@(" @>PT*(" @(" @("!S=&%T:6,@=&AR96%D*B!T M(#T@,#L-"B @(" @(" @<F5T=7)N('0[#0H@(" @?0T*?3L-"@T*=&5M<&QA M=&4\('1Y<&5N86UE(%%U975E(#X-"F-L87-S('=R:71E<@T*>PT*(" @('1Y M<&5D968@='EP96YA;64@475E=64Z.G9A;'5E7W1Y<&4@5#L-"B @("!1=65U M928@('$[#0H@(" @#0IP=6)L:6,Z#0H@(" @=W)I=&5R*"!1=65U928@<2 I M(#H@<2AQ*2![('T-"B @(" -"B @("!V;VED(&]P97)A=&]R*"DH*0T*(" @ M('L-"B @(" @(" @:6YT('=R:71T96Y?96QE;65N=',@/2 P.PT*(" @(" @ M("!W:&EL92@@=W)I='1E;E]E;&5M96YT<R \('1O=&%L7V5L96UE;G1S("D- M"B @(" @(" @>PT*(" @(" @(" @(" @:68H("%I<U]F=6QL*"!Q("D@*0T* M(" @(" @(" @(" @>PT*(" @(" @(" @(" @(" @('L-"B @(" @(" @(" @ M(" @(" @(" @;75T97@Z.G-C;W!E9%]L;V-K(&QO8VLH(&UU=&5X("D[#0H@ M(" @(" @(" @(" @(" @(" @('$N<'5S:%]B86-K*"!4*"D@*3L-"B @(" @ M(" @(" @(" @("!]#0H@(" @(" @(" @(" @(" @*RMW<FET=&5N7V5L96UE M;G1S.PT*(" @(" @(" @(" @?0T*(" @(" @(" @(" @96QS92 @(" -"B @ M(" @(" @(" @(" @("!W<FET95]T:')E860H*2T^>6EE;&0H*3L-"B @(" @ M(" @?0T*(" @('T-"B @(" @(" @#0H@(" @<W1A=&EC('1H<F5A9"HF('=R M:71E7W1H<F5A9"@I#0H@(" @>PT*(" @(" @("!S=&%T:6,@=&AR96%D*B!T M(#T@,#L-"B @(" @(" @<F5T=7)N('0[#0H@(" @?0T*?3L-"@T*#0IT96UP M;&%T93P@='EP96YA;64@475E=64@/@T*=F]I9"!F:69O7W1E<W0H*0T*>PT* M(" @('!R;V=R97-S7W1I;65R('0[#0H@(" @475E=64@('$H('%U975E7W-I M>F4@*3L-"B @("!R96%D97(\475E=64^(')E861E<B@@<2 I.PT*(" @('=R M:71E<CQ1=65U93X@=W)I=&5R*"!Q("D[(" @( T*(" @('1H<F5A9"!R96%D M*"!R96%D97(@*3L-"B @("!T:')E860@=W)I=&4H('=R:71E<B I.PT*(" @ M(')E861E<BYR96%D7W1H<F5A9"@I(#T@)G)E860[#0H@(" @=W)I=&5R+G=R M:71E7W1H<F5A9"@I(#T@)G=R:71E.PT*(" @(" @(" -"B @("!R96%D+FIO M:6XH*3L-"B @("!W<FET92YJ;VEN*"D[(" @(" @(" @(" -"@T*?0T*#0II M;G0@;6%I;B@I#0I[#0H@(" @8V]U=" \/" B7&X@8VER8W5L87)?8G5F9F5R M/&EN=#XZ("([#0H@(" @9FEF;U]T97-T/"!C:7)C=6QA<E]B=69F97(\:6YT M/B ^*"D[#0H@(" @#0H@(" @8V]U=" \/" B7&X@9&5Q=64\:6YT/CH@(CL- M"B @("!F:69O7W1E<W0\(&1E<75E/&EN=#X@/B@I.PT*#0H@(" @8V]U=" \ M/" B7&X@8VER8W5L87)?8G5F9F5R/'-T<FEN9SXZ("([#0H@(" @9FEF;U]T M97-T/"!C:7)C=6QA<E]B=69F97(\<W1R:6YG/B ^*"D[#0H@(" @#0H@(" @ M8V]U=" \/" B7&X@9&5Q=64\<W1R:6YG/CH@(CL-"B @("!F:69O7W1E<W0\ K(&1E<75E/'-T<FEN9SX@/B@I.PT*#0H@(" @<F5T=7)N(# [#0I]#0H-"@`` ` end

On Fri, 12 Mar 2004 21:06:16 +1100, "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote:
What is your evaluation of the potential usefulness of the library? ---------------------------------------------------------------- I'm in doubt. The library has efficiency as its hallmark. The rationale says that it gives efficient FIFO queue. So I tried to compare it with std::deque, and here is my results of an /O2 build with vc71:
$ ./cbuffer_vs_deque.exe
circular_buffer<int>: 2.15 s
deque<int>: 2.84 s
circular_buffer<string>: 20.59 s
deque<string>: 18.64 s
The test program is attached; I might have made an error, but if I have not, then I don't see the claim as true. In that case I need to see some more evidence about why the speed advantages should be so good. It also make me wonder if there is any need for circular_buffer_space_optimized.
Your code does have at least two major errors: mutex::scoped_lock lock( mutex ); is a function declaration (demonstrating the danger of C++ and "using namespace boost"). You meant: mutex::scoped_lock lock(queue_mutex); Also, you have implemented a stack rather than a queue; the reader should use front() and pop_front(). This didn't make much difference to the benchmark. You should also probably using condition variables rather than thread::yield - thread::yield is rarely needed in properly written multithreaded code (although I've been lucky enough to write most of my threading code in a realtime operating system with deterministic scheduling). But in any case, your benchmark does prove a point - circular_buffer seems a bit unnecessary if you have a properly implemented std::deque, as found in Dinkumware's library for one. No ongoing memory allocation has to happen in std::deque either! This is because it can use a circular buffer for the map of blocks, and not throw away empty blocks. Tom

"Tom Widmer" <tom_usenet@hotmail.com> wrote in message news:tcl350tm80fqpbma96j3jcsa6i6o3t9dm6@4ax.com... [snip]
Your code does have at least two major errors:
mutex::scoped_lock lock( mutex ); is a function declaration (demonstrating the danger of C++ and "using namespace boost"). You meant: mutex::scoped_lock lock(queue_mutex);
Doh! I dont get why this cold compile at all!
Also, you have implemented a stack rather than a queue; the reader should use front() and pop_front(). This didn't make much difference to the benchmark.
yeah it doesn't.
You should also probably using condition variables rather than thread::yield - thread::yield is rarely needed in properly written multithreaded code (although I've been lucky enough to write most of my threading code in a realtime operating system with deterministic scheduling).
Ok, I'm no MT expert, but I haven't seen any examples of how to do this. It would explain why you have to hack so much to call yield :-)
But in any case, your benchmark does prove a point - circular_buffer seems a bit unnecessary if you have a properly implemented std::deque, as found in Dinkumware's library for one. No ongoing memory allocation has to happen in std::deque either! This is because it can use a circular buffer for the map of blocks, and not throw away empty blocks.
ok. br Thorsten

Hi Thorsten! --- Thorsten Ottosen <nesotto@cs.auc.dk> wrote:
I'm in doubt. The library has efficiency as its hallmark. The rationale says that it gives efficient FIFO queue. So I tried to compare it with std::deque, and here is my results of an /O2 build with vc71:
$ ./cbuffer_vs_deque.exe
circular_buffer<int>: 2.15 s
deque<int>: 2.84 s
circular_buffer<string>: 20.59 s
deque<string>: 18.64 s
I compiled your test and made some experiments with it. My results are: circular_buffer<int>: 2.84 s deque<int>: 3.84 s circular_buffer<string>: 27.65 s deque<string>: 25.65 s struct test_struct { double d; }; circular_buffer<test_struct>: 3.15 s deque<test_struct>: 4.22 s struct test_struct2 { double d1; double d2; double d3; double d4; double d5; }; circular_buffer<test_struct2>: 7.67 s deque<test_struct2>: 6.47 s It seems to me that if you use circular_buffer for storing "small" elements the circular_buffer is about 30% faster than deque. If you use larger elements the deque is faster about 10%. (I did also experiments with buffer size, but the results were about the same.) I don't have an explanation for such behavour. (Does anyone have?) So, the result is: the circular_buffer is more effective for storing primitive types. Btw, at the early stages of library developmet I implemented the circular_buffer as std::deque adaptor. The problem was that there was no control over iterator invalidation. Best regards, Jan __________________________________ Do you Yahoo!? Yahoo! Mail - More reliable, more storage, less spam http://mail.yahoo.com

Hi Jan, The attached file corrects the mutex and fifo error in the first.
It seems to me that if you use circular_buffer for storing "small" elements the circular_buffer is about 30% faster than deque. If you use larger elements the deque is faster about 10%.
So, the result is: the circular_buffer is more effective for storing primitive types.
Could it be that your special treatment of small types makes this difference? I guess a deque cannot guarantee sequential data. Or could it with a special allocator? br Thorsten begin 666 cbuffer_vs_deque.cpp M(V1E9FEN92!"3T]35%]$25-!0DQ%7T-"7T1%0E5'(#$-"@T*(VEN8VQU9&4@ M(F)O;W-T+V-I<F-U;&%R7V)U9F9E<BYH<' B#0HC:6YC;'5D92 \8F]O<W0O M<')O9W)E<W,N:'!P/@T*(VEN8VQU9&4@/&)O;W-T+W1H<F5A9"]T:')E860N M:'!P/@T*(VEN8VQU9&4@/&1E<75E/@T*(VEN8VQU9&4@/'-T<FEN9SX-"B-I M;F-L=61E(#QI;W-T<F5A;3X-"@T*=7-I;F<@;F%M97-P86-E(&)O;W-T.PT* M=7-I;F<@;F%M97-P86-E('-T9#L-"@T*;75T97@@<75E=65?;75T97@[#0IC M;VYS="!I;G0@<75E=65?<VEZ92 @(" @/2 U,# [#0IC;VYS="!I;G0@=&]T M86Q?96QE;65N=',@/2!Q=65U95]S:7IE("H@,3 P,# P.PT*#0IT96UP;&%T M93P@='EP96YA;64@475E=64@/@T*:6YL:6YE(&)O;VP@:7-?9G5L;"@@8V]N M<W0@475E=64F(&,@*0T*>PT*(" @(')E='5R;B!C+G-I>F4H*2 ]/2!Q=65U M95]S:7IE.PT*?0T*#0IT96UP;&%T93P@='EP96YA;64@475E=64@/@T*8VQA M<W,@<F5A9&5R#0I[#0H@(" @='EP961E9B!T>7!E;F%M92!1=65U93HZ=F%L M=65?='EP92!4.PT*(" @(%%U975E)B @<3L-"B @(" -"G!U8FQI8SH-"B @ M("!R96%D97(H(%%U975E)B!Q("D@.B!Q*'$I('L@?0T*(" @( T*(" @('9O M:60@;W!E<F%T;W(H*2@I#0H@(" @>PT*(" @(" @("!I;G0@<F5A9%]E;&5M M96YT<R ](# [#0H@(" @(" @( T*(" @(" @("!W:&EL92@@<F5A9%]E;&5M M96YT<R \('1O=&%L7V5L96UE;G1S("D@(" -"B @(" @(" @>PT*(" @(" @ M(" @(" @:68H("%Q+F5M<'1Y*"D@*2 @( T*(" @(" @(" @(" @>PT*(" @ M(" @(" @(" @(" @('L-"B @(" @(" @(" @(" @(" @(" @;75T97@Z.G-C M;W!E9%]L;V-K(&QO8VLH('%U975E7VUU=&5X("D[#0H@(" @(" @(" @(" @ M(" @(" @(%0@=" ]('$N9G)O;G0H*3L-"B @(" @(" @(" @(" @(" @(" @ M<2YP;W!?9G)O;G0H*3L-"B @(" @(" @(" @(" @("!]#0H@(" @(" @(" @ M(" @(" @*RMR96%D7V5L96UE;G1S.PT*(" @(" @(" @(" @?0T*(" @(" @ M(" @(" @96QS90T*(" @(" @(" @(" @(" @(')E861?=&AR96%D*"DM/GEI M96QD*"D[#0H@(" @(" @('T-"B @("!]#0H@(" @#0H@(" @<W1A=&EC('1H M<F5A9"HF(')E861?=&AR96%D*"D-"B @("![#0H@(" @(" @('-T871I8R!T M:')E860J('0@/2 P.PT*(" @(" @("!R971U<FX@=#L-"B @("!]#0I].PT* M#0IT96UP;&%T93P@='EP96YA;64@475E=64@/@T*8VQA<W,@=W)I=&5R#0I[ M#0H@(" @='EP961E9B!T>7!E;F%M92!1=65U93HZ=F%L=65?='EP92!4.PT* M(" @(%%U975E)B @<3L-"B @(" -"G!U8FQI8SH-"B @("!W<FET97(H(%%U M975E)B!Q("D@.B!Q*'$I('L@?0T*(" @( T*(" @('9O:60@;W!E<F%T;W(H M*2@I#0H@(" @>PT*(" @(" @("!I;G0@=W)I='1E;E]E;&5M96YT<R ](# [ M#0H@(" @(" @('=H:6QE*"!W<FET=&5N7V5L96UE;G1S(#P@=&]T86Q?96QE M;65N=',@*0T*(" @(" @("![#0H@(" @(" @(" @("!I9B@@(6ES7V9U;&PH M('$@*2 I#0H@(" @(" @(" @("![#0H@(" @(" @(" @(" @(" @>PT*(" @ M(" @(" @(" @(" @(" @("!M=71E>#HZ<V-O<&5D7VQO8VL@;&]C:R@@<75E M=65?;75T97@@*3L-"B @(" @(" @(" @(" @(" @(" @<2YP=7-H7V)A8VLH M(%0H*2 I.PT*(" @(" @(" @(" @(" @('T-"B @(" @(" @(" @(" @(" K M*W=R:71T96Y?96QE;65N=',[#0H@(" @(" @(" @("!]#0H@(" @(" @(" @ M("!E;'-E(" @( T*(" @(" @(" @(" @(" @('=R:71E7W1H<F5A9"@I+3YY M:65L9"@I.PT*(" @(" @("!]#0H@(" @?0T*(" @(" @(" -"B @("!S=&%T M:6,@=&AR96%D*B8@=W)I=&5?=&AR96%D*"D-"B @("![#0H@(" @(" @('-T M871I8R!T:')E860J('0@/2 P.PT*(" @(" @("!R971U<FX@=#L-"B @("!] M#0I].PT*#0H-"G1E;7!L871E/"!T>7!E;F%M92!1=65U92 ^#0IV;VED(&9I M9F]?=&5S="@I#0I[#0H@(" @<')O9W)E<W-?=&EM97(@=#L-"B @("!1=65U M92 @<2@@<75E=65?<VEZ92 I.PT*(" @(')E861E<CQ1=65U93X@<F5A9&5R M*"!Q("D[#0H@(" @=W)I=&5R/%%U975E/B!W<FET97(H('$@*3L@(" @#0H@ M(" @=&AR96%D(')E860H(')E861E<B I.PT*(" @('1H<F5A9"!W<FET92@@ M=W)I=&5R("D[#0H@(" @<F5A9&5R+G)E861?=&AR96%D*"D@/2 F<F5A9#L- M"B @("!W<FET97(N=W)I=&5?=&AR96%D*"D@/2 F=W)I=&4[#0H@(" @(" @ M( T*(" @(')E860N:F]I;B@I.PT*(" @('=R:71E+FIO:6XH*3L@(" @(" @ M(" @( T*#0I]#0H-"FEN="!M86EN*"D-"GL-"B @("!C;W5T(#P\(")<;B!C M:7)C=6QA<E]B=69F97(\:6YT/CH@(CL-"B @("!F:69O7W1E<W0\(&-I<F-U M;&%R7V)U9F9E<CQI;G0^(#XH*3L-"B @(" -"B @("!C;W5T(#P\(")<;B!D M97%U93QI;G0^.B B.PT*(" @(&9I9F]?=&5S=#P@9&5Q=64\:6YT/B ^*"D[ M#0H-"B @("!C;W5T(#P\(")<;B!C:7)C=6QA<E]B=69F97(\<W1R:6YG/CH@ M(CL-"B @("!F:69O7W1E<W0\(&-I<F-U;&%R7V)U9F9E<CQS=')I;F<^(#XH M*3L-"B @(" -"B @("!C;W5T(#P\(")<;B!D97%U93QS=')I;F<^.B B.PT* M(" @(&9I9F]?=&5S=#P@9&5Q=64\<W1R:6YG/B ^*"D[#0H-"B @("!R971U ,<FX@,#L-"GT-"@T* ` end

Hi Thorsten!
Could it be that your special treatment of small types makes this difference? The test_struct is not a primitive type and for this type the optimization does not apply.
I guess a deque cannot guarantee sequential data. Or could it with a special allocator?
I don't understand this question. Jan __________________________________ Do you Yahoo!? Yahoo! Mail - More reliable, more storage, less spam http://mail.yahoo.com

Hi Jan,
I guess a deque cannot guarantee sequential data. Or could it with a special allocator?
I don't understand this question.
I mean a vector and a circular_buffer can pass a pointer to their data to C-style functions. A deque cannot because it's data is not guaranteed to be sequential. The question is if a deque could provide this guarantee somehow using eg. a special allocator. br Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message news:c31al8$bk8$1@sea.gmane.org...
Hi Jan,
I guess a deque cannot guarantee sequential data. Or could it with a special allocator?
I don't understand this question.
I mean a vector and a circular_buffer can pass a pointer to their data to C-style functions. A deque cannot because it's data is not guaranteed to be sequential. The question is if a deque could provide this guarantee somehow using eg. a special allocator.
A deque can't. From the way it is specified, it more-or-less has to be implemented as several fixed-size buffers, plus a control structure to tell which buffer is being used when. A circular_buffer really can't be passed to a C-style function either. What if the data starts in the middle and wraps around the end? Joe Gottman

I guess a deque cannot guarantee sequential data. Or could it with a special allocator? I don't understand this question.
I mean a vector and a circular_buffer can pass a pointer to their data to C-style functions. A deque cannot because it's data is not guaranteed to be sequential. The question is if a deque could provide this guarantee somehow using eg. a special allocator.
A deque can't. From the way it is specified, it more-or-less has to be implemented as several fixed-size buffers, plus a control structure to tell which buffer is being used when.
then what about one big fixed-sized buffer? Can't the allocator determine the buffer size?
A circular_buffer really can't be passed to a C-style function either. What if the data starts in the middle and wraps around the end?
You would have to call data(), right? br Thorsten

--- Thorsten Ottosen <nesotto@cs.auc.dk> wrote:
I guess a deque cannot guarantee sequential data. Or could it with a special allocator? I don't understand this question.
I mean a vector and a circular_buffer can pass a pointer to their data to C-style functions. A deque cannot because it's data is not guaranteed to be sequential. The question is if a deque could provide this guarantee somehow using eg. a special allocator.
A deque can't. From the way it is specified, it more-or-less has to be implemented as several fixed-size buffers, plus a control structure to tell which buffer is being used when.
then what about one big fixed-sized buffer? Can't the allocator determine the buffer size?
A circular_buffer really can't be passed to a C-style function either. What if the data starts in the middle and wraps around the end?
You would have to call data(), right?
Yes you're right. I just want to mention one more problem with deque. With deque as underlying container of the circular_buffer, you have no or very limited control over iterator invalidation. Best regards, Jan __________________________________ Do you Yahoo!? Yahoo! Mail - More reliable, more storage, less spam http://mail.yahoo.com
participants (4)
-
Jan Gaspar
-
Joe Gottman
-
Thorsten Ottosen
-
Tom Widmer