Topic: Benefit of new style for loop N1796
Author: kwikius <andy@servocomm.freeserve.co.uk>
Date: Wed, 25 Apr 2007 04:40:58 CST Raw View
On 20 Mar, 18:49, "Douglas Gregor" <doug.gre...@gmail.com> wrote:
> On Mar 19, 1:48 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
> > The complexity of current C++ compilers is a known problem. Its
> > difficult to predict which straw it is that will break the camels
> > back.
>
> <Jedi mind trick>This is not the straw you are looking for.</Jedi mind
> trick>
I don't understand the above comment and annotations etc. Perhaps you
could spell out less cryptically what you mean here?
regards
Andy Little
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Douglas Gregor <doug.gregor@gmail.com>
Date: Thu, 26 Apr 2007 11:06:27 CST Raw View
On Apr 25, 6:40 am, kwikius <a...@servocomm.freeserve.co.uk> wrote:
> On 20 Mar, 18:49, "Douglas Gregor" <doug.gre...@gmail.com> wrote:
>
> > On Mar 19, 1:48 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
> > > The complexity of current C++ compilers is a known problem. Its
> > > difficult to predict which straw it is that will break the camels
> > > back.
>
> > <Jedi mind trick>This is not the straw you are looking for.</Jedi mind
> > trick>
>
> I don't understand the above comment and annotations etc. Perhaps you
> could spell out less cryptically what you mean here?
*Sigh* It was a joke. You claimed that the addition of little features
is going to cause a break-down somewhere in C++ compilers due to their
implementation complexity. I completely disagree... small features
won't break a C++ compiler that can already handle most of C++98. If
you're going to be concerned about implementation complexity in C++0x,
there are much larger proposals that have a more fundamental effect on
the way C++ compilers process templates and optimize code.
The Jedi mind trick refers to Star Wars episode 4; look for "These
aren't the droids you're looking for" at http://www.imdb.com/title/tt0076759/quotes.
It was my subtle way of telling you that the range-based for loop
isn't going to be the straw that breaks any C++ compiler-camel's back.
- Doug
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Mon, 19 Mar 2007 23:03:03 CST Raw View
On Mar 19, 8:35 pm, ricec...@gehennom.invalid (Marcus Kwok) wrote:
> Richard Smith <rich...@ex-parrot.com> wrote:
> > Is there a reason why the
>
> > for ( int x : v ) {}
>
> > syntax was chosen instead of the existing C++/CLI syntax?
>
> > for each ( int x in v ) {}
>
> I think the issue with the C++/CLI syntax is that "for each" and "in"
> are context-sensitive keywords[0], and as far as I know, standard C++
> has context-free keywords.
Nearly. "in" is a context-sensitive keyword, but "for each" is a
whitespace keyword. (I.e. "each" is not a keyword, but "for each" is
a single keyword.)
But that doesn't answer the question: why was the former chosen over
the latter? What's wrong with ISO C++ using context-sensitive and/or
whitespace keywords? It clearly can be implemented as it is in use in
MSVC's C++/CLI implementation. Using the same syntax as C++/CLI seems
to make sense if there aren't any good reasons not to.
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Tue, 20 Mar 2007 10:04:42 CST Raw View
On 19 Mar, 15:20, thorsten.otto...@dezide.com (Thorsten Ottosen)
wrote:
> just a you can make an indirection iterator, I guess you can make an
> "outdirection" irerator:
>
> for( auto iter : make_outdirection_range( vec ) )
> iter->foo();
OK I don't understand how the above works but if it provides access
the iterator , great...
First, apologies as I seem to keep laying into your proposals.
FWIW I have the same problems with the lambda proposals.
Both aim to help to make writing expressions more concise.
The lambda proposal is probably much more complex to implement.
One use of lambda style functions
is also in loops over a sequence. IOW some aspects of the two problems
are closely related...
An alternative approach to the container loop issue might be by
applying an operator (e.g using 'for' as an operator )to the sequence
itself .
E.g in your above example :
for( auto iter : make_outdirection_range( vec ) )
iter->foo();
turns into:
(for make_outdirection_range(vec))->foo();
e.g,rather than by placeholders, local names etc the operator and
sequence name replaces the placeholder/local variable.
The same range Concepts would apply to args to the operator
e.g // 'for' again used as an operator
std::cout << for vec << ( (for make_direction_range(vec) !=
vec.end()-1) ? ' ' : '\n');
similarly The example in the lambda proposal (p 3 of n1958) currently
expressed:
void foo(
myvec& v, const myset& s, int a) {
// ...
v.erase(
std::remove_if(v.begin(), v.end(),
bool(int x) {
return std::abs(x) < a
&& s.find(x) != s.end();
}),
v.end()
);
}
becomes with some overload/alias remove_if for lambda on sequence
concept..
template <Range Seq>
typename Seq::iterator remove_if( bool for Seq );
---> typename Seq::iterator( Seq.begin(),Seq.end(), {{{ Predicate
defined in expression }}})
use as ..
void foo( myvec & v, const myset & s, int a)
{
v.erase(
std::remove_if(
// return is given by sig above,
// Seq deduced as v from expression
{
#if (1)
return std::abs(for v) < a && s.find(for v) != s.end() ;
#else
// or local variable version
auto vv = for v;
return std::abs(vv) < a && s.find(vv) != s.end() ;
#endif
}
),
v.end()
);
}
The gold standard in n1968 :
find if(v.begin(), v.end(), 1 < i);
becomes
find_if( 1 < (for v) );
Apologies if The above stuff is somewhat half baked...
regards
Andy Little
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Tue, 20 Mar 2007 11:49:33 CST Raw View
On Mar 19, 1:48 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
> On 17 Mar, 18:43, "Douglas Gregor" <doug.gre...@gmail.com> wrote:> On Mar 17, 12:09 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
>
> <...>
>
> > [snip examples]
> > > A major disadvantage of the new style for loop is that you cannot
> > > query the iterator, so limiting its use to only uniform actions on
> > > each element. The limits of use of this can be seen from the given
> > > example.In a real example one would presumably apply a separator
> > > between the output values, leaving the untidy issue of adding an
> > > unnecessary separator at the end.( which could be solved only by being
> > > able to query the iterator)
>
> > Why not just use a "bool" flag?
>
> I don't understand what you mean?
You're worried about printing extra separators:
bool first = true;
for (const strin s : list_of_strings) {
if (!first) std::cout << ", ";
first = false;
std::cout << s;
}
>There are many aspects to querying
> the iterator, dependent on the type of iterator:
And for those cases, one should use an iterator loop. We're not taking
iterator loops away... those loops are the general case, which has to
be there. The range-based for loop is there to make the simple cases
simple.
> > > The proposal also couples language and
> > > library features in a way that is not done anywhere else in the
> > > language and therefore feels somewhat out of the spirit of C++ to me
> > > anyway.
>
> > type_info does this already, as do type traits.
>
> These facilities are structural. They work uniformly on raw types.
> The for loop works only for one high level concept. IOW these examples
> are not equivalent.
The for loop works uniformly on every type that meets the requirements
of a concept.
> This proposal is more akin the D approach of enshrining complex in the
> language.
I disagree. D's complex is a small, predefined set of types that gets
special support from the compiler. There's no level of user
extensibility that I know of. The for-loop approach enshrines a con
concept in the language, yes, but it's a very open---and very
powerful---way of integrating user extensions. Want to make the loop
work with your data type? Write a concept map. Don't like the spelling
of the built-in "Range" concept? Create your own concept Seq, with a
concept map that makes each Seq into a Range.
I do agree that it's a different kind of language/library integration
than we've seen in C++ before, but I also think it's far, far better.
> The complexity of current C++ compilers is a known problem. Its
> difficult to predict which straw it is that will break the camels
> back.
<Jedi mind trick>This is not the straw you are looking for.</Jedi mind
trick>
> In practise implementing a feature involves a huge amount more work
> than just code. There is testing over a wide range of containers
> documentation and specification.
To date, I have not heard concerns about the implementability of the
for-loop proposal from any compiler implementers. They aren't shy
about telling us when a particular extension will be hard to implement
in their compiler, so either they've missed the proposal (doubtful) or
they aren't at all concerned about the implementation cost. Yes, there
is work beyond the initial implementation, and they account for that:
it took me a few hours to implement the full proposal, they'll
schedule a week for it. Given that the same implementers are
scheduling person-years to implement other features of C++0x (e.g.,
concepts), the implementability argument against this feature just
doesn't hold water.
Cheers,
Doug
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: thorsten.ottosen@dezide.com (Thorsten Ottosen)
Date: Tue, 20 Mar 2007 20:31:10 GMT Raw View
kwikius skrev:
> On 19 Mar, 15:20, thorsten.otto...@dezide.com (Thorsten Ottosen)
> wrote:
>
>> just a you can make an indirection iterator, I guess you can make an
>> "outdirection" irerator:
>>
>> for( auto iter : make_outdirection_range( vec ) )
>> iter->foo();
>
> OK I don't understand how the above works but if it provides access
> the iterator , great...
you simply need an iterator adaptor where operator*() returns the
iterator itself instead of giving an indirection.
make_outdirection_range() then returns the orignal begin/end pair
of vec wrapped in that iterator adaptor.
> First, apologies as I seem to keep laying into your proposals.
no apology needed.
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: David Abrahams <dave@boost-consulting.com>
Date: Tue, 20 Mar 2007 17:16:18 CST Raw View
on Tue Mar 20 2007, thorsten.ottosen-AT-dezide.com (Thorsten Ottosen) wrote:
> kwikius skrev:
>> On 19 Mar, 15:20, thorsten.otto...@dezide.com (Thorsten Ottosen)
>> wrote:
>>
>>> just a you can make an indirection iterator, I guess you can make an
>>> "outdirection" irerator:
>>>
>>> for( auto iter : make_outdirection_range( vec ) )
>>> iter->foo();
>>
>> OK I don't understand how the above works but if it provides access
>> the iterator , great...
>
> you simply need an iterator adaptor where operator*() returns the
> iterator itself instead of giving an indirection.
Try boost::counting_iterator<...>.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Sat, 17 Mar 2007 10:09:50 CST Raw View
I'm wondering how much value the for proposal (N1796) adds compared to
the implementation complexity.
Consider the motivating example.
vector<int> vec = ...;
for( int i : vec )
std::cout << i;
The current language this can be expressed (most obviously and
ignoring performance) by:
for (std::vector<int>::iterator iter = vec.begin(); iter!= vec.end();+
+iter)
std::cout << *iter;
The main inconvenience here is typing out the member iterator, Using
auto most of this inconvenience goes:
for ( auto iter= vec.begin(); iter!= vec.end();++iter)
std::cout << *iter;
A major disadvantage of the new style for loop is that you cannot
query the iterator, so limiting its use to only uniform actions on
each element. The limits of use of this can be seen from the given
example.In a real example one would presumably apply a separator
between the output values, leaving the untidy issue of adding an
unnecessary separator at the end.( which could be solved only by being
able to query the iterator) The proposal also couples language and
library features in a way that is not done anywhere else in the
language and therefore feels somewhat out of the spirit of C++ to me
anyway.
Each additional language feature has a cost in that it must be
implemented. In this case is the benfit worth it, especially
considering the large number of other features that must be
implemented?
regards
Andy Little
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Sat, 17 Mar 2007 12:43:21 CST Raw View
On Mar 17, 12:09 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
> I'm wondering how much value the for proposal (N1796) adds compared to
> the implementation complexity.
>
> Consider the motivating example.
>
> vector<int> vec = ...;
>
> for( int i : vec )
> std::cout << i;
[snip examples]
> A major disadvantage of the new style for loop is that you cannot
> query the iterator, so limiting its use to only uniform actions on
> each element. The limits of use of this can be seen from the given
> example.In a real example one would presumably apply a separator
> between the output values, leaving the untidy issue of adding an
> unnecessary separator at the end.( which could be solved only by being
> able to query the iterator)
Why not just use a "bool" flag?
> The proposal also couples language and
> library features in a way that is not done anywhere else in the
> language and therefore feels somewhat out of the spirit of C++ to me
> anyway.
type_info does this already, as do type traits.
> Each additional language feature has a cost in that it must be
> implemented. In this case is the benfit worth it, especially
> considering the large number of other features that must be
> implemented?
I'd suggest looking at one of the newer, concept-based formulations of
the range-based "for" loop. They're a lot cleaner, more robust, and
easier to understand. Check out one of:
N2049: My two-page discussion of using concepts for the range-based
for loop
N2196: Thorsten's revised wording for the range-based for loop
Is the feature worth the cost? I think so, because the lack of a
simple way to iterate over all of the elements in a container is one
small thing that makes it just slightly too hard to write code, and
also has the effect of making C++ look a bit dated. The feature is
quite simple to implement; it's probably about 100 lines of code in
ConceptGCC right now, and took only a few hours to do, so I wouldn't
worry about the implementation cost.
Cheers,
Doug
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Andrei Polushin" <polushin@gmail.com>
Date: Sun, 18 Mar 2007 23:26:55 CST Raw View
kwikius wrote:
> I'm wondering how much value the for proposal (N1796) adds compared to
> the implementation complexity.
>
> Consider the motivating example.
>
> vector<int> vec = ...;
>
> for( int i : vec )
> std::cout << i;
>
> [...] The proposal also couples language and
> library features in a way that is not done anywhere else in the
> language and therefore feels somewhat out of the spirit of C++ to me
> anyway.
That's an important objection. The range-based for loop could have
a possible generalization when we will have some kind of lambda
proposal accepted.
The range-based loop is a combination of for_each() standard
algorithm, and a local anonymous function. So we may just vary
a function invocation syntax in a way similar to N1796:
vector<int> vec;
/-------------------------- algorithm function name
|
| /----------------- local function parameter(s)
| __|__
| / \ /---------- algorithm parameter(s)
| | | |
for_each (int& i : vec) <------ algorithm invocation
{ --\
cout << i << endl; |---- local function body inlined
} --/
Where for_each() is declared in the way that allows lambda for the
last parameter, e.g.:
template <class C, class F, class V>
void for_each(C cont, F func(V val) = <<lambda )
{
for (auto it = cont.begin(); it != cont.end(); ++it) {
func(val);
}
}
FWIW, the examples of "for-loop" syntax applied to other algorithms:
vector<int> vec;
// sort(vec.begin(), vec.end(), <<lambda )
sort(int& a, int& b : vec) {
yield a < b; // bool lambda(int& a, int& b)
}
// long n = accumulate(vec.begin(), vec.end(), 0, <<lambda )
long n = accumulate(int& a, int& b : vec, 0) {
yield a * b; // int lambda(int& a, int& b)
}
(The above is NOT yet another lambda proposal, but just to confirm
that the new syntax of "for" loop could be a pure language feature,
less coupled with the standard library).
--
Andrei Polushin
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: thorsten.ottosen@dezide.com (Thorsten Ottosen)
Date: Mon, 19 Mar 2007 15:20:44 GMT Raw View
kwikius wrote:
> I'm wondering how much value the for proposal (N1796) adds compared to
> the implementation complexity.
> A major disadvantage of the new style for loop is that you cannot
> query the iterator, so limiting its use to only uniform actions on
> each element.
just a you can make an indirection iterator, I guess you can make an
"outdirection" irerator:
for( auto iter : make_outdirection_range( vec ) )
iter->foo();
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Mon, 19 Mar 2007 11:48:34 CST Raw View
On 17 Mar, 18:43, "Douglas Gregor" <doug.gre...@gmail.com> wrote:
> On Mar 17, 12:09 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
<...>
> [snip examples]
> > A major disadvantage of the new style for loop is that you cannot
> > query the iterator, so limiting its use to only uniform actions on
> > each element. The limits of use of this can be seen from the given
> > example.In a real example one would presumably apply a separator
> > between the output values, leaving the untidy issue of adding an
> > unnecessary separator at the end.( which could be solved only by being
> > able to query the iterator)
>
> Why not just use a "bool" flag?
I don't understand what you mean? There are many aspects to querying
the iterator, dependent on the type of iterator:
std::vector<T> vec;
for ( BOOST_AUTO( iter, vec.begin()); iter!= vec.end();){
if (iter == vec.begin())
std::cout << '(';
std::cout << *iter++;
if ((iter - vec.begin()) % 3){
std::cout << ' ';
}
else{
std::cout << ")\n";
if (iter != vec.end())
std::cout << '(';
}
}
> > The proposal also couples language and
> > library features in a way that is not done anywhere else in the
> > language and therefore feels somewhat out of the spirit of C++ to me
> > anyway.
>
> type_info does this already, as do type traits.
These facilities are structural. They work uniformly on raw types.
The for loop works only for one high level concept. IOW these examples
are not equivalent.
This proposal is more akin the D approach of enshrining complex in the
language.
> > Each additional language feature has a cost in that it must be
> > implemented. In this case is the benfit worth it, especially
> > considering the large number of other features that must be
> > implemented?
>
> I'd suggest looking at one of the newer, concept-based formulations of
> the range-based "for" loop. They're a lot cleaner, more robust, and
> easier to understand. Check out one of:
>
> N2049: My two-page discussion of using concepts for the range-based
> for loop
> N2196: Thorsten's revised wording for the range-based for loop
>
> Is the feature worth the cost? I think so, because the lack of a
> simple way to iterate over all of the elements in a container is one
> small thing that makes it just slightly too hard to write code, and
> also has the effect of making C++ look a bit dated. The feature is
> quite simple to implement; it's probably about 100 lines of code in
> ConceptGCC right now, and took only a few hours to do, so I wouldn't
> worry about the implementation cost.
The complexity of current C++ compilers is a known problem. Its
difficult to predict which straw it is that will break the camels
back.
In practise implementing a feature involves a huge amount more work
than just code. There is testing over a wide range of containers
documentation and specification.
regards
Andy Little
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Mon, 19 Mar 2007 11:50:14 CST Raw View
On Mar 17, 6:43 pm, "Douglas Gregor" <doug.gre...@gmail.com> wrote:
> On Mar 17, 12:09 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
> >
> > Each additional language feature has a cost in that it must be
> > implemented. In this case is the benfit worth it, especially
> > considering the large number of other features that must be
> > implemented?
>
> I'd suggest looking at one of the newer, concept-based formulations of
> the range-based "for" loop. They're a lot cleaner, more robust, and
> easier to understand. Check out one of:
>
> N2049: My two-page discussion of using concepts for the range-based
> for loop
> N2196: Thorsten's revised wording for the range-based for loop
Is there a reason why the
for ( int x : v ) {}
syntax was chosen instead of the existing C++/CLI syntax?
for each ( int x in v ) {}
It seems that as the latter syntax already exists and is in use, using
it for the concept-based for-loop would make sense too help write
generic code and eliminate an unnecessary difference between ISO C++
and C++/CLI. It shouldn't be incompatible with C++/CLI usage as a
concept_map can be written for IEnumerable subtypes.
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: ricecake@gehennom.invalid (Marcus Kwok)
Date: Mon, 19 Mar 2007 14:35:04 CST Raw View
Richard Smith <richard@ex-parrot.com> wrote:
> Is there a reason why the
>
> for ( int x : v ) {}
>
> syntax was chosen instead of the existing C++/CLI syntax?
>
> for each ( int x in v ) {}
>
> It seems that as the latter syntax already exists and is in use, using
> it for the concept-based for-loop would make sense too help write
> generic code and eliminate an unnecessary difference between ISO C++
> and C++/CLI. It shouldn't be incompatible with C++/CLI usage as a
> concept_map can be written for IEnumerable subtypes.
I think the issue with the C++/CLI syntax is that "for each" and "in"
are context-sensitive keywords[0], and as far as I know, standard C++
has context-free keywords.
[0] http://msdn2.microsoft.com/en-us/library/8d7y7wz6(VS.80).aspx
--
Marcus Kwok
Replace 'invalid' with 'net' to reply
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]