Wednesday, November 21, 2018

Digest for comp.lang.c++@googlegroups.com - 24 updates in 6 topics

"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Nov 21 01:57AM +0100

On 20.11.2018 19:24, Mr Flibble wrote:
 
> Except for reference parameters, arguments are /never/ mutated
 
> I quite often mutate function parameters treating them as local function
> variables.
 
There's as yet no real distinction between "parameter" and "argument",
but some people do make such distinctions. One woman's "argument" is
another mans "parameter". And vice versa.
 
The Holy Standard seems to have a preference for "parameter" for
templates and "argument" for functions.
 
Some people say "argument" and "parameter" instead of "actual argument"
and "formal argument", or "actual parameter" and "formal parameter".
 
And presumably some people do the opposite.
 
Now that we have Wikipedia as a direction guide for the most conformist
segment of the population, which I believe constitutes about 98%, it's
likely that they will all soon fall into line and say the same. But
still it's IMO prudent to be lenient in what to accept, and precise in
one's own formulations. I like the "actual" and "formal" qualifications.
 
 
Cheers!,
 
- Alf
James Kuyper <jameskuyper@alumni.caltech.edu>: Nov 20 09:09PM -0500

On 11/20/18 19:57, Alf P. Steinbach wrote:
>> variables.
 
> There's as yet no real distinction between "parameter" and "argument",
> but some people do make such distinctions.
 
Mpst importantly, the C++ standard makes such a distinction, and relies
upon that distinction to clearly specify it's meaning. You can't
properly understand what the standard says using those words unless you
interpret them in the manner defined by that standard.
 
One woman's "argument" is
> another mans "parameter". And vice versa.
 
> The Holy Standard seems to have a preference for "parameter" for
> templates and "argument" for functions.
 
No, the standard isn't Holy, It's authoritative, but because it's an ISO
standard, not because of anything of a religious nature, nor because of
any misguided belief that it's perfect or flawless.
 
The standard very explicitly defines the meanings of both "parameter"
and "argument", and it provides separate (and parallel) meanings for
both words for functions, function-like macros, templates, throw/catch code:
 
> argument
> <template instantiation> constant-expression, type-id, or id-expression in the comma-separated list bounded
> by the angle brackets (14.3)
...
 
> And presumably some people do the opposite.
 
> Now that we have Wikipedia as a direction guide for the most conformist
> segment of the population, which I believe constitutes about 98%, it's
 
It's not about being a conformist - it's about understanding the meaning
of the standard, and about communicating clearly about that meaning. If
you want to understand and be understood, your best bet is to stick to
the definitions provided by the C++ standard in any context where the
C++ standard is relevant. If you don't care about understanding or being
understood, go ahead and use the terms any way you wish.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Nov 21 06:18AM +0100

On 21.11.2018 03:09, James Kuyper wrote:
>> segment of the population, which I believe constitutes about 98%, it's
 
> It's not about being a conformist - it's about understanding the meaning
> of the standard, and about communicating clearly about that meaning.
 
This was not a discussion about the C++ standard.
 
C++ standardese literal text has its place, of course.
 
General programming discussion is usually not such a place.
 
 
> you want to understand and be understood, your best bet is to stick to
> the definitions provided by the C++ standard in any context where the
> C++ standard is relevant.
 
I don't agree that the context under discussion is one where the literal
text of the C++ standard is important.
 
But considering such contexts: that still /depends/.
 
Consider
 
C++17 §16.3.3.1.2/1
<quote>
A user-defined conversion sequence consists of an initial standard
conversion sequence followed by a user-defined conversion (15.3)
followed by a second standard conversion sequence. If the user-defined
conversion is specified by a constructor (15.3.1), the initial standard
conversion sequence converts the source type to the type required by the
argument of the constructor.
</quote>
 
Here the "source type" in the last sentence, is clearly the type of an
actual argument.
 
The last sentence can't therefore be talking about converting that type
to the type of the actual argument: it's already that type.
 
So "the argument of the constructor" must be referring to the
constructor's formal argument.
 
Hence, understanding ¹the general programming terminology, and how very
far from clear-cut unambiguous it is, is key to understanding at least
some parts of the C++ standard.
 
Because that standard is not quite perfect. ;-)
 
 
> If you don't care about understanding or being
> understood, go ahead and use the terms any way you wish.
 
I hope you see how irrelevant that advice is, now.
 
Cheers!,
 
- Alf
 
Notes:
¹ https://en.wikipedia.org/wiki/Parameter_(computer_programming)
David Brown <david.brown@hesbynett.no>: Nov 21 08:18AM +0100

On 20/11/2018 20:32, Christian Gollwitzer wrote:
 
> Now I'm confused. It is OK to put const in the formal parameter to catch
> bugs - I agree, and I did the same thing before. But it it is not OK to
> put it in the declaration? How should this work? Don't they need to match?
 
In the parameters of a function declaration that is not a definition,
qualifiers ("const" and "volatile") are ignored. (This applies to the
parameter itself, not any other parts of the type. A
pointer-to-const-int is different from a pointer-to-int, but a "const
int" is treated the same as an "int".)
 
Still, you may prefer to keep the declaration and the definition exactly
the same. I do - I use the same parameter names in declarations for a
function as in the definition, and I use the same "const" (or rather,
lack of it).
Bonita Montero <Bonita.Montero@gmail.com>: Nov 21 09:12AM +0100

>> overhead for the performance-relevant case that no exception is thrown.
 
> There are no runtime-based exception handling algorithms with zero
> overhead, short of entirely static systems that are profiled.
 
Correctly implemented, table-driven exception-handling has zero overhead
for the performance-relevant case that no exception is thrown.
Bonita Montero <Bonita.Montero@gmail.com>: Nov 21 09:13AM +0100

> There are no runtime-based exception handling algorithms with zero
> overhead, short of entirely static systems that are profiled.
 
Read this: http://www.ut.sco.com/developers/products/ehopt.pdf
David Brown <david.brown@hesbynett.no>: Nov 21 10:16AM +0100

On 20/11/18 15:20, James Kuyper wrote:
>> function.
 
> I think you mean "arguments", not "parameters". See the definitions of
> those terms in 1.3.2 and 1.3.15 of the standard.
 
Yes, that is what I should have written.
 
> function is allowed to lack qualifiers on parameters that have those
> qualifiers in the function definition, and it's conventional to take
> advantage of that fact.
 
Agreed.
David Brown <david.brown@hesbynett.no>: Nov 21 10:25AM +0100

On 21/11/18 01:57, Alf P. Steinbach wrote:
 
> There's as yet no real distinction between "parameter" and "argument",
> but some people do make such distinctions. One woman's "argument" is
> another mans "parameter". And vice versa.
 
There is a distinction in the C standards - "argument" is the things in
the function call, while "parameter" is the things in the function
declaration and definition. The distinction has not always been clear,
different languages have different terms, and people (such as myself)
are not always accurate about following the terminology of the language.
 
As far as I understand it, C++ standards follow the same distinction as
for C, but with more nuances. See section 1.3 of the standards-
 
> likely that they will all soon fall into line and say the same. But
> still it's IMO prudent to be lenient in what to accept, and precise in
> one's own formulations. I like the "actual" and "formal" qualifications.
 
"actual argument" and "formal parameter" would probably make things as
clear as they could be. Other than that, a little explanation of the
context always helps - if someone writes about the "arguments in the
function declaration", you know they actually mean parameters.
David Brown <david.brown@hesbynett.no>: Nov 21 10:57AM +0100

On 20/11/18 16:25, Rick C. Hodgin wrote:
 
> I disagree with this design philosophy. I believe it's better to
> declare everything in one place, and then assign it in a documented
> init { } block.
 
Fair enough. You are not alone in preferring to declare variables
together at the start of a block (usually the start of a function), and
assign to them later rather than initialising them. Just be aware that
initialisation and assignment are different. They usually - especially
in C, and with an optimising compiler - have the same effect in
practice. But you can't assign to a "const" variable, you can only
initialise it.
 
And in C++, initialisation (construction) can be very different from
assignment. In particular, initialisation of objects can be a good deal
more efficient than default construction followed by assignment later
on. In C, something like "T x;" does not usually correspond to any
generated code, but in C++ it certainly can do.
 
So you will find "declare all your variables at the top of the function"
style in C90 code, and amongst some C99/C11 programmers - but you will
rarely find it in C++ programming.
 
(I am not trying to persuade you here, just pointing out some issues for
your consideration.)
 
> some other location that's more optimized for you. But in this way,
> everything is grouped / encapsulated in its area, able to be docu-
> mented and understood at first glance.
 
I agree that the compiler can move things around for optimisation. As
for how much this affects documentation or understanding, I think the
diplomatic answer is "it's not that simple, and it will vary case by case".
 
> not be set after this state is established. Depending on the
> dynamics of the code at work using that variable, this is either
> by known compiler state, or a hard internal flag.
 
Well, for your language, you make the rules to suit yourself.
Personally, I think:
 
void my_function(void)
{
const int my_variable = populate_it();
// Other code here
}
 
is simpler, and clearer. It also makes it clear that "my_variable" is a
really bad name, since it is immediately obvious that it cannot vary.
 
Would it make a difference if there were lots of variables here, some of
them constant (or read-only)? I don't think so, but you may have a
different opinion.
 
> {
> [|readonly|] int my_variable;
> ...
 
I am lost here - I can't see the use-cases. If I want to say something
can't be changed, I define it as "const". I don't want to be able to
make it non-const later on. If I want a read-write version, I make a
new variable initialised to the const's value. Conversely, if I want to
say something can be changed, I define it without "const". If I want an
unchanging copy of it, I define a new "const" variable initialised from
the variable.
 
(This may just be another case where you want to give programmers
flexibility, and I like there to be more concrete rules.)
 
> and if you need something else that can't be shifted through tra-
> ditional pointer manipulation ... use a reference, or even pass
> it by value if the source doesn't need to be altered.
 
Sometimes you want to change where a pointer points, other times you
don't. const pointers have their place. It is certainly the case that
many, perhaps most, uses of const pointers in C can be replaced by
references in C++, but they are still not entirely pointless.
 
"Rick C. Hodgin" <rick.c.hodgin@gmail.com>: Nov 21 05:17AM -0800

On Wednesday, November 21, 2018 at 3:12:28 AM UTC-5, Bonita Montero wrote:
> > overhead, short of entirely static systems that are profiled.
 
> Correctly implemented, table-driven exception-handling has zero overhead
> for the performance-relevant case that no exception is thrown.
 
It's an interesting approach. I considered something similar,
but equated it to being a little too dangerous and risky. It's
better to have an observed runtime construction in my opinion.
 
And in those places / cases where performance is truly an issue,
don't use try..catch at every instance, but implement something
else that's custom with a larger outer parent try..catch to min-
imize the impact on oft-called functions. A set of enum constant
values can be used to populate a single member variable indicating
where the exception took place. Minimal overhead, and nothing
fancy required.
 
Note also:
 
The author of the PDF you linked cited it does have some per-
formance overhead. It has a large footprint in RAM at all
times, removes some optimizations which may otherwise be em-
ployed in normal code (in the code that does not generate
exceptions), and has some additional slower performance in
processing exceptions in unwinding and recovery.
 
The author cites that some (rather extraordinary) effort can
be applied to overcome many of the non-exception-case performance
issues, but the memory footprint and slower exception handling
cannot be overcome.
 
IMO ... it's a questionable approach. But, I have no doubts
it would work. I just wouldn't use its design.
 
--
Rick C. Hodgin
"Rick C. Hodgin" <rick.c.hodgin@gmail.com>: Nov 21 05:27AM -0800

On Wednesday, November 21, 2018 at 4:57:53 AM UTC-5, David Brown wrote:
> (I am not trying to persuade you here, just pointing out some issues for
> your consideration.)
 
I understand. I still stand behind the philosophy.
 
> // Other code here
> }
 
> is simpler, and clearer...
 
To me, the use of "const" can be confusing. Suppose it
were const int* my_variable = populate_it(); ... it would
be easy to become confused on if the value pointed to by
the pointer is a constant, or if the pointer value itself
is a constant. That confusion was in my mind a few days
ago. It's still a little murky. :-)
 
I believe a different approach is appropriate: Pointers
should always be non-constant. And the values they point
to only should ever be read-only or read-write, and I'm
trying to convince myself to make a use case for write-
only as well.
 
> It also makes it clear that "my_variable" is a
> really bad name, since it is immediately obvious that it cannot vary.
 
It's still a variable. It's not a fixed quantity. It is
populated externally, and used here as that value.
 
> say something can be changed, I define it without "const". If I want an
> unchanging copy of it, I define a new "const" variable initialised from
> the variable.
 
I believe the const/non-const state should be a policy
when the input may be in read/write memory anyway. The
only fixed cases are when it's in read-only memory. And
in such a case, I don't think "const" is a good idea,
but maybe [|locked|] or something, to indicate the value
is locked and cannot be changed even with an override.
 
There's probably a legal term for such a thing. I can re-
member in the Bible there were laws issued or decreed which
could not be revoked once issued. I don't remember if they
had a special name though. I'm sure there's one somewhere
that could replace "locked" with the proper term.
 
> (This may just be another case where you want to give programmers
> flexibility, and I like there to be more concrete rules.)
 
The rules here would be concrete. You just have to follow
policy / protocol.
 
> don't. const pointers have their place. It is certainly the case that
> many, perhaps most, uses of const pointers in C can be replaced by
> references in C++, but they are still not entirely pointless.
 
My approach is that pointers are just numbers, and if you
have a value that you don't want to change, don't use a
pointer, but use a commensurately sized unsigned integer,
and then do the direct compare.
 
Pointers are for pointing to data, and they should always
be able to be manipulated. If you need something that
should not be manipulated, don't use a pointer. Use some-
thing else.
 
--
Rick C. Hodgin
James Kuyper <jameskuyper@alumni.caltech.edu>: Nov 21 09:15AM -0500

On 11/21/18 00:18, Alf P. Steinbach wrote:
>>>>> Except for reference parameters, parameters are /never/ mutated
 
>>>> I assume you meant to type:
 
>>>> Except for reference parameters, arguments are /never/ mutated
...
 
>> It's not about being a conformist - it's about understanding the meaning
>> of the standard, and about communicating clearly about that meaning.
 
> This was not a discussion about the C++ standard.
 
This group is for discussing C++, the rules of which are set by that
standard. The context of your comment was an incorrect statement of one
of those rules. It was claimed, incorrectly, that function parameters
are never mutated.
The correct statement is that function parameters may freely be mutated
unless declared "const"; it's function arguments that a function cannot
mutate directly. I can't imagine a clearer example of the necessity of
clearly distinguishing between parameters and arguments, and of doing so
in a manner consistent with the standard's definitions of those terms.
 
...
> far from clear-cut unambiguous it is, is key to understanding at least
> some parts of the C++ standard.
 
> Because that standard is not quite perfect. ;-)
 
No, as you say, the standard is not perfect. Correcting that section to
refer to the parameter rather than the argument is the key to making it
more clearly understandable. Taking liberties in interpreting those
words can lead only to more misunderstandings down the road, even if
those liberties happen to give you a correct understanding in this case.
 
>> If you don't care about understanding or being
>> understood, go ahead and use the terms any way you wish.
 
> I hope you see how irrelevant that advice is, now.
 
No, I see how relevant it is to precisely the cases you presented as
counter-examples.
David Brown <david.brown@hesbynett.no>: Nov 21 09:57PM +0100

On 21/11/2018 14:17, Rick C. Hodgin wrote:
> cannot be overcome.
 
> IMO ... it's a questionable approach. But, I have no doubts
> it would work. I just wouldn't use its design.
 
Table-driven exception handling is by far the most common system used in
modern C++ compilers. It takes extra code space for the unwind tables -
these are rarely an issue (except for constraint-limited embedded
systems). Yes, you get some optimisation limitations, such as
limitations on the amount of re-ordering and moving the compiler can do,
especially on object construction and destruction. But you get that
kind of limitation with any C++ exception implementation - when
compiling code which has an external call, the compiler does not know if
the function can throw an exception, and must order code on the
assumption that it might.
 
The common alternative strategy for C++ exceptions is to store lists of
destructors on the stack. This avoids the need to generate long tables
in code, but uses more run-time memory and stack space, and means fewer
functions can skip having a stack frame. It has higher run-time speed
costs for code that does not throw an exception, but works faster when
an exception is thrown.
Jan Riewenherm <vollasso@googlemail.com>: Nov 21 06:05AM -0800

Hi,
 
i´ve got an issue where i do not really find a proper solution.
 
We have locking objects that take a pointer as argument and protect simultaneous access to the same object from different threads. These locking objects have a singleton in the backed but thats not the point of interest here.
 
What i would like to prevent is that the locking object is used in the wrong way.
 
Code Snippped of the locking helper object.
class LockingObject
{
LockingObject(void* pObjectToLock)
{
Singleton_Lock(pObjectToLock);
}
~LockingObject()
{
Singleton_Unlock(m_pObjectToLock);
}
private:
void* m_pObjectToLock
}
 
How to use the locking object:
function test()
{
LockingObject object(this);
do_something_on_this();
}
 
As soon as the locking object is created the simultaneous access is protected.
As soon as i run out of the scope of my function the locking objects is freeed.
 
The problem i have:
function test()
{
LockingObject(this);
do_something_on_this();
}
 
How to prevent calling the constructor where the created object is not used at all? c++17 [[nodiscard]] does not work for Constructors.
 
The locking object is a very generic approach to lock access from different threads and leads to a very low ressource consumption as i do not need to have dedicated locking objects for members or functions i want to protect. So please do not take a closer look at how other locking mechanisms might work. I would like to focus on the problem of having constructors which do not actually create an object.
 
Any advice appreciated.
 
Jan
"Öö Tiib" <ootiib@hot.ee>: Nov 21 07:56AM -0800

On Wednesday, 21 November 2018 16:06:08 UTC+2, Jan Riewenherm wrote:
> {
> Singleton_Lock(pObjectToLock);
> }
 
That constructor leaves private member m_pObjectToLock
uninitialized.
 
> {
> Singleton_Unlock(m_pObjectToLock);
> }
 
That destructor passes uninitialized member.
 
> function test()
> {
> LockingObject(this);
 
Note that the temporary constructed above must be destroyed by now.
 
> do_something_on_this();
> }
 
> How to prevent calling the constructor where the created object is not used at all? c++17 [[nodiscard]] does not work for Constructors.
 
Technically it is possible to replace that particular public
constructor with a public factory method and private
constructor. Inlining and RVO might achieve that there
are no performance differences.
 
The compilers do not warn there because it might be
code as its designer desired:
 
std::ofstream("filename"); // creates ./filename if it doesn't exist

Philosophically all useful languages have to allow endless
ways to express lies and nonsenses for to achieve that
at least some truths can be expressed in those as well.
Blocking all lies and nonsense will be achieved only when
nothing useful can be expressed anymore.
 
 
> The locking object is a very generic approach to lock access from different threads and leads to a very low ressource consumption as i do not need to have dedicated locking objects for members or functions i want to protect. So please do not take a closer look at how other locking mechanisms might work. I would like to focus on the problem of having constructors which do not actually create an object.
 
> Any advice appreciated.
 
Always try to post code that compiles, runs and demonstrates
the issue that you have. Otherwise it is hard to realize
what sort of defect or lack of expertise is its cause.
Paavo Helde <myfirstname@osa.pri.ee>: Nov 21 07:54PM +0200

On 21.11.2018 16:05, Jan Riewenherm wrote:
> do_something_on_this();
> }
 
> How to prevent calling the constructor where the created object is not used at all? c++17 [[nodiscard]] does not work for Constructors.
 
Define a macro
 
#define SCOPED_LOCK(x) LockingObject object(x);
 
and make a rule to use only this macro throughout the code base:
 
void test() {
SCOPED_LOCK(this);
// ...
}
 
I use this all the time, works fine for avoiding temporary objects
(although TBH the initial motivation for using a macro was to have
correct __FILE__ and __LINE__ for debugging purposes).
Elephant Man <conanospamic@gmail.com>: Nov 21 04:09PM

Article d'annulation émis par un modérateur JNTP via Nemo.
Horizon68 <horizon@horizon.com>: Nov 21 08:07AM -0800

Hello...
 
 
My Scalable Parallel C++ Conjugate Gradient Linear System Solver Library
was updated
 
The Dense Linear System Solver library was updated to version 1.72
 
You can download it from:
 
https://sites.google.com/site/scalable68/scalable-parallel-c-conjugate-gradient-linear-system-solver-library
 
 
Thank you,
Amine Moulay Ramdane.
"Öö Tiib" <ootiib@hot.ee>: Nov 21 01:12AM -0800

On Tuesday, 20 November 2018 23:38:31 UTC+2, Jorgen Grahn wrote:
 
> 0 + x == x 0 | x == x
> 0 * x == 0 0 & x == 0
 
> (But again, I have no use of this knowledge.)
 
I have memorized the precedence for ages but still find
that some "redundant" parentheses often help me to
read complex expressions.
 
On current case the OP predicate is not ordering
its arguments but lack of parentheses feels to make
that harder to realize.
"Öö Tiib" <ootiib@hot.ee>: Nov 21 05:46AM -0800

On Tuesday, 20 November 2018 02:13:33 UTC+2, Paul wrote:
 
> But it doesn't need memorization or looking it up. The precedence between
> || and && can't be different from the precedence between + and *.
> Surely everyone knows that true || false && false means true || (false && false) which == true.
 
Still there seems to be confusion exactly there because logic of
your predicate does not follow logic that your describe in prose.
Also there seem to be difference *exactly* in parentheses
misplaced.
 
That is what your code does:
 
return word1.size() <= length && word2.size() > length
|| word1.size() > word2.size();
 
That seems to be what you describe in prose:

return word1.size() <= length && (word2.size() > length
|| word1.size() > word2.size());
 
Notice the parentheses.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Nov 21 01:50AM +0100

On 19.11.2018 11:19, Paul wrote:
> std::unordered_set<std::string> words = {"acceca", "ace", "acce", "accce" };
> std::cout << LongestWord("acecad", words);
> }
 
"acce", said to be a correct answer, doesn't contain all the characters
in "acecad".
 
As an example of another word that contains /some/ of the characters,
"ace" does.
 
None of words contain /all/ of the characters.
 
And none of the words are /permutations/ of the characters.
 
What's the rule here?
 
 
Cheers!,
 
- Alf
Louis Krupp <lkrupp@nospam.pssw.com.invalid>: Nov 20 11:49PM -0700

On Tue, 20 Nov 2018 14:55:35 -0000 (UTC), Juha Nieminen
>until you have gone through the entire word.
 
>The above is a bit lacking as a full algorithm explanation, but I'm sure
>it ought to be enough to give an idea.
 
True, the implementation isn't complicated, but in my opinion
describing what it does takes longer than laying out these steps:
 
1. Sort the letters in the pattern.
2. Generate a regular expression.
3. Sort the letters in each input string.
4. Call routines in <regex> to do the matching.
5. Keep track of the longest matching input string.
 
The regular expression library has been tested, so there's (hopefully)
no need to worry about that.
 
If you'd rather do everything in, say, Python, the steps are simple.
If you want it to happen faster, use C++ and <regex>. If it's still
not fast enough, the implementation you laid out is probably the way
to go. Doing it first with regular expressions -- whether it's in
Python or in C++ -- would give you a baseline you could use for
testing.
 
( I almost said "do it in Python or a shell script" but then I thought
about what a nightmare it would be to do it in a shell script. There
was a time when I would have said "Perl," meaning Perl 5, but that's
out of fashion now, so ... Perl 6. Yes, that's it. Perl 6. :))
 
Louis
Juha Nieminen <nospam@thanks.invalid>: Nov 21 07:40AM

> to go. Doing it first with regular expressions -- whether it's in
> Python or in C++ -- would give you a baseline you could use for
> testing.
 
Firstly, I have my doubts that building an ascii string that's a regexp
that does what you want is any simpler than doing the comparison yourself.
 
Secondly, the original task is, essentially and pretty much, comparing
binary bytes to each other. Going the route of creating a dynamic string
from the binary data, have a library parse that string, allocate more
data containers within itself (probably some kind of tree or such),
and then run a complicated regexp matching algorithm not only sounds
like it's needlessly inefficient, but also needlessly complicated for
such a relatively simple task. It's like shooting flies with a cannon.
 
Thirdly, depending on what kind of thing it is that the task is trying
to teach, it may actually be more didactic and beneficial to do the
comparison yourself, as a programming exercise.
Louis Krupp <lkrupp@nospam.pssw.com.invalid>: Nov 21 03:47AM -0700

On Wed, 21 Nov 2018 07:40:05 -0000 (UTC), Juha Nieminen
 
>Thirdly, depending on what kind of thing it is that the task is trying
>to teach, it may actually be more didactic and beneficial to do the
>comparison yourself, as a programming exercise.
 
The world would be a scary place if everyone agreed with me.
 
Louis
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: