Topic: Why aren't constructor arguments allowed for new[]?
Author: Seungbeom Kim <musiphil@bawi.org>
Date: Thu, 26 Aug 2010 00:06:26 CST Raw View
The subject line says it all.
When these are possible for a non-array type A:
new A
new A()
new A(args)
these are possible:
new A[n]
new A[n]()
but not this:
new A[n](args) // an array of n "same" A(args) objects
What is the rationale for this?
I know that the solution is usually to use std::vector<A>(n, A(args)),
but I once was asked by a friend who hated that solution. I also know
that we could use std::malloc followed by std::uninitialized_fill_n,
but... wouldn't things be much simpler if we had new A[n](args)?
There have been several threads asking the same question, since 1990's,
but I haven't seen a satisfactory answer yet.
--
Seungbeom Kim
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Thu, 26 Aug 2010 13:08:35 CST Raw View
On Aug 26, 8:06 am, Seungbeom Kim <musip...@bawi.org> wrote:
> The subject line says it all.
> When these are possible for a non-array type A:
>
> new A
> new A()
> new A(args)
>
> these are possible:
>
> new A[n]
> new A[n]()
>
> but not this:
>
> new A[n](args) // an array of n "same" A(args) objects
>
> What is the rationale for this?
>
> I know that the solution is usually to use std::vector<A>(n, A(args)),
> but I once was asked by a friend who hated that solution. I also know
> that we could use std::malloc followed by std::uninitialized_fill_n,
> but... wouldn't things be much simpler if we had new A[n](args)?
Here some thoughts about possible reasons why this extension
might not be a good idea:
It's not immediately obvious how often the initializer expression
would be evaluated - each time for each array element?
Related to this question we have to consider situations where the
constructor can potentially modify it's arguments - this can already
be realized in C++03 via lvalue references to non-const or especially
in C++0x in the presence of rvalue-references (to non-const). Both
std::vector and uninitialized_fill_n have an clear interface that
prevent
an unintentional programmer error here, because the arguments is
required to be a const T& (A programmer could provide explicit
template
arguments to uninitialized_fill_n, but I have never seen such a code
and even if that code would exist, I would tag it as some very
intentional doing). Theoretically it would be possible to specify the
semantics of
new A[n](args)
to be equivalent to invoke this expression in the context of a
signature
template<class... Args>
void construct(Args&& args) {
new A[n](T(std::forward<Args>(args)...)); // pseudo-code
}
but I doubt that this special semantics would be obvious for the
programmer.
In regard to your suggestion to combine malloc and a proper
uninitialized_fill* function I would instead suggest to provide a
library solution basing on operator new[]:
template<class T, class... Args>
T* make_array_new(std::size_t n, Args&&... args)
{
T* result = ::operator new[](n);
try {
std::uninitialized_fill_n(result, n,
T(std::forward<Args>(args)...));
} catch(...) {
::operator delete[](result);
throw;
}
return result;
}
or alternatively on an allocator:
template<class T, class Alloc, class Size, class... Args>
T* allocate_array(Alloc&& alloc, Size n, Args&&... args)
{
T* result = alloc.allocate(n);
try {
// You would probably prefer to replace the following by a variant
that
// loops over the a result array and invokes alloc.construct. In
case
// of a an failure it would need to invoke alloc.destroy for all
constructed
// objects:
std::uninitialized_fill_n(result, n,
T(std::forward<Args>(args)...));
} catch(...) {
alloc.deallocate(result, n);
throw;
}
return result;
}
I decided for the perfect forwarding signature for the allocator as
well
to allow for providing an rvalue that can be immediately used.
Provide
an overload with const Alloc& if you like, but it would need to make
a copy of the allocator before using it (allocate/deallocate are
potentially
mutable functions).
HTH & Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Mathias Gaunard <loufoque@gmail.com>
Date: Fri, 27 Aug 2010 11:39:34 CST Raw View
On Aug 26, 7:06 am, Seungbeom Kim <musip...@bawi.org> wrote:
> I also know
> that we could use std::malloc followed by std::uninitialized_fill_n,
> but... wouldn't things be much simpler if we had new A[n](args)?
Why std::malloc and not ::operator new?
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Richard Corden <richard.corden@gmail.com>
Date: Fri, 27 Aug 2010 11:39:20 CST Raw View
Hi,
On 08/26/2010 09:08 PM, Daniel Kr=FCgler wrote:
> On Aug 26, 8:06 am, Seungbeom Kim<musip...@bawi.org> wrote:
>
[...]
>> but not this:
>>
>> new A[n](args) // an array of n "same" A(args) objects
>>
>
[...]
It's not immediately obvious how often the initializer expression
> would be evaluated - each time for each array element?
>
Evaluation order would have to be specified, but I'm not sure that it was a=
n
argument to ban the feature. Intuitively, the argument was been specified
once so it probably should be evaluated only once.
Related to this question we have to consider situations where the
> constructor can potentially modify it's arguments - this can already
> be realized in C++03 via lvalue references to non-const or especially
> in C++0x in the presence of rvalue-references (to non-const). Both
> std::vector and uninitialized_fill_n have an clear interface that
> prevent
> an unintentional programmer error here,
>
There are lots of places where the language doesn't provide for any
protection against programmer error - not sure why this case would have bee=
n
different.
The standard could add a sentence stating that if the constructor modifies
the initialiser then the behaviour is undefined, or alternatively it could
have worded the feature as for the equivalent vector initialization: v(10,
A(args)).
But then it begs the question, why not just use a vector? And so, with the
ability to get the behaviour for most cases using a vector it may have been
considered unnecessary to add the core language feature.
Cheers,
Richard
--
Richard
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: David Krauss <potswa@gmail.com>
Date: Sat, 28 Aug 2010 22:31:44 CST Raw View
On Aug 27, 12:39 pm, Richard Corden <richard.cor...@gmail.com> wrote:
> But then it begs the question, why not just use a vector? And so, with the
> ability to get the behaviour for most cases using a vector it may have been
> considered unnecessary to add the core language feature.
The reason for using an array and not a vector is always lack of copy-
constructability. Resource objects like files and threads cannot be
stored in a vector, and need an extra, pre-initialization, default-
constructed state to work with arrays.
In C++0x, they may be emplaced into a deque, which solves some of the
issues, but you still need an ugly dummy copy constructor.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Sun, 29 Aug 2010 19:27:56 CST Raw View
On 29 Aug., 06:31, David Krauss <pot...@gmail.com> wrote:
> On Aug 27, 12:39 pm, Richard Corden <richard.cor...@gmail.com> wrote:
>
> > But then it begs the question, why not just use a vector? And so, with the
> > ability to get the behaviour for most cases using a vector it may have been
> > considered unnecessary to add the core language feature.
>
> The reason for using an array and not a vector is always lack of copy-
> constructability. Resource objects like files and threads cannot be
> stored in a vector, and need an extra, pre-initialization, default-
> constructed state to work with arrays.
To be sure that we are still talking about this in the
context of this thread: So, is your implied assumption
that the hypothetically supported expression
new A[n](args)
would have the meaning that would solve this problem,
but std::vector does not? Currently I don't see how.
> In C++0x, they may be emplaced into a deque, which solves some of the
> issues, but you still need an ugly dummy copy constructor.
I'm not really sure what you intent to say here, but it
sounds to me as if you say that a type hosted within
a C++0x std::vector needs to satisfy the CopyConstructible
requirements. In C++0x std::thread objects or std::unique_ptr
objects can be hosted within a std::vector without problems.
I don't know, why you should need a deque for this and if
so, why you should still need a copy constructor.
You can either construct an empty std::vector and reserve
the required size followed by a sequence of push_back's
with rvalues or emplace_back's with whatever arguments
needs to add MoveConstructible items into the container.
Alternatively, you could feed the "sequence constructor"
template <class InputIterator>
vector(InputIterator first, InputIterator last, const Allocator& =
Allocator());
with an iterator range of std::move_iterator as a source.
So, again, there is no need for a copy constructor.
HTH & Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Dragan Milenkovic <dragan@plusplus.rs>
Date: Mon, 30 Aug 2010 12:06:36 CST Raw View
On 08/29/2010 06:31 AM, David Krauss wrote:
>
> On Aug 27, 12:39 pm, Richard Corden<richard.cor...@gmail.com> wrote:
>
>> But then it begs the question, why not just use a vector? And so, with the
>> ability to get the behaviour for most cases using a vector it may have been
>> considered unnecessary to add the core language feature.
>
> The reason for using an array and not a vector is always lack of copy-
> constructability. Resource objects like files and threads cannot be
> stored in a vector, and need an extra, pre-initialization, default-
> constructed state to work with arrays.
That doesn't seems like a complete solution to me. For example,
you cannot add or remove an object later. I would prefer using
a wrapper, maybe a smart pointer.
--
Dragan
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: David Krauss <potswa@gmail.com>
Date: Mon, 30 Aug 2010 13:30:22 CST Raw View
On Aug 29, 8:27 pm, Daniel Kr=FCgler <daniel.krueg...@googlemail.com>
wrote:
> On 29 Aug., 06:31, David Krauss <pot...@gmail.com> wrote:
>
> > On Aug 27, 12:39 pm, Richard Corden <richard.cor...@gmail.com> wrote:
>
> > > But then it begs the question, why not just use a vector? And so,
with the
> > > ability to get the behaviour for most cases using a vector it may hav=
e
been
> > > considered unnecessary to add the core language feature.
>
> > The reason for using an array and not a vector is always lack of copy-
> > constructability. Resource objects like files and threads cannot be
> > stored in a vector, and need an extra, pre-initialization, default-
> > constructed state to work with arrays.
>
> To be sure that we are still talking about this in the
> context of this thread: So, is your implied assumption
> that the hypothetically supported expression
>
> new A[n](args)
>
> would have the meaning that would solve this problem,
> but std::vector does not? Currently I don't see how.
No, I was getting a little off-topic, sorry. I don't think new A[n]
(args) solves anything, and I can't conceive of a good one-liner
syntax to initialize an array of distinct resource objects. You would
need to allocate, but not initialize, array storage, and then have
some kind of uninitialized_transform function with exception handling.
Anyone competent to jump through the hoops would choose a simpler
solution.
C++0x solves this with Movability and unique_ptr, although the user is
still left to manually do the exception handling.
> > In C++0x, they may be emplaced into a deque, which solves some of the
> > issues, but you still need an ugly dummy copy constructor.
>
> I'm not really sure what you intent to say here, but it
> sounds to me as if you say that a type hosted within
> a C++0x std::vector needs to satisfy the CopyConstructible
> requirements. In C++0x std::thread objects or std::unique_ptr
> objects can be hosted within a std::vector without problems.
> I don't know, why you should need a deque for this and if
> so, why you should still need a copy constructor.
I went on a tangent and forgot about std::unique_ptr. Non-
MoveConstructible objects such as std::mutex cannot be placed in a
container. (And, looking closer, it is forbidden by 30.4.1/3 to extend
std::mutex with a dummy move constructor to make it compatible.)
The question was, "why not just use a vector? ... it may have been
considered unnecessary to add the core language feature." It sounds
like the context was C++03.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Bo Persson" <bop@gmb.dk>
Date: Thu, 16 Sep 2010 11:37:23 CST Raw View
Seungbeom Kim wrote:
> On 2010-08-27 10:39, Richard Corden wrote:
>
>> And so, with the
>> ability to get the behaviour for most cases using a vector it may
>> have been considered unnecessary to add the core language feature.
>
> I agree that it might have been the most probable reason, but I
> still think it would be nice to add the core language feature,
> because it gives the least surprise to users ("Why is this an
> error, while every other combination is okay?" as I wrote first),
> and because it's easier to teach, and because users who cannot use
> the library solution for any reason may benefit from it.
I think it would just add another special case, that is not intuitive
at all.
What would
p = new x[2](arg1, arg2);
mean?
Do I want two x's with different values, or does x have a 2-argument
constructor?
Or did I perhaps misplace a comma operator? :-)
Me impression is that the committee considered C style arrays to be
broken beyond repair, and focused on std::vector instead.
Your friend obviously is hard core C buff. For anyone else there is
nothing "intuitive" about C arrays.
Bo Persson
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Seungbeom Kim <musiphil@bawi.org>
Date: Mon, 30 Aug 2010 13:31:01 CST Raw View
On 2010-08-27 10:39, Richard Corden wrote:
>
> But then it begs the question, why not just use a vector?
The original reason was, as I wrote first, that I was asked by a friend
who hated std::vector. Probably because he works in an embedded environment
and cannot afford even a single machine instruction "overhead"[*] which
follows an abstraction mechanism such as operator overloading but which
is hidden from the source code view. Maybe the implementation he uses is
a freestanding one and doesn't offer <vector>; I don't know for sure.
And he blamed the language for "substituting a library hack for a core
language deficiency." So I got curious.
[*] An overhead of such a kind that would make v[n]=x (for a std::vector
v) less efficient than a[n]=x (for an array a, which should be heap-
allocated for a fair comparison). Not that I agree that such a gratuitous
overhead exists, but there exist some people who believe so.
> And so, with the
> ability to get the behaviour for most cases using a vector it may have
been
> considered unnecessary to add the core language feature.
I agree that it might have been the most probable reason, but I still
think it would be nice to add the core language feature, because it gives
the least surprise to users ("Why is this an error, while every other
combination is okay?" as I wrote first), and because it's easier to teach,
and because users who cannot use the library solution for any reason may
benefit from it.
--
Seungbeom Kim
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Seungbeom Kim <musiphil@bawi.org>
Date: Mon, 30 Aug 2010 13:31:37 CST Raw View
On 2010-08-27 10:39, Mathias Gaunard wrote:
> On Aug 26, 7:06 am, Seungbeom Kim <musip...@bawi.org> wrote:
>> I also know
>> that we could use std::malloc followed by std::uninitialized_fill_n,
>> but... wouldn't things be much simpler if we had new A[n](args)?
>
> Why std::malloc and not ::operator new?
Without any particular reason. (Maybe influenced by the friend who hates
any abstraction mechanism that's not in C). I just needed a block of
memory that can hold an array of n A objects.
Does the difference matter in any way?
--
Seungbeom Kim
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Dragan Milenkovic <dragan@plusplus.rs>
Date: Mon, 30 Aug 2010 20:15:45 CST Raw View
On 08/30/2010 09:31 PM, Seungbeom Kim wrote:
>
> On 2010-08-27 10:39, Mathias Gaunard wrote:
>>
>> On Aug 26, 7:06 am, Seungbeom Kim<musip...@bawi.org> wrote:
>>>
>>> I also know
>>> that we could use std::malloc followed by std::uninitialized_fill_n,
>>> but... wouldn't things be much simpler if we had new A[n](args)?
>>
>> Why std::malloc and not ::operator new?
>
> Without any particular reason. (Maybe influenced by the friend who hates
> any abstraction mechanism that's not in C). I just needed a block of
> memory that can hold an array of n A objects.
I propose that you influence your friend instead. I can't remember
needing elements in an array to be constructed in a special manner,
unless together with the need to add, remove and/or replace
elements of the array. (I already said this in another message,
but it was in a bit different context)
--
Dragan
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Rick Wheeler <rwheeler@oyogeospace.com>
Date: Tue, 14 Sep 2010 14:49:17 CST Raw View
On Aug 26, 2:08=A0pm, Daniel Kr=FCgler <daniel.krueg...@googlemail.com>
wrote:
> On Aug 26, 8:06 am, Seungbeom Kim <musip...@bawi.org> wrote:
>
>
>
>
>
> > The subject line says it all.
> > When these are possible for a non-array type A:
>
> > =A0 =A0new A
> > =A0 =A0new A()
> > =A0 =A0new A(args)
>
> > these are possible:
>
> > =A0 =A0new A[n]
> > =A0 =A0new A[n]()
>
> > but not this:
>
> > =A0 =A0new A[n](args) =A0 =A0 =A0// an array of n "same" A(args) object=
s
>
> > What is the rationale for this?
>
> > I know that the solution is usually to use std::vector<A>(n, A(args)),
> > but I once was asked by a friend who hated that solution. I also know
> > that we could use std::malloc followed by std::uninitialized_fill_n,
> > but... wouldn't things be much simpler if we had new A[n](args)?
>
> Here some thoughts about possible reasons why this extension
> might not be a good idea:
>
> It's not immediately obvious how often the initializer expression
> would be evaluated - each time for each array element?
>
> Related to this question we have to consider situations where the
> constructor can potentially modify it's arguments - this can already
> be realized in C++03 via lvalue references to non-const or especially
> in C++0x in the presence of rvalue-references (to non-const). Both
> std::vector and uninitialized_fill_n have an clear interface that
> prevent
> an unintentional programmer error here, because the arguments is
> required to be a const T& (A programmer could provide explicit
> template
> arguments to uninitialized_fill_n, but I have never seen such a code
> and even if that code would exist, I would tag it as some very
> intentional doing). Theoretically it would be possible to specify the
> semantics of
>
> new A[n](args)
>
> to be equivalent to invoke this expression in the context of a
> signature
>
> template<class... Args>
> void construct(Args&& args) {
> =A0new A[n](T(std::forward<Args>(args)...)); // pseudo-code
>
> }
>
> but I doubt that this special semantics would be obvious for the
> programmer.
>
> In regard to your suggestion to combine malloc and a proper
> uninitialized_fill* function I would instead suggest to provide a
> library solution basing on operator new[]:
>
> template<class T, class... Args>
> T* make_array_new(std::size_t n, Args&&... args)
> {
> =A0T* result = ::operator new[](n);
> =A0try {
> =A0 =A0std::uninitialized_fill_n(result, n,
> T(std::forward<Args>(args)...));
> =A0} catch(...) {
> =A0 =A0::operator delete[](result);
> =A0 =A0throw;
> =A0}
> =A0return result;
>
> }
>
> or alternatively on an allocator:
>
> template<class T, class Alloc, class Size, class... Args>
> T* allocate_array(Alloc&& alloc, Size n, Args&&... args)
> {
> =A0T* result = alloc.allocate(n);
> =A0try {
> =A0 =A0// You would probably prefer to replace the following by a variant
> that
> =A0 =A0// loops over the a result array and invokes alloc.construct. In
> case
> =A0 =A0// of a an failure it would need to invoke alloc.destroy for all
> constructed
> =A0 =A0// objects:
> =A0 =A0std::uninitialized_fill_n(result, n,
> T(std::forward<Args>(args)...));
> =A0} catch(...) {
> =A0 =A0alloc.deallocate(result, n);
> =A0 =A0throw;
> =A0}
> =A0return result;
>
> }
>
> I decided for the perfect forwarding signature for the allocator as
> well
> to allow for providing an rvalue that can be immediately used.
> Provide
> an overload with const Alloc& if you like, but it would need to make
> a copy of the allocator before using it (allocate/deallocate are
> potentially
> mutable functions).
>
> HTH & Greetings from Bremen,
>
> Daniel Kr=FCgler
>
> --
> [ comp.std.c++ is moderated. =A0To submit articles, try just posting with=
]
> [ your news-reader. =A0If that fails, use mailto:std-...@netlab.cs.rpi.ed=
u]
> [ =A0 =A0 =A0 =A0 =A0 =A0 =A0--- Please see the FAQ before posting. --- =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 ]
> [ FAQ:http://www.comeaucomputing.com/csc/faq.html=A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0]- Hide quoted text -
>
> - Show quoted text -
It should be reasonably obvious that any expressions comprised in args
would only be evaluated once. This is true of all other expressions
that manifest a fuction argument. In fact, any capture of expressions'
syntax to be replayed as if by macro like textual substitution would
be highly unexpected. On the other hand, it would be fully expected
for the constructor to be run for each array entry, the order of which
would have to be specified in the core construct (pesumably from 0 to
N-1).
There should be no constraint nor concern on whether or not the
constructor can modify its arguments. This really represents the most
interesting case and advancement of such an extension. First of all,
it would be hard to construe it as an unintentional programmer error
because any such mistake would have been in the formulation of the
basic argumented constructor, not in its use in an array. Secondly,
the benefits of an allowed behavioral variation with each constructor
invocation is too large to give up for this already weak concern.
This would indeed be a worthwhile core language construct, and seems
unproblematic.
Rick Wheeler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]