What Value Does Const Correctness Offer

  • Follow


I was having an argument recently with a fellow programmer.
His take was that const correctness does not add significant
value to your software, and, given that it is quite burdensome
to maintain, he questioned whether it should be ignored.

My primary argument in its favor is that it gives additional
information to the compiler about your intentions, which
can allow the compiler to detect bugs, however, on challenge
I really couldn't remember an example of a bug that it
prevented (from my own practical expereience.)

I also argued that const correctness was not generally
difficult to maintain, but, in all honesty, it is a
significant amount of work to maintain it. Just off the
top of my head I would say that about 10% of syntax errors
are const related errors. (And the large majority of them
only show an error in const correctness, rather than
offering any productive value.)

Often some of the strangest syntax errors arise from const
correctness errors, and they sometimes take some
detangling. Further, many external libraries one uses
often have const correctness errors in them, which have
to be eliminated by tangled casts and inappropriate
mutables.

I wonder if any of you can offer some convincing arguments
for the effort involved in const correctness. Or perhaps
offer some class of bug caught by it use.

Thanks.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply jessica_boxer 9/11/2004 5:00:31 AM

Jessica B wrote:
 > I was having an argument recently with a fellow programmer.
 > His take was that const correctness does not add significant
 > value to your software, and, given that it is quite burdensome
 > to maintain, he questioned whether it should be ignored.

You can't ignore it, because you are forced to use const anywhere you
might need to bind a reference to a temporary.

 > My primary argument in its favor is that it gives additional
 > information to the compiler about your intentions, which
 > can allow the compiler to detect bugs, however, on challenge
 > I really couldn't remember an example of a bug that it
 > prevented (from my own practical expereience.)
<snip>

It should prevent you from putting the source and destination
arguments to memcpy or std::copy the wrong way round (note that the
correct order differs between the two functions!).

-- 
Ben Hutchings
If God had intended Man to program,
we'd have been born with serial I/O ports.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Ben 9/12/2004 10:09:13 AM


"Jessica B" <jessica_boxer@yahoo.com> wrote in message
news:642e01d8.0409101150.63271032@posting.google.com...
 > I was having an argument recently with a fellow programmer.
 > His take was that const correctness does not add significant
 > value to your software, and, given that it is quite burdensome
 > to maintain, he questioned whether it should be ignored.

My experience is similar to your colleague's. Maintaining const correctness,
while generating lots of errors at compile time, has never found an actual
bug for me. Peppering declarations with 'const' doesn't look good and tends
to visually obfuscate what the types are. Const has numerous arcane and
sometimes inconsistent rules with how overloading and partial specialization
works (sometimes it's ignored, sometimes it isn't). And lastly, const
doesn't help the optimizer, since there are so many legal ways to get around
it.

Nevertheless, many programmers swear by const and firmly believe it improves
program readability and that it does find real bugs. I'm just not one of
them <g>. This is why D makes const a storage class, rather than a type
modifier. A const storage class means that the data is semantically
read-only, and could be placed in ROM or in a read-only data segment. The
optimizer can rely on it being constant.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/12/2004 10:09:56 AM

"Jessica B" <jessica_boxer@yahoo.com> wrote in message
news:642e01d8.0409101150.63271032@posting.google.com...

> I was having an argument recently with a fellow programmer.
> His take was that const correctness does not add significant
> value to your software, and, given that it is quite burdensome
> to maintain, he questioned whether it should be ignored.

Rubbish.

> My primary argument in its favor is that it gives additional
> information to the compiler about your intentions, which
> can allow the compiler to detect bugs, however, on challenge
> I really couldn't remember an example of a bug that it
> prevented (from my own practical expereience.)

Well. IMHO you can just safely ignore the guy, really. If one thinks a
safety belt is just there for some idiots have spare time tomake additional
rules -- you will not convince them anyhow.

And 'const' in C++ is not just there to detect bugs, but serves a plenty of
other purposes -- like communicates intent, documents interface, allows
overloads, etc.  And built-in rules of the language will make your life a
hell if you never write 'const' in your classes and functions.     As
literals, temporaries will not bind to nonconst refs.   And passing by value
will be slow for one set of cases and completely inappropriate for others.

> I also argued that const correctness was not generally
> difficult to maintain,

It is trivial to maintain.    What is cumbersome -- picking up some oldtimer
C library and convert it to a const-correct C++ package.  But when you write
stuff from scratch, it's completely natural to add 'const' wherever your
*intetntion* is not to change the object.  (And I observe it is the case for
the majority of situations.)

> but, in all honesty, it is a
> significant amount of work to maintain it. Just off the
> top of my head I would say that about 10% of syntax errors
> are const related errors.

You mean mistyping const to cosnt?

Anyway, IMHO the syntax-error-pervcentage is not an interesting measure.
What counts the overall *time* spent on work.  I find the time spent on
typing the extra letters, and correct the mistypes, and also misthinks, the
recompiles just caused by that -- all that time is still under the
measurable limit.

> (And the large majority of them
> only show an error in const correctness, rather than
> offering any productive value.)

Okey, then pick a single case of the minority, and estimate the time it
would cause going uncaught.  My guess it will be by one magnitude more than
all the time consumed by the majority.

> Often some of the strangest syntax errors arise from const
> correctness errors, and they sometimes take some
> detangling.

Example?

> Further, many external libraries one uses
> often have const correctness errors in them,

That is indeed a problem -- but the fact you face defective libraries is
hardly an excuse to write more defective software of your own.    :-)
The general solution if you can't drop that library or manage to get it
fixed by supplier is to create some wrappers, that are const-correct.
[btw most shops use external libraries through domestic wrappers anyway, so
it is rarely a new and extra work.]

> which have
> to be eliminated by tangled casts and inappropriate
> mutables.

The important rule is to have those casts (just as any such stuff) AWAY from
the user code.  :)

> I wonder if any of you can offer some convincing arguments
> for the effort involved in const correctness. Or perhaps
> offer some class of bug caught by it use.

Hm, it's hard to show the bugs for a simple reason -- they are caught so
early in the process and so easy to correct wihout cost, likely no one
bothers to document or even remember them.    See the estimates for a cost
of bug caught at design/coding/autotests/review/other tests/production,
there are steep jumps.

OTOH you can't hack that problem in isolation.  The issue of 'code shall
reflect the intent' and 'the design shall be clear about what changes what'
is essential to write correct stuff.  If you have people who can't think
that way, sure tools that help will really just cause headache, and yield no
real benefits.

Paul



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Balog 9/12/2004 3:59:19 PM

In article <bPJ0d.74330$3l3.5033@attbi_s03>, Walter 
<walter@digitalmars.nospamm.com> writes
>My experience is similar to your colleague's. Maintaining const correctness,
>while generating lots of errors at compile time, has never found an actual
>bug for me. Peppering declarations with 'const' doesn't look good and tends
>to visually obfuscate what the types are. Const has numerous arcane and
>sometimes inconsistent rules with how overloading and partial specialization
>works (sometimes it's ignored, sometimes it isn't). And lastly, const
>doesn't help the optimizer, since there are so many legal ways to get around
>it.

You can't be serious:-)

double const pi(3.14159265);

is a very different beast to

double value(2.1);

The compiler 'knows' that unless the programmer does something silly 
like take the address of pi that it need not provide storage for it, 
however in the case of value, it better provide storage unless it can 
categorically prove that value never ever changes.

Equally, it is fully entitled to ROM objects that are declared as const 
and if the programmer tries to change the object the programmer is 
wrong, not the compiler. We even introduced an extra keyword, mutable, 
in order to deal with the case of logically const objects that would 
still have a mutable element.

The only people I know that have a problem with const are those that 
come from a C background. Novices quickly learn to write const correct 
code if correctly introduced to the concept.

Now the problem is that const is an overloaded keyword. At top level it 
effectively means immutable (and so ROMable) at other levels it means 
'read only'.

BTW that is yet another reason for placing const at the extreme right of 
the type being qualified; if there is then any other type element (* or 
&) to its right it the overall type is not immutable, only write 
protected.

-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Francis 9/12/2004 3:59:57 PM

Jessica B wrote:

> I was having an argument recently with a fellow programmer.
> His take was that const correctness does not add significant
> value to your software, and, given that it is quite burdensome
> to maintain, he questioned whether it should be ignored.
> 
> My primary argument in its favor is that it gives additional
> information to the compiler about your intentions, which
> can allow the compiler to detect bugs, however, on challenge
> I really couldn't remember an example of a bug that it
> prevented (from my own practical expereience.)
[snip]

const correctness enables you to express the concept of immutable
data, and have the compiler support it; for example, if your
data is in ROM.

If you use memory mapped I/O, writing to certain memory locations
can have unintended side effects.  With the proper const specifications
you can avoid such situations at compile time.

The promise of constness increases the probability of better
optimization.

And, if you're dealing with big objects, it saves you a lot
of copying to pass a const&.

There is little question that constness and its requirements
have been introduced to solve some real issues, and many love
it.  It's a lot easier to deal with problems at compile time
than after releasing to the customer.
-- 
Antoun Kanawati
antounk.at@comcast.dot.net
[remove .dot and .at before use]

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Antoun 9/12/2004 4:06:44 PM

In article <642e01d8.0409101150.63271032@posting.google.com>, Jessica B 
<jessica_boxer@yahoo.com> writes
>I was having an argument recently with a fellow programmer.
>His take was that const correctness does not add significant
>value to your software, and, given that it is quite burdensome
>to maintain, he questioned whether it should be ignored.

Let me guess that he came from a C background where they (with good 
reason) talk of const poisoning. Without function overloading const is 
costly because it is often possible for an object to acquire const 
protection by 'accident' Such possibility means that it then has to be 
cast away, and the programmer now has the responsibility to ensure that 
the cast is valid (i.e. do not try to cast away const qualification of 
an object that was declared as const, while safely casting away an 
acquired const attribute)

>
>My primary argument in its favor is that it gives additional
>information to the compiler about your intentions, which
>can allow the compiler to detect bugs, however, on challenge
>I really couldn't remember an example of a bug that it
>prevented (from my own practical expereience.)

You mean you have never had to deal with accidental attempts to modify a 
string literal? The concept of const in C++ is also essential to 
allowing the passing of object by reference when the object must not be 
changed by the function being called. Again this is an issue for C++ but 
not for C.

>
>I also argued that const correctness was not generally
>difficult to maintain, but, in all honesty, it is a
>significant amount of work to maintain it. Just off the
>top of my head I would say that about 10% of syntax errors
>are const related errors. (And the large majority of them
>only show an error in const correctness, rather than
>offering any productive value.)

Just because 10% of your syntax errors are caused by breaches of const 
correctness does not make const correctness hard to maintain but it does 
mean that the compiler is identifying places in your code where you are 
doing something suspect.

>
>Often some of the strangest syntax errors arise from const
>correctness errors, and they sometimes take some
>detangling. Further, many external libraries one uses
>often have const correctness errors in them, which have
>to be eliminated by tangled casts and inappropriate
>mutables.

So? Incompetence in developers may be a reason not to buy their products 
but it certainly isn't a reason to allow their incompetence by changing 
the language.

>
>I wonder if any of you can offer some convincing arguments
>for the effort involved in const correctness. Or perhaps
>offer some class of bug caught by it use.

But like exception safety, it isn't a problem once one changes ones 
coding habits. Those who write C++ as if it were C will have problems, 
but trying to write C++ without const correctness would result in a 
mess. const wasn't introduced to C++ on a whim, though perhaps C would 
have been better off had it not copied the keyword and then watered down 
its semantics. C might have found it better to have made const a keyword 
and then specified that it did nothing in C but was only there for C++ 
compatibility.

-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/12/2004 4:08:57 PM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:1A2S+CRwpCRBFwxc@robinton.demon.co.uk...
 > You can't be serious:-)
 >
 > double const pi(3.14159265);
 >
 > is a very different beast to
 >
 > double value(2.1);
 >
 > The compiler 'knows' that unless the programmer does something silly
 > like take the address of pi that it need not provide storage for it,
 > however in the case of value, it better provide storage unless it can
 > categorically prove that value never ever changes.
 >
 > Equally, it is fully entitled to ROM objects that are declared as const
 > and if the programmer tries to change the object the programmer is
 > wrong, not the compiler.

In this case, using const as the 'top level', const is essentially being
used as a storage class, and it can be put in ROM. I was referring, however,
to things like pointer to const. The optimizer cannot rely on whatever the
pointer is pointing to being the same value after, say, a function call or
an assignment through another pointer.


 >We even introduced an extra keyword, mutable,
 > in order to deal with the case of logically const objects that would
 > still have a mutable element.

I've always been bemused by mutable - it seems to be driven by the notion
that const things are better, so we'll enable things to be declared as const
even though they're not <g>. Isn't mutable is an admission of the semantic
failure of const? Maybe there's a compelling argument for it I haven't seen.


 > The only people I know that have a problem with const are those that
 > come from a C background. Novices quickly learn to write const correct
 > code if correctly introduced to the concept.

I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and I
know how to write const correct code. I just haven't found a practical
benefit to it - it doesn't find real bugs, it doesn't look good, and it
doesn't help code generation.


 > Now the problem is that const is an overloaded keyword. At top level it
 > effectively means immutable (and so ROMable)

D keeps this meaning, and so making const a storage class makes sense.

 > at other levels it means 'read only'.
 >
 > BTW that is yet another reason for placing const at the extreme right of
 > the type being qualified; if there is then any other type element (* or
 > &) to its right it the overall type is not immutable, only write
 > protected.



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/13/2004 10:28:25 AM

"Francis Glassborow" wrote:

Walter  <walter@digitalmars.nospamm.com> writes
 > >My experience is similar to your colleague's. Maintaining const
correctness,
 > >while generating lots of errors at compile time, has never found an
actual
 > >bug for me.
 >
 > You can't be serious:-)
 >
 > double const pi(3.14159265);
 >
 > is a very different beast to
 >
 > double value(2.1);
 >
I guess the experience is more about const qualified member
functions than variables.

Const qualifier for member function helps
to catch errors (though not as often I would like)
but single change somewhere deep in code can
propagate across whole project with dozens of places
needing update.

That's how I understand the complaint.

/Pavel




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Pavel 9/13/2004 10:29:51 AM

jessica_boxer@yahoo.com (Jessica B) wrote (abridged):
 > I wonder if any of you can offer some convincing arguments
 > for the effort involved in const correctness. Or perhaps
 > offer some class of bug caught by it use.

For me the main use is when you have a reference and aren't sure what the
effects of changing it would be. For example:

     void move( Rect *pRect ) {
          pRect->topLeft().x += 10;
     }

Does this move the given rectangle 10 units to the left? It depends on
whether what topLeft() returns is a copy of the rectangle's point, or the
actual defining point itself. If you have garbage collection or are using
smart pointers, then a result can be a (smart) pointer and still be a
copy, and the examples are not so trivial.

Anyway, const cuts this Gordian Knot by disallowing it; by not allowing
you to change through the reference. It makes it much easier to use
references and shared data without unwanted side effects. When I first
started using Smalltalk (a language which has garbage collection, but no
static type checks) I found const type errors to be the ones which took
most of my debugging time. (Admittedly when I returned to Smalltalk a few
years later my experienced changed, so this may be a matter of programming
style or competence).

The importance of "const", in the broader sense of state-free programming,
is such that the entire school of Functional Programming is largely based
upon the idea. I find it quite useful when organising my thoughts and code
to distinguish between operations which may change the program's state,
and operations which will not. It helps when debugging live code, for
example - if a routine is const, I know I can set the program counter back
and re-execute a call to it without changing anything. It helps when
reasoning about the code.

Whether all this is compelling is another matter. I agree the cost is
high. I am especially bothered by the frequent need to replicate code, to
have const and non-const versions of the same function.

-- Dave Harris, Nottingham, UK

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply brangdon 9/13/2004 10:33:05 AM

"Walter" <walter@digitalmars.nospamm.com> writes:

|>  "Jessica B" <jessica_boxer@yahoo.com> wrote in message
|>  news:642e01d8.0409101150.63271032@posting.google.com...
|>   > I was having an argument recently with a fellow programmer. His
|>   > take was that const correctness does not add significant value to
|>   > your software, and, given that it is quite burdensome to
|>   > maintain, he questioned whether it should be ignored.

|>  My experience is similar to your colleague's. Maintaining const
|>  correctness, while generating lots of errors at compile time, has
|>  never found an actual bug for me. Peppering declarations with
|>  'const' doesn't look good and tends to visually obfuscate what the
|>  types are. Const has numerous arcane and sometimes inconsistent
|>  rules with how overloading and partial specialization works
|>  (sometimes it's ignored, sometimes it isn't). And lastly, const
|>  doesn't help the optimizer, since there are so many legal ways to
|>  get around it.

I can understand your point of view; I find it particularly agravating
that I sometimes have to provide two exactly identical functions (e.g.
like operator[]), one of which can be called on a const object, and the
other which allows modification through the returned reference.  On the
other hand, I find that when joined with the refusal to bind a const
reference to a temporary, it does catch some errors -- cases when an
unintentional implicit conversion reared its head.  IMHO, the better
solution would have been to get rid of implicit conversions, but
somehow, I don't think that that would have flown.

At any rate, given the current rules, you really don't have the choice
in general.  The choice you might have is whether to use const, and
enforce const correctness, for entity objects, i.e. objects you pass by
reference or pointer, and never copy.  Even their, all things
considered, I prefer the consistency of const correctness.

-- 
James Kanze
Conseils en informatique orient�e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S�mard, 78210 St.-Cyr-l'�cole, France +33 (0)1 30 23 00 34

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply James 9/13/2004 10:38:48 AM

Jessica B wrote:
 > [...]
 > My primary argument in its favor is that it gives additional
 > information to the compiler about your intentions, which
 > can allow the compiler to detect bugs, however, on challenge
 > I really couldn't remember an example of a bug that it
 > prevented (from my own practical expereience.)

How about any kind of ownership transfer scenario?  For instance,
ownership probably should not leave a const object, since that
would imply that the object should lose its reference to the object.
Thus, a perfectly obvious and ubiquitous example would be
std::auto_ptr<>.  You should never be able to move from a const
auto_ptr<>.

 > I also argued that const correctness was not generally
 > difficult to maintain, but, in all honesty, it is a
 > significant amount of work to maintain it. Just off the
 > top of my head I would say that about 10% of syntax errors
 > are const related errors. (And the large majority of them
 > only show an error in const correctness, rather than
 > offering any productive value.)

Do you say that because you've written the code in a const-free
form and obtained correct programs, or because the programs that
forced you to use const-correctness turned out to be correct?
Make sure you don't infer causality in the wrong direction.

 > Often some of the strangest syntax errors arise from const
 > correctness errors, and they sometimes take some
 > detangling. Further, many external libraries one uses
 > often have const correctness errors in them, which have
 > to be eliminated by tangled casts and inappropriate
 > mutables.
 > [...]

Is that the fault of const or people who don't use it correctly?
Does that fact that people abuse pointers imply that pointers are
a flawed language concept that should be removed?  (Well, I guess
Java has answered that question).

Dave

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/13/2004 10:39:59 AM

"Balog Pal" <pasa@lib.hu> wrote in message news:<41442b67@andromeda.datanet.hu>...

> it's completely natural to add 'const' wherever your
> *intention* is not to change the object.  (And I observe it is the case for
> the majority of situations.)

Agreed. And therefore const correctness would be simpler and programs
would be shorter if "const" were the default, and "var" modifier to
designated modifiable items. If not for backwards compatibility ...

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply clemd 9/13/2004 7:59:34 PM

Francis Glassborow <francis@robinton.demon.co.uk> wrote in message news:<dp1IEGJ4AtQBFwu7@robinton.demon.co.uk>...
> In article <642e01d8.0409101150.63271032@posting.google.com>, Jessica B 
> <jessica_boxer@yahoo.com> writes
> You mean you have never had to deal with accidental attempts to modify a 
> string literal?

Perhaps once or twice. I rarely use string literals
anyway, since I produce shrink-wrap software that must
be localized, meaning text strings usually come from
an external source into a string variable.

Let me start by clarifying the question. If is not
whether const is useful or necessary, clearly it is.
It is whether const correctness throughout the system
is necessary or, more specifically, a net benefit.

> The concept of const in C++ is also essential to 
> allowing the passing of object by reference when the object must not be 
> changed by the function being called. Again this is an issue for C++ but 
> not for C.

But that begs the question. You say const is essential
for that, which is undoubtedly true, but the goal you set
out (making in immutable) is the very matter under question.

> Just because 10% of your syntax errors are caused by breaches of const 
> correctness does not make const correctness hard to maintain but it does 
> mean that the compiler is identifying places in your code where you are 
> doing something suspect.

But if, as the premise of the thread suggests, const correctness
adds little value, then the extra work involved in
the aforementioned syntax problems is still significant
enough to offset any minimal benefit conveyed. Of course
that is a judgment call. Const obviously is going to catch
some bugs, with a cost of n hours work saved, and the
challenge of maintaining cost is m hours. The question is
whether in practice n > m. If n -> 0, and m>0 then we
have an answer.

> >Often some of the strangest syntax errors arise from const
> >correctness errors, and they sometimes take some
> >detangling. Further, many external libraries one uses
> >often have const correctness errors in them, which have
> >to be eliminated by tangled casts and inappropriate
> >mutables.
> 
> So? Incompetence in developers may be a reason not to buy their products 
> but it certainly isn't a reason to allow their incompetence by changing 
> the language.

I find that a curious statement. What if you have no
choice but to use a particular library (as is often
the case in many, many situations)? What does one do
then? Toss the project out of some sense of linguistic
purity?

This is not some meaningless debate either. There are
many such real world problems. For example, one of the
most widely used libraries in the world, the Microsoft
Foundational Class library has at least a half a dozen
const correctness issues I know of. Should I not use it
because of that reason? Should I really write a mega
wrapper around it?

Further I can think of several features added to the
language to facilitate the use of errant or old libraries
such as extern "C" or the use of the "using namespace std"
idiom, both of which fall under this category, and that's
what I can think of off the top of my head. (One facilitates
the linking of old C libraries, and the other facilitates
old code that does not use namespace qualification.)

Anyway, the question is not one of changing the language
rather it is a question of whether a particular feature
of the language should be used. And that is an entirely
different thing.

> But like exception safety, it isn't a problem once one changes ones 
> coding habits.

But that isn't true. Regardless of ones coding habits
it still takes extra cognitive effort to maintain
const correctness. And I might add that to suggest that
providing exception safety is easy once one has good
habits is not a viewpoint I agree with at all. If I
remember correctly Stroustrup wrote a whole appendix
in "The C++ Programming Language" on the subtleties of 
exception safety in the standard library.

I find it curious indeed that despite the fact that
there is almost universal acceptance of the dogma
that const correctness is key to great software, (and
I am an priest of that church also), that there is
no good quality list of reasons why.

For many of the other good software practices I use 
I can clearly articulate a list of reasons for their
use, that are clear, irrefutable, and legion. For
example, can you give me a list of reasons to prefer
exceptions over error return codes? I can give you
at least a dozen excellent reasons. What about
encapsulation? Why shouldn't I use protected member
variables? Why should I avoid multiple inheritance?
Why should I prefer the standard library over roll
your own code? I can give lists of reasons for these
that are simply beyond question. However, const
correctness seems different.

Here are the reasons I have heard here:

* "it is needed for embedded programming", sure,
  you need to put stuff in ROM, but that hardly
  applies to programming in general, and seems more
  like the sort of thing that the toolset should deal
  with, in a pragma for example;

* "bind refs to temps need it" which seems to me to
   be something that can be dealt with locally rather
   than globally (i.e., const is useful, const correctness
   not so much);

* "const pi = 3.14" again is important, but local;

* "const provides opportunities for optimization", which
  it does in theory, however, I doubt it really translates
  into anything significant in practice. For example:

  class C { int f() const; };
  int test(const C c)
  {
    return c.f() + c.f();
  }

  Would any compiler actually optimize out the second call to
  f? I doubt there is a complier in existence that would. I
  may be wrong, however, perhaps there are some aggressive
  compilers that do. However, make c non const, and there is
  no realistic way to expect a compiler to eliminate the second
  call. For optimization to succeed requires a lot of stars
  to line up just right. As I say, I doubt it happens much
  in the real world. (Of course except for constant folding
  const pi = 3.14; but that example doesn't count since it
  doesn't require systematic const correctness to use.)


Even Myers in his Tips books says const correctness is
crucial, but doesn't really give a good explanation of
why.

I feel unsatisfied. Perhaps my problem is that after
reading the article linked below I wonder if the whole
concept of static type checking is more effort than it
is worth (written by Java maven Bruce Eckel -- not some
inexperienced kid from college.)

<http://mindview.net/WebLog/log-0025>

This article basically argues that static type checking
is a test applied by the compiler to test a small subset
of the correctness of a program. However, the cost of
maintaining and implementing it is large enough that it
slows the programmer down reducing the amount of unit
testing that can be done, and bang for buck, unit testing
is much more valuable than static type checking for
validating your software. To use pseudo math:

For some project P,
n is the number of hours saved by static type checking
m is the number of hours cost maintaining static checks
and f(x) is the number of hours saved by spending x hours
writing and running unit tests (which presumably declines
exponentially as x -> infinity.)

Static type checking is inefficient if

    n < f(m)

and it is Eckel's contention in the above article that
n is much smaller than f(m). If he is right, that is a
pretty scary result.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply jessica_boxer 9/13/2004 8:00:33 PM

In article <2qjhk1F102644U1@uni-berlin.de>, Pavel Vozenilek 
<pavel_vozenilek@yahoo.co.uk> writes
>I guess the experience is more about const qualified member
>functions than variables.

But the two necessarily go hand-in-glove. The concept of immutable 
objects only works if we have a way to guarantee that they are not going 
to be violated.

>
>Const qualifier for member function helps
>to catch errors (though not as often I would like)
>but single change somewhere deep in code can
>propagate across whole project with dozens of places
>needing update.

Indeed, correcting a design fault often has that property.

-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/13/2004 8:01:57 PM

In article <3a11d.179552$Fg5.58279@attbi_s53>, Walter 
<walter@digitalmars.nospamm.com> writes
>In this case, using const as the 'top level', const is essentially being
>used as a storage class, and it can be put in ROM. I was referring, however,
>to things like pointer to const. The optimizer cannot rely on whatever the
>pointer is pointing to being the same value after, say, a function call or
>an assignment through another pointer.

But it is hard to have one without the other. We could do what C does 
with string literals (makes them semantically immutable, but provides no 
syntax support) but there is a long history of bugs with that approach 
that only manifest when code is ported to a system that actually uses 
some form of write protected area for const objects.

>
>
> >We even introduced an extra keyword, mutable,
> > in order to deal with the case of logically const objects that would
> > still have a mutable element.
>
>I've always been bemused by mutable - it seems to be driven by the notion
>that const things are better, so we'll enable things to be declared as const
>even though they're not <g>. Isn't mutable is an admission of the semantic
>failure of const? Maybe there's a compelling argument for it I haven't seen.

How compelling? When seeking to produce high performance code it is not 
unusual to cache results for future use. I.e. compute on first demand, 
reuse subsequently. mutable was introduced to support that design 
choice. It permits a certain design option without requiring its use. 
Caching results is an implementation detail, but the compiler needs to 
know about such so that it uses a suitable mechanism to avoid breaking 
code that uses in object caching.

>
>
> > The only people I know that have a problem with const are those that
> > come from a C background. Novices quickly learn to write const correct
> > code if correctly introduced to the concept.
>
>I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and I
>know how to write const correct code. I just haven't found a practical
>benefit to it - it doesn't find real bugs, it doesn't look good, and it
>doesn't help code generation.

But for comparison you would have to be able to write const incorrect 
code that worked. Only then could you claim that const correctness did 
nothing to help avoid bugs. Note that I said 'avoid' not 'find'. A whole 
class of bugs are simply not possible in C++ (well the prime surviving 
case is with string literals)

>

-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/13/2004 8:02:19 PM

Jessica B wrote:

> I was having an argument recently with a fellow programmer.
> His take was that const correctness does not add significant
> value to your software, and, given that it is quite burdensome
> to maintain, he questioned whether it should be ignored.
> 
> My primary argument in its favor is that it gives additional
> information to the compiler about your intentions, which
> can allow the compiler to detect bugs, however, on challenge
> I really couldn't remember an example of a bug that it
> prevented (from my own practical expereience.)
> 
> I also argued that const correctness was not generally
> difficult to maintain, but, in all honesty, it is a
> significant amount of work to maintain it. Just off the
> top of my head I would say that about 10% of syntax errors
> are const related errors. (And the large majority of them
> only show an error in const correctness, rather than
> offering any productive value.)
> 
> Often some of the strangest syntax errors arise from const
> correctness errors, and they sometimes take some
> detangling. Further, many external libraries one uses
> often have const correctness errors in them, which have
> to be eliminated by tangled casts and inappropriate
> mutables.
> 
> I wonder if any of you can offer some convincing arguments
> for the effort involved in const correctness. Or perhaps
> offer some class of bug caught by it use.

I think there is some truth in both viewpoints.

It is true that const can catch bugs, but this is not the main merit of 
const, IMHO. Firstly, someone else already mentioned that you may be 
forced to use const because of the rule that temporaries can't be bound 
to non-const references.

The second merit of const is to me that it can reveal design problems. 
As an example, I once developed a serialization scheme that relied on an 
abstract base class from which all polymorphic classes thad need to be 
serialized are derived. I decided that both reading and writing (loading 
and storing / deserializing and serializing, whatever you prefer to call 
it) were to be done by a single virtual function serialize(), as this 
appeared to make the whole notation more concise. What I overlooked was 
that writing is an operation that leaves the object unchanged whereas 
reading changes it. This led to a number of problems further down the 
line, for example if you wanted to store only (for example for logging), 
you still had to omit const qualifiers or use const_cast. It gradually 
transpired that the decision to handle both in the same function was 
misguided. Separating it into two independent virtual functions rendered 
a cleaner design, and allowed const-correctness.

In a different guise, you have discovered the problem yourself when you 
tried to use external libraries and got into const-correctness trouble. 
This is exactly what would have happened if I had sold you my original 
serialization system as a library. You would have been forced to use 
mutable and const_cast at inappropriate places.

Note that this has nothing to do with bugs, my original code wasn't more 
buggy than the corrected one. Therefore I consider const to be more 
valuable as a means of ensuring correct design than to ensure correct 
usage, although both are related to each other.

Still, const has its problems. See, for example, the trickery needed 
occasionally to convert from const_iterators to iterators in the 
standard library. Things can become quite subtle with classes that 
represent references to other objects (smart pointers, iterators and 
similar), as const-ness of the reference is something quite different 
from const-ness of the referred object. The average programmer is 
unlikely to have a grasp of all those subtleties involved.

So in my experience striving for const-correctness is still useful, as 
it has a tendency to uncover design flaws, even though it can be quite 
involved at times. If you are dealing with external libraries that 
contain enough const-correctness errors to make your life difficult, 
however, there may be a point where you are practically forced to 
abandon the ideal. Also, trying to retrofit const-correctness to an 
existing design can be near hopeless.

It also follows from this that if you are in the business of providing 
libraries, you should in all cases strive for const-correctness as 
otherwise you would force your decision onto your users. If you are just 
using libraries, a more "pragmatic" approach may be viable. This is one 
reason why library programming is harder than application programming.

-- 
Cheers
Stefan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stefan 9/13/2004 8:19:23 PM

jessica_boxer@yahoo.com (Jessica B) wrote in message news:<642e01d8.0409101150.63271032@posting.google.com>...
> I was having an argument recently with a fellow programmer.
> His take was that const correctness does not add significant
> value to your software, 
Hi Jessica,

I think he is wrong.

> and, given that it is quite burdensome
> to maintain, he questioned whether it should be ignored.
If You use const correctness, it is no problem and nearly no work to
maintain it. If it is Your Job to change an application without const
to const corrrectness it is burdensome of course - but perhaps You
find some 'errors' ;-)

> My primary argument in its favor is that it gives additional
> information to the compiler about your intentions, which
> can allow the compiler to detect bugs, however, on challenge
> I really couldn't remember an example of a bug that it
> prevented (from my own practical expereience.)
Ok - I myself have difficulties to find convincing arguments for const
correctness. Mainly in such cases where a program is written without
const and people think about to change this. The other posters in this
thread have given the usual arguments.

But some months ago I have made an amazing experience. I had to do
some work in an application without const and I tried to understand
what the program is doing. I found it very difficult to understand
because I can't see in many cases whether objects will be changed or
not.
Example:
  SomeType obj;
  int rc = someFunction( obj );
  // -- more code, manipulating 'obj'
Here I ask myself, wether 'obj' is changed. The definition of the
function was without const:
  int someFunction( SomeType& obj );
... and in this function there was another call to someFunction2 in the
same way; and so on. After searching through 3 or 6 files, I found
there was no change - 'obj' was only called with a get-method.
This happens rather often and I found I spend a much of time with this
needless stuff.

In a statistic I have read, that programmers spend up to 70% of there
working time to read code - in the most cases code they have not
written themselves.

So 'const correctness' helps me to save time and money!

Greetings
Werner

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply werner 9/13/2004 8:25:43 PM

Walter wrote:

> "Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
> news:1A2S+CRwpCRBFwxc@robinton.demon.co.uk...
>  > You can't be serious:-)
>  >
>  > double const pi(3.14159265);
>  >
>  > is a very different beast to
>  >
>  > double value(2.1);
>  >
>  > The compiler 'knows' that unless the programmer does something silly
>  > like take the address of pi that it need not provide storage for it,
>  > however in the case of value, it better provide storage unless it can
>  > categorically prove that value never ever changes.
>  >
>  > Equally, it is fully entitled to ROM objects that are declared as const
>  > and if the programmer tries to change the object the programmer is
>  > wrong, not the compiler.
> 
> In this case, using const as the 'top level', const is essentially being
> used as a storage class, and it can be put in ROM. I was referring, however,
> to things like pointer to const. The optimizer cannot rely on whatever the
> pointer is pointing to being the same value after, say, a function call or
> an assignment through another pointer.

For a simple optimizer that's true.  In specific cases, of course,
smarter optimizers could do better.  (And if we combine const with
restrict, the optimizer gets another boost.)

>  >We even introduced an extra keyword, mutable,
>  > in order to deal with the case of logically const objects that would
>  > still have a mutable element.
> 
> I've always been bemused by mutable - it seems to be driven by the notion
> that const things are better, so we'll enable things to be declared as const
> even though they're not <g>.

Glad you have a <g> there.  mutable doesn't enable that.  It
enables objects that *are* logically const to be declared as
such, even when some of their physical state is mutable.  It
allows us to fine-tune the default assumption of C++ that
physical state of logically const objects is immutable.

> Isn't mutable is an admission of the semantic
> failure of const? 

A way to get closer to enforcement of logical const-ness with, yes.
Const fails to give compilers magical powers.

> Maybe there's a compelling argument for it I haven't seen.

The distinction between logical constness (which is something in the 
land of meaning, hard to communicate to a compiler) and physical 
constness (which can be communicated to a compiler and enforced by it)? 
  No, I'd have thought you'd understand that.  And yet that is the 
compelling reason for allowing mutable physical state in logically 
immutable objects.

>  > The only people I know that have a problem with const are those that
>  > come from a C background. Novices quickly learn to write const correct
>  > code if correctly introduced to the concept.
> 
> I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and I
> know how to write const correct code. I just haven't found a practical
> benefit to it - it doesn't find real bugs, it doesn't look good, and it
> doesn't help code generation.

It does find bugs, it does look good to me, and it can help code 
generation.  It certainly documents intent nicely, and helps
programmers to consider issues of mutability -- and as such it
*does* catch or even prevent design flaws as well as coding
errors.

I think you meant: Walter hasn't found it to find "real" bugs, Walter 
doesn't think that it looks good, and it doesn't help code generation as 
much as some people would think.

>  > Now the problem is that const is an overloaded keyword. At top level it
>  > effectively means immutable (and so ROMable)
> 
> D keeps this meaning, and so making const a storage class makes sense.

Making it more like "final" in Java, which is a poor man's const.
I use final extensively when writing Java code, but then you get
into the immutability wrapper classes to substitute for not having
const.  Does D have a better solution?

-- James

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply James 9/13/2004 8:31:06 PM

"Walter" <walter@digitalmars.nospamm.com> writes:

>  >We even introduced an extra keyword, mutable,
>  > in order to deal with the case of logically const objects that would
>  > still have a mutable element.
>
> I've always been bemused by mutable - it seems to be driven by the notion
> that const things are better, so we'll enable things to be declared as const
> even though they're not <g>. Isn't mutable is an admission of the semantic
> failure of const? Maybe there's a compelling argument for it I haven't seen.

Not at all.  There are things that are "logically const" (i.e. from
the outside) but have an internal cache that needs to be updated even
for externally-const operations.  You can think of it as an
optimization for something that would otherwise have to be
dynamically-allocated and held by a pointer.  Allocating a bool on
the heap just to escape from the object's const-ness seems perverse
to me.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/13/2004 8:37:28 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<3a11d.179552$Fg5.58279@attbi_s53>...
> "Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
> news:1A2S+CRwpCRBFwxc@robinton.demon.co.uk...
>  >We even introduced an extra keyword, mutable,
>  > in order to deal with the case of logically const objects that would
>  > still have a mutable element.
> 
> I've always been bemused by mutable - it seems to be driven by the notion
> that const things are better, so we'll enable things to be declared as const
> even though they're not <g>. Isn't mutable is an admission of the semantic
> failure of const? Maybe there's a compelling argument for it I haven't seen.
> 

Mutable is an admission that logical constness is not always
equivalent to physical constness, and that it is useful to be able to
express the difference in a way that the compiler can understand.

The usual example given is that of a class that uses some sort of
cache internally to store the results of a time consuming operation
(reading from a file, querying a database, whatever).  While the
operation itself may be physically const, the act of updating the
cache can't be.  However, the implementation detail of whether a cache
exists at all is generally irrelevant to users of the class.  For
them, a method that doesn't modify the externally visible state of the
object /is/ logically const.  Mutable and const allow us to express
that, and enable the compiler to do the drudge work of verification on
our behalf (just as with any other kind of static type information).

Scott McCaskill

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply scott_mccaskill 9/13/2004 8:40:50 PM

"James Kanze" <kanze@gabi-soft.fr> wrote in message
news:m2pt4rmssh.fsf@lns-vlq-32-82-253-207-17.adsl.proxad.net...
> At any rate, given the current rules, you really don't have the choice
> in general.  The choice you might have is whether to use const, and
> enforce const correctness, for entity objects, i.e. objects you pass by
> reference or pointer, and never copy.  Even their, all things
> considered, I prefer the consistency of const correctness.

That's true, const is too deeply embedded in C++ for it to be credible that
it could be removed or revamped.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/13/2004 8:49:33 PM

brangdon@cix.co.uk (Dave Harris) wrote in message news:<memo.20040912173228.916A@brangdon.m>...

    <snip>

> ... I am especially bothered by the frequent need to replicate code, to
> have const and non-const versions of the same function.
> 
> -- Dave Harris, Nottingham, UK

    I'm surprised that one might frequently need const and non-const
    versions of a function.

    Assuming the semantics are the same, I generally find that if a function
    modifies its object/argument(s), it doesn't make sense to have
    a const version, while if it doesn't, I only need the "const"
    version.

    The main counter-example I know of is when you want the const
    version to return a value and the non-const version to return
    a proxy for updating the value (e.g., vector<bool>.)

    Can you give some examples of where you would need distinct, but
    logically equivalent, const and non-const versions?

-- Alan McKenney
alan_mckenney1@yahoo.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply alan_mckenney1 9/13/2004 9:03:43 PM

> used as a storage class, and it can be put in ROM. I was referring, however,
> to things like pointer to const.

Well, given the many uses of const perhaps you should be more
specific up front?

> I've always been bemused by mutable - it seems to be driven by the notion
> that const things are better, so we'll enable things to be declared as const
> even though they're not.

It is driven by the very practical and core notion of separating
interface from implementation. We often think of objects as a
thing with a set of behaviors rather than a thing with a particular
implementation. For example, I think of my lab bench incubator
as maintaining a constant temperature of 37 C. I don't care
what it has to do (which is far from constant) to maintain that
temperature. Though it is constantly monitoring and controlling
the temperature, in my mind it has the logically constant behavior
of constant temperature. (At least until such time and context
as I need to think about it's implementation or other behaviors
such as setting the temperature or opening the door.)

As a programming example consider this interface for random
variables similar to those which I have used many times in
practice :

// elsewhere typedef double real

class random_variable {

    // implementation certainly with mutable members

    public :

        real mean     ( ) const ;  // return mean
        real variance ( ) const ;  // return variance
 
        real operator()() const ;  // return random value

        void mean     ( real )  ;  // set the mean
        void variance ( real )  ;  // set the variance

        // ... etc

} ;

The logical const'ness of a random variable is that the
distribution of the variable does not change though it's
current value is random and thus changes.

> Isn't mutable is an admission of the semantic
> failure of const?

Not in my opinion and experience.

> Maybe there's a compelling argument for it I haven't seen.

I'm sure there are far more compelling arguments than mine.
However, from a practical standpoint from learning C++ with
only a little experience in C I've found const extremely
useful. Useful for both documenting as well as enforcing an
interface differently in different contexts as well as
catching errors in program logic.

>  > The only people I know that have a problem with const are those that
>  > come from a C background. Novices quickly learn to write const correct
>  > code if correctly introduced to the concept.

I second that sentiment being one of those with little serious
C background who quickly learned to write and appreciate const
correct code.

> I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm),

It shows :))

> know how to write const correct code. I just haven't found a practical
> benefit to it - it doesn't find real bugs, it doesn't look good, and it
> doesn't help code generation.

It has helped me find real bugs and it absolutely does help
with code generation even if many compilers aren't taking
advantage of it. Why do you keep claiming it doesn't help
with code generation?

As for not looking good, well of course this is subjective.
Do you think the interface I outlined above was terribly
cluttered by the const's? I've never found it that intrusive.

> D keeps this meaning, and so making const a storage class makes sense.

We always come back to D with you :))

And would this storage class apply to user defined types?
If so, how would you indicate which part of the interface
is valid for const storage? Or do you prefer the more
restrictive compiler checked "physical const'ness" rather
than the current more flexible notion of "logical const'ness"?

Keith

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply duggar 9/13/2004 9:04:11 PM

"Stefan Heinzmann" <stefan_heinzmann@yahoo.com> wrote in message
news:ci41hh$a9a$07$1@news.t-online.com...

 > As an example, I once developed a serialization scheme that relied on an
 > abstract base class from which all polymorphic classes thad need to be
 > serialized are derived. I decided that both reading and writing (loading
 > and storing / deserializing and serializing, whatever you prefer to call
 > it) were to be done by a single virtual function serialize(), as this
 > appeared to make the whole notation more concise. What I overlooked was
 > that writing is an operation that leaves the object unchanged whereas
 > reading changes it.

And where's the overlook?  I too use such a schema. Let's see the
alternatives:

A)
void SerializeIn(T& );
void SerializeOut(const T& );
B)
void Serialize(T&, bool dir_out);

The implementation of the functions is generally dumb -- a list of all
members and superclasses passed to Serialize. Certainly for A) the list must
perfectly match. So we have a redundancy problem there.  That problem is
eliminated in B).

But we face the problem of serializing out a const object. Is that a real
problem? Just add:

template<typename T>
inline void SerializeOut(const T& obj)
{
     Serialize(static_cast<T&>(obj, true)); // safe, no mutation with true
}

and there you go.   For hierarchy the picture is the same, just T is
implicit, and instead of template you have SerializeOut as nonvirtual member
in the root class.   (SerializeIn can be created in a similar way as
syntactic sugar.)

 > This led to a number of problems further down the
 > line, for example if you wanted to store only (for example for logging),
 > you still had to omit const qualifiers or use const_cast.

What's indeed a problem if not abstracted to a proper single place.


 >In a different guise, you have discovered the problem yourself when you
 >tried to use external libraries and got into const-correctness trouble.
 >This is exactly what would have happened if I had sold you my original
 >serialization system as a library. You would have been forced to use
 >mutable and const_cast at inappropriate places.

Hmm, maybe THAT is the myth/problem with fighting const bugs or type system
bugs -- everyone thinks the one and only true "solution" to stuff in a
c-style cast where the compiler stops with error.  Then no wonder the
outcome is suboptimal.

Paul






      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Balog 9/14/2004 12:11:21 PM

"Keith H Duggar" <duggar@mit.edu> wrote in message
news:b47de02.0409131119.746d8f64@posting.google.com...
 > It has helped me find real bugs and it absolutely does help
 > with code generation even if many compilers aren't taking
 > advantage of it. Why do you keep claiming it doesn't help
 > with code generation?

An example of where it would help would be illustrative - something more
than the top-level optimization:

     const a = 3;
     b = a;        // optimizer rewrites as "b = 3;"

I'm always looking for new and better ways to optimize. Here's a canonical
example of where it cannot be optimized:

     void foo(int *p, const int *q)
     {
         int i = *q;
         *p = 3;        // essentially any assignment through any pointer,
any modification of a static variable,
                             // or any function call since we don't know what
q may be aliased to
         int j = *q;    // cannot rewrite as "j = i;"
     }


 > As for not looking good, well of course this is subjective.

Of course.


 > > D keeps this meaning, and so making const a storage class makes sense.
 > We always come back to D with you :))

Naturally, since having a practical and freely available implementation of
these notions means anyone can evaluate and compare with real code, rather
than theory.


 > And would this storage class apply to user defined types?
 > If so, how would you indicate which part of the interface
 > is valid for const storage? Or do you prefer the more
 > restrictive compiler checked "physical const'ness"

Yes, I prefer the notion of "ROMable" for const. It's straightforward,
unambiguous, unsubtle, and useful. For another point to this, see the new
thread here entitled
"C++ Gotcha #32 - Misunderstanding Pointer-to-Pointer-to-Const Conversion"



 > rather than the current more flexible notion of "logical const'ness"?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/14/2004 12:13:50 PM

"Scott McCaskill" <scott_mccaskill@yahoo.com> wrote in message
news:4d4ad750.0409130854.2ad13652@posting.google.com...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
news:<3a11d.179552$Fg5.58279@attbi_s53>...
 > > Maybe there's a compelling argument for it I haven't seen.
 >
 > Mutable is an admission that logical constness is not always
 > equivalent to physical constness, and that it is useful to be able to
 > express the difference in a way that the compiler can understand.
 >
 > The usual example given is that of a class that uses some sort of
 > cache internally to store the results of a time consuming operation
 > (reading from a file, querying a database, whatever).  While the
 > operation itself may be physically const, the act of updating the
 > cache can't be.  However, the implementation detail of whether a cache
 > exists at all is generally irrelevant to users of the class.  For
 > them, a method that doesn't modify the externally visible state of the
 > object /is/ logically const.  Mutable and const allow us to express
 > that, and enable the compiler to do the drudge work of verification on
 > our behalf (just as with any other kind of static type information).

Thank-you, that is an excellent explanation of the rationale behind mutable,
even though I am not yet compelled <g>.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/14/2004 12:16:18 PM

"David Abrahams" <dave@boost-consulting.com> wrote in message
news:uacvu5ih6.fsf@boost-consulting.com...
 > "Walter" <walter@digitalmars.nospamm.com> writes:
 > > I've always been bemused by mutable - it seems to be driven by the
notion
 > > that const things are better, so we'll enable things to be declared as
const
 > > even though they're not <g>. Isn't mutable is an admission of the
semantic
 > > failure of const? Maybe there's a compelling argument for it I haven't
seen.
 >
 > Not at all.  There are things that are "logically const" (i.e. from
 > the outside) but have an internal cache that needs to be updated even
 > for externally-const operations.  You can think of it as an
 > optimization for something that would otherwise have to be
 > dynamically-allocated and held by a pointer.  Allocating a bool on
 > the heap just to escape from the object's const-ness seems perverse
 > to me.

I understand what you mean about "logical" constness vs actual constness,
and the utility of using a cache. But the options are not limited to mutable
or allocating a pointer - you could just declare the function as not taking
a const reference. Is that really so bad?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/14/2004 12:16:40 PM

alan_mckenney1@yahoo.com (Alan McKenney) writes:

 > Can you give some examples of where you would need distinct, but
 > logically equivalent, const and non-const versions?

Any time you return a reference to some member variable of a type that
has both a useful const and non-const interface, this idiom pops
up. Witness std::vector::operator[].

-- 
Steven E. Harris

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Steven 9/14/2004 12:17:57 PM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:V1wKTPSq6XRBFwGQ@robinton.demon.co.uk...
 > In article <3a11d.179552$Fg5.58279@attbi_s53>, Walter
 > <walter@digitalmars.nospamm.com> writes
 > >In this case, using const as the 'top level', const is essentially being
 > >used as a storage class, and it can be put in ROM. I was referring,
however,
 > >to things like pointer to const. The optimizer cannot rely on whatever
the
 > >pointer is pointing to being the same value after, say, a function call
or
 > >an assignment through another pointer.
 > But it is hard to have one without the other. We could do what C does
 > with string literals (makes them semantically immutable, but provides no
 > syntax support) but there is a long history of bugs with that approach
 > that only manifest when code is ported to a system that actually uses
 > some form of write protected area for const objects.

I think that only reinforces my point of const not finding bugs.


 > > >We even introduced an extra keyword, mutable,
 > > > in order to deal with the case of logically const objects that would
 > > > still have a mutable element.
 > >I've always been bemused by mutable - it seems to be driven by the notion
 > >that const things are better, so we'll enable things to be declared as
const
 > >even though they're not <g>. Isn't mutable is an admission of the
semantic
 > >failure of const? Maybe there's a compelling argument for it I haven't
seen.
 > How compelling? When seeking to produce high performance code it is not
 > unusual to cache results for future use. I.e. compute on first demand,
 > reuse subsequently. mutable was introduced to support that design
 > choice. It permits a certain design option without requiring its use.
 > Caching results is an implementation detail, but the compiler needs to
 > know about such so that it uses a suitable mechanism to avoid breaking
 > code that uses in object caching.

I don't see how having mutable is better than just saying the object is not
const.


 > > > The only people I know that have a problem with const are those that
 > > > come from a C background. Novices quickly learn to write const correct
 > > > code if correctly introduced to the concept.
 > >I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and
I
 > >know how to write const correct code. I just haven't found a practical
 > >benefit to it - it doesn't find real bugs, it doesn't look good, and it
 > >doesn't help code generation.
 > But for comparison you would have to be able to write const incorrect
 > code that worked. Only then could you claim that const correctness did
 > nothing to help avoid bugs.

Not only do I write const incorrect code that works, the bugs that I do make
are not ones that would have been caught by const correctness. Many C++
innovations, like function prototyping, have been a big debugging
productivity booster for me. Const hasn't.


 > Note that I said 'avoid' not 'find'. A whole
 > class of bugs are simply not possible in C++ (well the prime surviving
 > case is with string literals)

The most common, plug-ugly nasty bugs I have in C++ are related to
uninitialized data. This can happen in many different forms in C++, is very
hard to pick up in code reviews, and is why D goes to considerable lengths
to put a stop to it.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/14/2004 12:19:06 PM

jessica_boxer@yahoo.com (Jessica B) wrote in message news:<642e01d8.0409121819.39070c07@posting.google.com>...
> 
> Let me start by clarifying the question. If is not
> whether const is useful or necessary, clearly it is.
> It is whether const correctness throughout the system
> is necessary or, more specifically, a net benefit.

It's very difficult to quantify const in such a way.

Is good design a net benefit?
Is using C++ a net benefit?
Is writing function formal specifications a net benefit?
Is exception safety a net benefit?
Is unit testing a net benefit?
Is static type checking a net benefit?
Are smart pointers a net benefit?

The answer to all of the above is that you _can_ obtain a net benefit.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply pdimov 9/14/2004 10:41:38 PM

In article <Zvp1d.431066$%_6.332010@attbi_s01>, Walter 
<walter@digitalmars.nospamm.com> writes
> > But it is hard to have one without the other. We could do what C does
> > with string literals (makes them semantically immutable, but provides no
> > syntax support) but there is a long history of bugs with that approach
> > that only manifest when code is ported to a system that actually uses
> > some form of write protected area for const objects.
>
>I think that only reinforces my point of const not finding bugs.

I do not understand your conclusion. Had the type of a string literal in 
C been array of const char we would not have the problem of something 
being semantically immutable tough literally non-const. Of course K&R 
could not have provided that fix because const was still years into the 
future. By the time the C Standard was produced too much existing code 
would have been broken by a change. C++ had to produce a very ugly work 
round in order to support legacy code (largely from C) while ensuring 
correct overload resolution.

Trying to modify a logically const (and therefore potentially ROMmable) 
object is a programming error and the earlier it is detected the better. 
In addition we genuinely do sometimes want to treat const objects 
differently rather than simply limiting what can be done with them, 
hence overloading on the basis of reference to const/non-const.

>
>
> > > >We even introduced an extra keyword, mutable,
> > > > in order to deal with the case of logically const objects that would
> > > > still have a mutable element.
> > >I've always been bemused by mutable - it seems to be driven by the notion
> > >that const things are better, so we'll enable things to be declared as
>const
> > >even though they're not <g>. Isn't mutable is an admission of the
>semantic
> > >failure of const? Maybe there's a compelling argument for it I haven't
>seen.
> > How compelling? When seeking to produce high performance code it is not
> > unusual to cache results for future use. I.e. compute on first demand,
> > reuse subsequently. mutable was introduced to support that design
> > choice. It permits a certain design option without requiring its use.
> > Caching results is an implementation detail, but the compiler needs to
> > know about such so that it uses a suitable mechanism to avoid breaking
> > code that uses in object caching.
>
>I don't see how having mutable is better than just saying the object is not
>const.

You mean you do not believe in implementation hiding? Well, sorry, but I 
cannot agree.


-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/14/2004 10:50:58 PM

>     I'm surprised that one might frequently need const and non-const
>     versions of a function.
> 
>     Assuming the semantics are the same, I generally find that if a function
>     modifies its object/argument(s), it doesn't make sense to have
>     a const version, while if it doesn't, I only need the "const"
>     version.
> 
>     The main counter-example I know of is when you want the const
>     version to return a value and the non-const version to return
>     a proxy for updating the value (e.g., vector<bool>.)
> 
>     Can you give some examples of where you would need distinct, but
>     logically equivalent, const and non-const versions?
> 
> -- Alan McKenney


When class member functions have nothing significant to return, and
instead of of returning void, they return a reference to *this (to
enable "method chaining") . So, we can have:

class A
{
		//...
	public:

	A & foo();
	const A & foo() const;
	volatile A & foo() volatile;
	const volatile A & foo()const volatile;
};

And, while we are at it, why stop at const-correctness? Why not
cv-correctness? While the standard library is const-correct, it is not
cv-correct as there are no volatile and const volatile versions of
functions and types.

--                                                   --
Abstraction is selective ignorance. -Andrew Koenig
--                                                   --

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply kprateek88 9/14/2004 10:52:41 PM

"Walter" <walter@digitalmars.nospamm.com> writes:

> "Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
> news:V1wKTPSq6XRBFwGQ@robinton.demon.co.uk...
>
>  > In article <3a11d.179552$Fg5.58279@attbi_s53>, Walter
>  > <walter@digitalmars.nospamm.com> writes
>  > >In this case, using const as the 'top level', const is
>  > >essentially being used as a storage class, and it can be put in
>  > >ROM. I was referring, however, to things like pointer to
>  > >const. The optimizer cannot rely on whatever the pointer is
>  > >pointing to being the same value after, say, a function call or
>  > >an assignment through another pointer.
>  > 
>  > But it is hard to have one without the other. We could do what C does
>  > with string literals (makes them semantically immutable, but provides no
>  > syntax support) but there is a long history of bugs with that approach
>  > that only manifest when code is ported to a system that actually uses
>  > some form of write protected area for const objects.
>
> I think that only reinforces my point of const not finding bugs.

I have a similar argument all the time with Python programmers.  Some
of them claim that no static checking is ever worth the trouble.  I
think it's probably true that static type checking seldom "finds
bugs."  It does very occasionally, but where I find it indispensable
is:

  a. In making a codebase understandable.  Knowing the types of
     objects that you can pass to a function is crucial.  Arrive at a
     big Python codebase you've never seen before and, unless it's
     been *vigilantly* commented, getting a grip on it is really hard
     (for me, at least).

  b. In refactoring.  I like to make changes and "brace myself against
     the type system", counting on the compiler to find other places
     where I need to make other changes that make the system work
     again.

Admittedly, const-ness is a less important component of the type
system for these purposes than others, but is still important.  Also,
I value the clarity of a "functional programming style."  It's very
useful to know (and enforce) that a parameter value will be the same
near the end of a function as it was at the beginning.  It makes
thinking about the correctness of my program much easier.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/14/2004 11:04:18 PM

"Walter" <walter@digitalmars.nospamm.com> writes:

> "David Abrahams" <dave@boost-consulting.com> wrote in message
> news:uacvu5ih6.fsf@boost-consulting.com...
>  > "Walter" <walter@digitalmars.nospamm.com> writes:
>  > > I've always been bemused by mutable - it seems to be driven by the
> notion
>  > > that const things are better, so we'll enable things to be declared as
> const
>  > > even though they're not <g>. Isn't mutable is an admission of the
> semantic
>  > > failure of const? Maybe there's a compelling argument for it I haven't
> seen.
>  >
>  > Not at all.  There are things that are "logically const" (i.e. from
>  > the outside) but have an internal cache that needs to be updated even
>  > for externally-const operations.  You can think of it as an
>  > optimization for something that would otherwise have to be
>  > dynamically-allocated and held by a pointer.  Allocating a bool on
>  > the heap just to escape from the object's const-ness seems perverse
>  > to me.
>
> I understand what you mean about "logical" constness vs actual constness,
> and the utility of using a cache. But the options are not limited to mutable
> or allocating a pointer - you could just declare the function as not taking
> a const reference. Is that really so bad?

Whether or not enforcing and representing logical constness in
function signatures is worthwhile is a different argument.  If you buy
that it's worthwhile -- I do; apparently you don't -- then mutable is
not an admission of the failure of const.  It has a useful and
easily-explained place in the type system.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/14/2004 11:05:00 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<pFp1d.431122$%_6.152211@attbi_s01>...
> "David Abrahams" <dave@boost-consulting.com> wrote in message
> news:uacvu5ih6.fsf@boost-consulting.com...
>  > "Walter" <walter@digitalmars.nospamm.com> writes:
>  > > I've always been bemused by mutable - it seems to be driven by the
>  notion
>  > > that const things are better, so we'll enable things to be declared as
>  const
>  > > even though they're not <g>. Isn't mutable is an admission of the
>  semantic
>  > > failure of const? Maybe there's a compelling argument for it I haven't
>  seen.
>  >
>  > Not at all.  There are things that are "logically const" (i.e. from
>  > the outside) but have an internal cache that needs to be updated even
>  > for externally-const operations.  You can think of it as an
>  > optimization for something that would otherwise have to be
>  > dynamically-allocated and held by a pointer.  Allocating a bool on
>  > the heap just to escape from the object's const-ness seems perverse
>  > to me.
> 
> I understand what you mean about "logical" constness vs actual constness,
> and the utility of using a cache. But the options are not limited to mutable
> or allocating a pointer - you could just declare the function as not taking
> a const reference. Is that really so bad?
> 

Yes, because that undermines the ability of the compiler to assist you
in keeping track of when a thing should be able to be modifed and when
it should not, which is the whole point of const.

Scott McCaskill

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply scott_mccaskill 9/15/2004 12:57:11 AM

"David Abrahams" <dave@boost-consulting.com> wrote in message
news:uhdq0sxlx.fsf@boost-consulting.com...
 > Admittedly, const-ness is a less important component of the type
 > system for these purposes than others, but is still important.  Also,
 > I value the clarity of a "functional programming style."  It's very
 > useful to know (and enforce) that a parameter value will be the same
 > near the end of a function as it was at the beginning.  It makes
 > thinking about the correctness of my program much easier.

I understand what you mean. Where we disagree is the value in the very weak
notion of const in C++.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/15/2004 9:38:57 AM

Walter <walter@digitalmars.nospamm.com> wrote in message
news:3a11d.179552$Fg5.58279@attbi_s53...
 >
 > "Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
 > news:1A2S+CRwpCRBFwxc@robinton.demon.co.uk...

[snip]

 >  > The only people I know that have a problem with const are those that
 >  > come from a C background. Novices quickly learn to write const correct
 >  > code if correctly introduced to the concept.
 >
 > I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and
I
 > know how to write const correct code. I just haven't found a practical
 > benefit to it - it doesn't find real bugs, it doesn't look good, and it
 > doesn't help code generation.

So FORTRAN taught you nothing of the perils of pass by non-const reference?

Louis.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Louis 9/15/2004 9:39:19 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:YgpZG5gMauRBFwWs@robinton.demon.co.uk...
 > > > How compelling? When seeking to produce high performance code it is
not
 > > > unusual to cache results for future use. I.e. compute on first demand,
 > > > reuse subsequently. mutable was introduced to support that design
 > > > choice. It permits a certain design option without requiring its use.
 > > > Caching results is an implementation detail, but the compiler needs to
 > > > know about such so that it uses a suitable mechanism to avoid breaking
 > > > code that uses in object caching.
 > >
 > >I don't see how having mutable is better than just saying the object is
not
 > >const.
 >
 > You mean you do not believe in implementation hiding?

No, I mean that I don't believe in having constant objects that aren't
constant. I do understand the notion of "logical" constants and what they're
for, I just don't agree with others on the importance of such. The
uninitialized data problems in C++ are a far richer source of bugs than
whether something is logically const or not.

[An anecdote about the limits of implementation hiding: a friend of mine was
learning programming for the first time. He read the FORTRAN manual, and
based on it wrote a program that calculated some things and wrote the
results to a file. The program worked, but ran incredibly slowly. Stumped,
he showed it to a colleague, who asked him why, in the inner loop, he opened
the file, appended a character, and closed the file? Didn't he know that
that was terribly inefficient because of how file I/O worked? My friend said
he had no idea, since the manual said nothing about how the implementation
worked.]



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/15/2004 9:51:46 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:pFp1d.431122$%_6.152211@attbi_s01...
 > I understand what you mean about "logical" constness vs actual constness,
 > and the utility of using a cache. But the options are not limited to
mutable
 > or allocating a pointer - you could just declare the function as not
taking
 > a const reference. Is that really so bad?

I forgot another option - one can just cast away const'ness when assigning
to the 'mutable' member.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/15/2004 9:54:28 AM

David Abrahams <dave@boost-consulting.com> wrote in message
news:<uhdq0sxlx.fsf@boost-consulting.com>...

    [...]
> I have a similar argument all the time with Python programmers.  Some
> of them claim that no static checking is ever worth the trouble.  I
> think it's probably true that static type checking seldom "finds
> bugs."  

Just a nit, but I don't quite agree.  I did one large project in Java,
and I was unpleasantly surprised to see just how often the wrong type
managed to slip into a container.  My collegues weren't beginners, by
any means, and we generally tried to document fairly thoroughly.  Never
the less...

Of course, I suspect that there would already be less errors in coding
if the type of the container were spelled std::vector< SomeType > rather
than simply java.util.Vector.  But one way or another, errors do creep
in, no matter how careful you are, and it's always nicer when the
compiler finds them, rather than your unit tests (or worse, integration
tests or a user).

--
James Kanze           GABI Software         http://www.gabi-soft.fr
Conseils en informatique orient�e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S�mard, 78210 St.-Cyr-l'�cole, France, +33 (0)1 30 23 00 34

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply kanze 9/15/2004 6:30:31 PM

In article <5dO1d.178768$9d6.78418@attbi_s54>, Walter 
<walter@digitalmars.nospamm.com> writes
>"Walter" <walter@digitalmars.nospamm.com> wrote in message
>news:pFp1d.431122$%_6.152211@attbi_s01...
> > I understand what you mean about "logical" constness vs actual constness,
> > and the utility of using a cache. But the options are not limited to
>mutable
> > or allocating a pointer - you could just declare the function as not
>taking
> > a const reference. Is that really so bad?
>
>I forgot another option - one can just cast away const'ness when assigning
>to the 'mutable' member.

No it cannot, it is exactly because that does not work safely that 
mutable was introduced. A object that is declared as const can be placed 
in ROM unless it has a mutable member. Without mutable, the compiler has 
no way to know if it is OK to ROM an object, alternatively it has no way 
to identify attempts to modify an object that is in ROM.

The concept of named values (which is, when all is said and done, what a 
const object supports) is important to some programming styles. If you 
want to design a pure OO language, fine, but that is not the design 
concept of C++.


-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/15/2004 6:37:08 PM

Balog Pal wrote:

> "Stefan Heinzmann" <stefan_heinzmann@yahoo.com> wrote in message
> news:ci41hh$a9a$07$1@news.t-online.com...
> 
>  > As an example, I once developed a serialization scheme that relied on an
>  > abstract base class from which all polymorphic classes thad need to be
>  > serialized are derived. I decided that both reading and writing (loading
>  > and storing / deserializing and serializing, whatever you prefer to call
>  > it) were to be done by a single virtual function serialize(), as this
>  > appeared to make the whole notation more concise. What I overlooked was
>  > that writing is an operation that leaves the object unchanged whereas
>  > reading changes it.
> 
> And where's the overlook?  I too use such a schema. Let's see the
> alternatives:
> 
> A)
> void SerializeIn(T& );
> void SerializeOut(const T& );
> B)
> void Serialize(T&, bool dir_out);
> 
> The implementation of the functions is generally dumb -- a list of all
> members and superclasses passed to Serialize. Certainly for A) the list must
> perfectly match. So we have a redundancy problem there.  That problem is
> eliminated in B).

That was my original reasoning, too. I even overloaded operator|() to 
make implementing Serialize() as intuitive as possible. I tried 
overloading operator,() but ran into trouble with the standard library 
implementation (that was Visual C++ 6.0 at the time). I wanted an 
operator precedence that was as low as possible.

However, I found that I needed to distinguish between reading and 
writing inside the serialize() function more often than I expected. That 
was for various reasons, some of which escape my memory now, but 
versioning was one of them. Gradually, the advantage of having to write 
only a single function was shrinking, and the disadvantages became more 
visible. I now think that having to provide two matching functions isn't 
a serious problem.

> But we face the problem of serializing out a const object. Is that a real
> problem? Just add:
> 
> template<typename T>
> inline void SerializeOut(const T& obj)
> {
>      Serialize(static_cast<T&>(obj, true)); // safe, no mutation with true
> }

I suppose you wanted to use a const_cast here, since you're casting away 
const. It is simple and safe, but it is ugly.

> and there you go.   For hierarchy the picture is the same, just T is
> implicit, and instead of template you have SerializeOut as nonvirtual member
> in the root class.   (SerializeIn can be created in a similar way as
> syntactic sugar.)
> 
>  > This led to a number of problems further down the
>  > line, for example if you wanted to store only (for example for logging),
>  > you still had to omit const qualifiers or use const_cast.
> 
> What's indeed a problem if not abstracted to a proper single place.

I don't call this abstraction, I call it hiding ;-)

I think that on second glance, loading and storing a data structure are 
less symmetrical than it appears at first. The const/non-const issue ist 
just one aspect of this. Another asymmetry is that storing the data 
structure leaves the data structure intact, whereas loading often 
creates a new object structure, in other words it needs to instantiate 
objects. For this, it is desirable that the objects are constructed 
directly from serialized data. Your solution would construct a blank 
object and initialize it with the Serialize() function. I think it is 
preferable to do everything in the constructor. For this, your 
SerializeIn function really becomes a constructor that is invoked 
through a factory. If implemented like this, you can also handle 
properly members that are references. It offers an overall cleaner 
solution for initialization. It also makes it much less likely to 
accidentally forget to deserialize a base class or a member.

Another side effect was that I could go back to using operator<<() and 
operator>>() as syntactic sugar for the serialization system, staying 
in-line with the C++ tradition. No invention of new syntax with 
operator,() or operator|() was necessary.

I still don't think that the "symmetrical" version that I implemented 
first and that you advocate is wrong. I just think it is ugly. The first 
scar was the const-correctness issue. Since then, I try to listen more 
carefully to what const problems tell me about the design.

>  >In a different guise, you have discovered the problem yourself when you
>  >tried to use external libraries and got into const-correctness trouble.
>  >This is exactly what would have happened if I had sold you my original
>  >serialization system as a library. You would have been forced to use
>  >mutable and const_cast at inappropriate places.
> 
> Hmm, maybe THAT is the myth/problem with fighting const bugs or type system
> bugs -- everyone thinks the one and only true "solution" to stuff in a
> c-style cast where the compiler stops with error.  Then no wonder the
> outcome is suboptimal.

Sometimes you have no choice. Sometimes you even have no time to 
investigate how to correct the problem in the right way. What do you do 
when the prototype of the write() function in your standard library goes 
like this:

extern int write(int handle, void* buffer, unsigned count);

That's a library bug, but if you can't change it, you need a cast. Or 
would you require a wrapper around the standard library?

But in general you're right. There are many programmers who are much too 
quick with a cast. Maybe it would be better if each cast would cost a 
Euro for the coffee fund.

-- 
Cheers
Stefan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stefan 9/15/2004 6:38:08 PM

On 15 Sep 2004 05:54:28 -0400, "Walter"
<walter@digitalmars.nospamm.com> wrote:

>
>"Walter" <walter@digitalmars.nospamm.com> wrote in message
>news:pFp1d.431122$%_6.152211@attbi_s01...
> > I understand what you mean about "logical" constness vs actual constness,
> > and the utility of using a cache. But the options are not limited to
>mutable
> > or allocating a pointer - you could just declare the function as not
>taking
> > a const reference. Is that really so bad?
>
>I forgot another option - one can just cast away const'ness when assigning
>to the 'mutable' member.

Casting away const'ness can lead to undefined behavior when the
compiler allocates const data in a memory segment marked as read-only
(see my other reply in this thread).

--
Bob Hairgrove
NoSpamPlease@Home.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Bob 9/15/2004 7:29:28 PM

On 15 Sep 2004 05:38:57 -0400, "Walter"
<walter@digitalmars.nospamm.com> wrote:

>
>"David Abrahams" <dave@boost-consulting.com> wrote in message
>news:uhdq0sxlx.fsf@boost-consulting.com...
> > Admittedly, const-ness is a less important component of the type
> > system for these purposes than others, but is still important.  Also,
> > I value the clarity of a "functional programming style."  It's very
> > useful to know (and enforce) that a parameter value will be the same
> > near the end of a function as it was at the beginning.  It makes
> > thinking about the correctness of my program much easier.
>
>I understand what you mean. Where we disagree is the value in the very weak
>notion of const in C++.

Why do you say "weak"? On many platforms, const data can be allocated
within a read-only memory segment by the compiler. Attempting to
modify data in segments marked as read-only can cause an access
violation. IMHO, this is not weak.

--
Bob Hairgrove
NoSpamPlease@Home.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Bob 9/15/2004 7:29:49 PM

"Walter" <walter@digitalmars.nospamm.com> writes:

> "David Abrahams" <dave@boost-consulting.com> wrote in message
> news:uhdq0sxlx.fsf@boost-consulting.com...
>  > Admittedly, const-ness is a less important component of the type
>  > system for these purposes than others, but is still important.  Also,
>  > I value the clarity of a "functional programming style."  It's very
>  > useful to know (and enforce) that a parameter value will be the same
>  > near the end of a function as it was at the beginning.  It makes
>  > thinking about the correctness of my program much easier.
>
> I understand what you mean. Where we disagree is the value in the very weak
> notion of const in C++.

I disagree that it's weak.  It's highly expressive.

How do you write a function in D that doesn't modify its argument, to
which you can pass a const or non-const object?  The const in that
case *can't* indicate "strong physical" const-ness, or you get code
duplication.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/15/2004 7:33:33 PM

"Bob Hairgrove" <invalid@bigfoot.com> wrote in message
news:5cjgk0hq03rk668gjauuoqbgrfoar0qnj1@4ax.com...
> On 15 Sep 2004 05:54:28 -0400, "Walter"
> <walter@digitalmars.nospamm.com> wrote:
>
> >
> >"Walter" <walter@digitalmars.nospamm.com> wrote in message
> >news:pFp1d.431122$%_6.152211@attbi_s01...
> > > I understand what you mean about "logical" constness vs actual
constness,
> > > and the utility of using a cache. But the options are not limited to
> >mutable
> > > or allocating a pointer - you could just declare the function as not
> >taking
> > > a const reference. Is that really so bad?
> >
> >I forgot another option - one can just cast away const'ness when
assigning
> >to the 'mutable' member.
>
> Casting away const'ness can lead to undefined behavior when the
> compiler allocates const data in a memory segment marked as read-only
> (see my other reply in this thread).

Ok, I can see that (Francis made the same argument, and I'll reply to both
here).

But let's combine that with the argument for mutable, that of caching some
computed value. The only way a const object can be put in ROM is if its
values can be computed at compile time. If so, wouldn't its 'mutable' member
be const too? What is a mutable member doing in the midst of a bunch of
compile time constants?

This seems to me to be a rather odd object, and I wonder why it is such an
issue that it would merit its own keyword.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/15/2004 11:32:04 PM

On 15 Sep 2004 15:29:49 -0400, Bob Hairgrove <invalid@bigfoot.com>
wrote:

>Why do you say "weak"? On many platforms, const data can be allocated
>within a read-only memory segment by the compiler. Attempting to
>modify data in segments marked as read-only can cause an access
>violation. IMHO, this is not weak.

The problem is that for example if you see a function
starting with

    void foo(const int& x)

you can't be sure that the value of x at the end of the
function will be the same as at the start of the function
because "const int& x" means that you can't change the
referenced integer *using that reference* but it doesn't
mean that the referenced integer is a constant.
"const" with references and pointers is an attribute of
the reference and pointer and *NOT* of the referenced or
pointed-to object.

In C++ the quite commonly used ugly performance detail of
passing const refs instead of values reinforces this
dangerous logical shift.

Andrea

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrea 9/15/2004 11:32:52 PM

On 13 Sep 2004 06:28:25 -0400, "Walter"
<walter@digitalmars.nospamm.com> wrote:

>I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and I
>know how to write const correct code. I just haven't found a practical
>benefit to it - it doesn't find real bugs, it doesn't look good, and it
>doesn't help code generation.

I've arrived to the very same quite blaspheme conclusion. I cannot
remember a case (even just a single case) in which const-correctness
helped me spotting a logical problem in my code. Not even once.
Every single time the compiler complained about a const-correctness
related problem it was a problem in the const-correctness castle
itself (for example something that should have been accpeting a
"const Foo*" was instead accepting a "Foo *")... so in other words
the const-correctness error was just about const-correctness and
not about the program logic.

On the other side the cost is IMO high... sometimes you're forced
to even duplicate code just to be const-correct. Sometimes things
get terribly ugly (const_iterator).

With little (no) return for the investment (either at runtime or
at programming/debugging time) and high cost I found myself
pondering on why I should waste time on that.

Unfortunately with C++ there's simply no choice about it.
Const-correctness s required by the language... being it
really useful or not.

When writing a program we're not describing the whole reality,
but only the part that is needed to compute the desired result.
Telling to a compiler what is constant and what is not is IMO
a waste of time... in other words it's a minor detail not
worth the words needed for it. Sure for other humans reading
the code there's a little documenting power of the "const" word...
but the logical const-ness of an entity is such a raw concept
that not having some better documentation than just the
declaration of a function is a total disaster anyway.

Of course my personal view is *very* limited... I mostly worked
as a team of one... may be experience of programmers working
in teams of hundreds on programs of tens of millions lines of
code is different.

Andrea

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrea 9/15/2004 11:40:07 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<5dO1d.178768$9d6.78418@attbi_s54>...
> "Walter" <walter@digitalmars.nospamm.com> wrote in message
> news:pFp1d.431122$%_6.152211@attbi_s01...
>  > I understand what you mean about "logical" constness vs actual constness,
>  > and the utility of using a cache. But the options are not limited to
>  mutable
>  > or allocating a pointer - you could just declare the function as not
>  taking
>  > a const reference. Is that really so bad?
> 
> I forgot another option - one can just cast away const'ness when assigning
> to the 'mutable' member.

Casting away constness would result in undefined behavior if the object
is actually const.

#include <iostream>

class WithMutable {
    int mutable _access_count;
    char const* _text;
public:
    WithMutable() : _access_count(0), _text("Hello World!") 
    {
    }
    friend std::ostream& operator<<(
        std::ostream& left, 
        const WithMutable& right) 
    {
        right._access_count++;
        return left << right._text; 
    }
};

class WithoutMutable {
    int _access_count;
    char const* _text;
public:
    WithoutMutable() : _access_count(0), _text("Hello World!") 
    {
    }
    friend std::ostream& operator<<(
        std::ostream& left, 
        const WithoutMutable& right) 
    {
        const_cast<int&>(right._access_count)++; // potential UB
        return left << right._text; 
    }
};

WithMutable const with;
WithoutMutable const without;

int main()
{
    std::cout << with << std::endl;    // OK
    std::cout << without << std::endl; // undefined behavior

    return 0;
}

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply niklasb 9/15/2004 11:41:08 PM

"Walter" <walter@digitalmars.nospamm.com> writes:

> I forgot another option - one can just cast away const'ness when
> assigning to the 'mutable' member.

I worked on a large code base for a few years that was full of such
casts, as one of the compilers did not support the mutable
keyword. Right above every such cast was the commented phrase:

  // I meant /logically/ const!

The mutable keyword validated and alleviated that frustration.

-- 
Steven E. Harris

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Steven 9/15/2004 11:56:57 PM

"David Abrahams" <dave@boost-consulting.com> wrote in message
news:u4qlzpsiq.fsf@boost-consulting.com...
> "Walter" <walter@digitalmars.nospamm.com> writes:
>
> > "David Abrahams" <dave@boost-consulting.com> wrote in message
> > news:uhdq0sxlx.fsf@boost-consulting.com...
> >  > Admittedly, const-ness is a less important component of the type
> >  > system for these purposes than others, but is still important.  Also,
> >  > I value the clarity of a "functional programming style."  It's very
> >  > useful to know (and enforce) that a parameter value will be the same
> >  > near the end of a function as it was at the beginning.  It makes
> >  > thinking about the correctness of my program much easier.
> >
> > I understand what you mean. Where we disagree is the value in the very
weak
> > notion of const in C++.
>
> I disagree that it's weak.  It's highly expressive.
>
> How do you write a function in D that doesn't modify its argument, to
> which you can pass a const or non-const object?

Call by value makes a copy anyway, so that's not an issue. Call by reference
from a const object - this happens rarely since nearly all reference objects
will be on the heap, not in read-only memory. And to reiterate, I've never
had a programming bug where a function modified a readonly object it
shouldn't have. It just doesn't come up.

What does come up often is the nasty uninitialized data problem C++ has in
multiple manifestations. Plugging *those* holes would have saved me hundreds
of frustrating hours over the years.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 9/15/2004 11:59:16 PM

"Bob Hairgrove" <invalid@bigfoot.com> wrote in message
news:j5jgk0l43ma3ff2fbshtq3v6r0fqa2jfmq@4ax.com...
> On 15 Sep 2004 05:38:57 -0400, "Walter"
> <walter@digitalmars.nospamm.com> wrote:
> >"David Abrahams" <dave@boost-consulting.com> wrote in message
> >news:uhdq0sxlx.fsf@boost-consulting.com...
> > > Admittedly, const-ness is a less important component of the type
> > > system for these purposes than others, but is still important.  Also,
> > > I value the clarity of a "functional programming style."  It's very
> > > useful to know (and enforce) that a parameter value will be the same
> > > near the end of a function as it was at the beginning.  It makes
> > > thinking about the correctness of my program much easier.
> >
> >I understand what you mean. Where we disagree is the value in the very
weak
> >notion of const in C++.
>
> Why do you say "weak"? On many platforms, const data can be allocated
> within a read-only memory segment by the compiler. Attempting to
> modify data in segments marked as read-only can cause an access
> violation. IMHO, this is not weak.

This goes back to const meaning two different things - at top level, it
means it can be put into read-only segments, and is strong. D implements
this by having const be a storage class. I am not referring to this meaning
of const.

The weak part is const as a type modifier, as in:

    const int *p;

That is weak, as it is perfectly legal in C++ for the value of *p to change
at any point because there may be some other aliased reference to *p that is
not const. All it restricts is assignment through p itself, although even
that is weak as it is legal to cast away constness and assign to it anyway.
Therefore, as a guarantee that *p will be constant, it has little value. It
has no value at all as a hint to the optimizer, and a careful QA code
reviewer would have to treat it as not much more than a /*const*/ comment.

If C++ specified that there would be no assignments through non-const alias
to a const reference for the duration of the scope of that reference, then
that would be valuable to the optimizer, and an implementation could help
with QA by automatically inserting checks to make sure it doesn't change.
That would make it strong. But the spec doesn't say that.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 9/16/2004 12:01:11 AM

kanze@gabi-soft.fr writes:

 > David Abrahams <dave@boost-consulting.com> wrote in message
 > news:<uhdq0sxlx.fsf@boost-consulting.com>...
 >
 >     [...]
 > > I have a similar argument all the time with Python programmers.  Some
 > > of them claim that no static checking is ever worth the trouble.  I
 > > think it's probably true that static type checking seldom "finds
 > > bugs."
 >
 > Just a nit, but I don't quite agree.  I did one large project in Java,
 > and I was unpleasantly surprised to see just how often the wrong type
 > managed to slip into a container.

I agree with you, in my experience the number of problems which could
have been found by a stricter static type checking in systems using a
dynamicly typed language or having subverted the static type checks is
suprisingly large.  More those problems are often difficult to debug
because they are not detected at the place where wrongly typed
information is put in a place not expecting it but too often much
later.

The program I'm working on is written in C, C++ and an extension
language which is a dialect of Lisp (I think the part written in C/C++
is in order of several tens of millions of lines of code, the part in
the extension language in the order of several millions of lines of
code).

We manipulate lot of abstract types which are all represented as void*
(instead of of struct foo* which would have provided better type
checking -- this part is in pure C).

We spend a frustratingly large amount of time fixing problems detected
late because of lack of static type checks (either in lisp or due to
void*).  The problems are detected late in the development cycle
(during tests or worse integration test or worse full pre-release
tests) and late in the program (the problems cause a crash or a
misfunction at a place unrelated to the cause, sometimes deaper in the
call stack -- that's easy -- sometimes a long time after having put a
bad value in a persistent data structure -- finding why this value is
not of the expected type can be a nightmare).

Used correctly, type information is a kind of pre-condition which can
be checked by the compiler.  Putting that information in comments
where the coherence with the code can't be checked is a lost.

BTW, another place where the same problem occur is in template: why do
some push for the introduction of concept checking?

Yours,

-- 
Jean-Marc

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Jean 9/16/2004 11:07:04 AM

"Andrea Griffini" <agriff@tin.it> wrote in message
news:qqugk0p3dgpgko7khcmeejienev7eacip7@4ax.com...
 > I've arrived to the very same quite blaspheme conclusion. I cannot
 > remember a case (even just a single case) in which const-correctness
 > helped me spotting a logical problem in my code. Not even once.
 > Every single time the compiler complained about a const-correctness
 > related problem it was a problem in the const-correctness castle
 > itself (for example something that should have been accpeting a
 > "const Foo*" was instead accepting a "Foo *")... so in other words
 > the const-correctness error was just about const-correctness and
 > not about the program logic.

Hmm, it's easy to say there's no error if you fail to recognize the logic
error as one.

If a function takes Foo* when it is not supposed to mutate foo is an error.
It's just when a 'turn left' table is put on right turn.

 > On the other side the cost is IMO high... sometimes you're forced
 > to even duplicate code just to be const-correct.

It happens but I found it is rare, and the duplicated code is tiny/trivial
(just a return bar). When it would really be duplicated, you implement one
function calling the other, so it will not duplicate after all.

 > Sometimes things  get terribly ugly (const_iterator).

Yeah, const_iterator could not be provided to the required transparency.

 > With little (no) return for the investment (either at runtime or
 > at programming/debugging time) and high cost I found myself
 > pondering on why I should waste time on that.

Seems there are two casts of people, one being 'const-blind'.

I wonder how you follow the state of the program if everything is mutable?

 > Sure for other humans reading
 > the code there's a little documenting power of the "const" word...

Little?  IMHO it is *tremendous*.

 > but the logical const-ness of an entity is such a raw concept
 > that not having some better documentation than just the
 > declaration of a function is a total disaster anyway.

Beg pardon?

 > Of course my personal view is *very* limited... I mostly worked
 > as a team of one... may be experience of programmers working
 > in teams of hundreds on programs of tens of millions lines of
 > code is different.

Team of one means no communication of any kind.  You can read your own mind.
Also you're probably young enough to keep most of your program in your
memory as long as you work on the project.

You may try to grab some code other people wrote, and figure out how it
works and what it does.     An interesting experiment: global-replace const
to nothing before your first peek.  Then start reading.   When you got stuck
fetch the original code, and look if you can get further.

Paul



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Balog 9/16/2004 11:11:05 AM

"Andrea Griffini" <agriff@tin.it> wrote in message
news:ks6hk0tp7dc9ohl2brcmokbnofsb9kcaac@4ax.com...

 > The problem is that for example if you see a function
 > starting with
 >
 >     void foo(const int& x)
 >
 > you can't be sure that the value of x at the end of the
 > function will be the same as at the start of the function

For one, why is that a problem? I mean why is that a problem of this
function or prototype?

The function sig is pretty clear, it says it will not change the passed x.
So I can be sure it will not.

What can subvert this assumptation is:
1/ the function casts away sonst and goes on modifying.  Well, it could also
just dereference 0 pointers or format the harddrive instead of doing foo --
deliberately broken stuff is not a blame on language.

2/ It may have obtained a nonconst pointer to x through other channels.
Well. That can be a problem -- then it point a serious design error in the
whole project, again not related to this particular const.    Or it is a
well awaited modification of x-alias on that other channel -- then what is
the problem, really?

And do you think even the possibility is any common in a healthily-designed
and precisely coded program?    I just can;t recall a single case from my
memory when such aliasing happened -- except for self-assignment.

 > because "const int& x" means that you can't change the
 > referenced integer *using that reference* but it doesn't
 > mean that the referenced integer is a constant.

Yep.

 > "const" with references and pointers is an attribute of
 > the reference and pointer and *NOT* of the referenced or
 > pointed-to object.
 >
 > In C++ the quite commonly used ugly performance detail of
 > passing const refs instead of values reinforces this
 > dangerous logical shift.

I guess you had real-life problems with that, could you give an example?

Sure the baseline method to pass something is by constref. Byval is reserved
for builtin types, and plain ref for output.

It is indeed unfortunete all the defaults are bad -- we should say explicit
var instead of const, and explicit byval instead of refs, but history leads
back to C...  Still that's just the amount of typing, the idiom is like the
above.  And passing cref is not a 'performance detail' but the primary
intent.  You want to pass the original object most times not a copy.  The
performance detail is the case when you pass that int by value.  :)

Or do you really want a new and unrelated copy of params as baseline?  I see
extremely few programmers use mutate the passed params or use them as
storage.   [Okey I confess sometimes I do change some int or pointer -- at
the front of the function, bnut that's more like a trick, and the real body
starts after those lines.]

Paul




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Balog 9/16/2004 11:13:49 AM

"Steven E. Harris" <seh@panix.com> wrote in message
news:jk4wtyvwold.fsf@W003275.na.alarismed.com...
 > "Walter" <walter@digitalmars.nospamm.com> writes:
 >
 > > I forgot another option - one can just cast away const'ness when
 > > assigning to the 'mutable' member.
 >
 > I worked on a large code base for a few years that was full of such
 > casts, as one of the compilers did not support the mutable
 > keyword. Right above every such cast was the commented phrase:
 >
 >   // I meant /logically/ const!
 >
 > The mutable keyword validated and alleviated that frustration.

I can understand the frustration of that. But I can also suggest that
abstracting away the modification of the mutable member can be done with a
function call with an appropriate name. That would lessen the annoyance of
it, and make it a bit more self-documenting.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/16/2004 11:22:14 AM

"Andrea Griffini" <agriff@tin.it> wrote in message
news:ks6hk0tp7dc9ohl2brcmokbnofsb9kcaac@4ax.com...
 > On 15 Sep 2004 15:29:49 -0400, Bob Hairgrove <invalid@bigfoot.com>
 > wrote:
 >
 > >Why do you say "weak"? On many platforms, const data can be allocated
 > >within a read-only memory segment by the compiler. Attempting to
 > >modify data in segments marked as read-only can cause an access
 > >violation. IMHO, this is not weak.
 >
 > The problem is that for example if you see a function
 > starting with
 >
 >     void foo(const int& x)
 >
 > you can't be sure that the value of x at the end of the
 > function will be the same as at the start of the function
 > because "const int& x" means that you can't change the
 > referenced integer *using that reference* but it doesn't
 > mean that the referenced integer is a constant.
 > "const" with references and pointers is an attribute of
 > the reference and pointer and *NOT* of the referenced or
 > pointed-to object.
 >
 > In C++ the quite commonly used ugly performance detail of
 > passing const refs instead of values reinforces this
 > dangerous logical shift.

Yes, that's exactly the issue I was referring to. Not only is this the
reason why the optimizer can't make use of const, it confuses users into
falsely assuming that the const referenced data cannot change, leading to
faulty code that may, for example, cache portions of that data into local
variables.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/16/2004 11:23:54 AM

"Niklas Borson" <niklasb@microsoft.com> wrote in message
news:bc2bfdd5.0409150859.697769@posting.google.com...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
news:<5dO1d.178768$9d6.78418@attbi_s54>...
 > > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > > news:pFp1d.431122$%_6.152211@attbi_s01...
 > >  > I understand what you mean about "logical" constness vs actual
constness,
 > >  > and the utility of using a cache. But the options are not limited to
 > >  mutable
 > >  > or allocating a pointer - you could just declare the function as not
 > >  taking
 > >  > a const reference. Is that really so bad?
 > >
 > > I forgot another option - one can just cast away const'ness when
assigning
 > > to the 'mutable' member.
 >
 > Casting away constness would result in undefined behavior if the object
 > is actually const.

I understand your example, but it really looks to me like it should be an
error to ever declare a static const instance of it. This could be easilly
enforced by not allowing objects with constructors to have static const
instances - you cannot put such objects in ROM anyway.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/16/2004 11:24:27 AM

Andrea Griffini wrote:

 > On 15 Sep 2004 15:29:49 -0400, Bob Hairgrove <invalid@bigfoot.com>
 > wrote:
 >
 >
 >>Why do you say "weak"? On many platforms, const data can be allocated
 >>within a read-only memory segment by the compiler. Attempting to
 >>modify data in segments marked as read-only can cause an access
 >>violation. IMHO, this is not weak.
 >
 >
 > The problem is that for example if you see a function
 > starting with
 >
 >     void foo(const int& x)
 >
 > you can't be sure that the value of x at the end of the
 > function will be the same as at the start of the function
 > because "const int& x" means that you can't change the
 > referenced integer *using that reference* but it doesn't
 > mean that the referenced integer is a constant.
 > "const" with references and pointers is an attribute of
 > the reference and pointer and *NOT* of the referenced or
 > pointed-to object.
 >
 > In C++ the quite commonly used ugly performance detail of
 > passing const refs instead of values reinforces this
 > dangerous logical shift.

I don't see it as a logical shift, it is an overloaded meaning of const.
I think this is where the root of the problem is. A const reference is
something quite different from a constant object. A C++ programmer who
is not aware of this is likely to get into trouble. The dangerous
logical shift isn't in the language, it is in the head of the mediocre
programmer. You can blame the language for making it easy to trip over
this, but the concept behind it holds water.

I see this situation as a historical artefact. I'm not happy with it
either. If starting from scratch were an option, my preference would be
closer to the way Pascal used to pass arguments. If you don't specify
VAR, the compiler is free to choose between a const reference and a
copy. The "performance detail" is thus left to the compiler.

But we're dealing with real-world C++ that evolved from C. Knowing what
const means in the different places where it may appear is not simple,
but it's part of mastering C++ and it /is/ logical once you grasped the
idea.

-- 
Cheers
Stefan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stefan 9/16/2004 11:25:09 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<Sj12d.183503$9d6.177977@attbi_s54>...
> 
> But let's combine that with the argument for mutable, that of caching some
> computed value. The only way a const object can be put in ROM is if its
> values can be computed at compile time. If so, wouldn't its 'mutable' member
> be const too? What is a mutable member doing in the midst of a bunch of
> compile time constants?
> 

Aren't you forgetting your own (very useful) distinction between
'strong' const and 'weak' const.

You're asking what place mutable has in 'strong' const-ness, and the
answer is clearly none. 'Strong' const-ness is only about physical
const-ness.


> This seems to me to be a rather odd object, and I wonder why it is such an
> issue that it would merit its own keyword.
> 

To me, it all comes down to relationships between objects. A set of
objects with only const references to each other is much easier to
think about than a similar set with non-const references.

Ok, you could say that because of the existence of const_cast, const
is really only a comment. I can accept that, but if your coding
standards practically forbid the use of const_cast [*], const is a
'comment' which the compiler checks for you: the most valuable kind of
comment.

That brings it round to mutable. If you believe in the power of
logical const-ness to help write correct programs, mutable is
indispensible. It's the exact missing piece of the puzzle that makes
the blunt tool of 'const = read only' sharp enough to be incredibly
important.

On the other hand, I can see that if you don't believe in the power of
logical const-ness, getting consts in the right place is just a PITA,
while mutable is sitting there grinning at you :-)


James

[*] In my code, const_casts show up only in very rare language
work-arounds (which boost normally handles for me), or desperate last
minute hacks (not recommended, obviously).

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply jhopkin 9/17/2004 2:02:27 AM

kanze@gabi-soft.fr wrote in message news:<d6652001.0409150040.58e703ab@posting.google.com>...
> David Abrahams <dave@boost-consulting.com> wrote in message
> news:<uhdq0sxlx.fsf@boost-consulting.com>...
> 
>     [...]
> > I have a similar argument all the time with Python programmers.  Some
> > of them claim that no static checking is ever worth the trouble.  I
> > think it's probably true that static type checking seldom "finds
> > bugs."  
> 
> Just a nit, but I don't quite agree.  I did one large project in Java,
> and I was unpleasantly surprised to see just how often the wrong type
> managed to slip into a container.  My collegues weren't beginners, by
> any means, and we generally tried to document fairly thoroughly.  Never
> the less...

To be fair, Python programmers have more of an argument than Java
ones. While Java is halfway between being a statically typed and a
dynamically typed language, Python is a real dynamically typed one.
This fact can be exploited to achieve a degree of genericity in
programming that's similar to what you can achieve with C++ templates,
even if all the resolution takes place at runtime.

Combine this with a standard library that is more or less as rich as
Java's one and the result is you typically write a lot less code than
in C++ or Java.

My point of view, however, is that C++ is not typed strongly enough,
and that it lacks useful features, such as Pascal's subranges and
(true) enumerative types.

Cheers,
Nicola Musatti

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Nicola 9/17/2004 2:09:06 AM

In article <x952d.51613$MQ5.13088@attbi_s52>, Walter 
<walter@digitalmars.nospamm.com> writes
>I can understand the frustration of that. But I can also suggest that
>abstracting away the modification of the mutable member can be done with a
>function call with an appropriate name. That would lessen the annoyance of
>it, and make it a bit more self-documenting.

How? Please give a simple example. BTW what can be more self-documenting 
than 'mutable'?


-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/17/2004 2:17:36 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:<NE22d.183665$9d6.155903@attbi_s54>...
> "David Abrahams" <dave@boost-consulting.com> wrote in message
> news:u4qlzpsiq.fsf@boost-consulting.com...
> > "Walter" <walter@digitalmars.nospamm.com> writes:

> > > "David Abrahams" <dave@boost-consulting.com> wrote in message
> > > news:uhdq0sxlx.fsf@boost-consulting.com...

> > >  > Admittedly, const-ness is a less important component of the
> > >  > type system for these purposes than others, but is still
> > >  > important.  Also, I value the clarity of a "functional
> > >  > programming style."  It's very useful to know (and enforce)
> > >  > that a parameter value will be the same near the end of a
> > >  > function as it was at the beginning.  It makes thinking about
> > >  > the correctness of my program much easier.
> > > I understand what you mean. Where we disagree is the value in the
> > > very weak notion of const in C++.

> > I disagree that it's weak.  It's highly expressive.

> > How do you write a function in D that doesn't modify its argument,
> > to which you can pass a const or non-const object?

> Call by value makes a copy anyway, so that's not an issue. Call by
> reference from a const object - this happens rarely since nearly all
> reference objects will be on the heap, not in read-only memory. And to
> reiterate, I've never had a programming bug where a function modified
> a readonly object it shouldn't have. It just doesn't come up.

I've seen actual errors where a function accidentally modified a
temporary, instead of the object it was supposed to modify.  Typically
as a result of an unintentional implicit conversion.  The fact that I
cannot bind a temporary to a non-const reference does prevent those
errors.  And of course, if you can't bind a temporary to a non-const
referemce, you need const references so you can bind them to
something -- you probably don't want your matrix arithmetic operators to
take the matrix parameters by value.

> What does come up often is the nasty uninitialized data problem C++
> has in multiple manifestations. Plugging *those* holes would have
> saved me hundreds of frustrating hours over the years.

Curiously enough, I've not had too much problem there.  Just initialize
systematically in the declaration, and there shouldn't be too many
problems.  (I have had order of initialization problems with static
variables.)

--
James Kanze           GABI Software         http://www.gabi-soft.fr
Conseils en informatique orient�e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S�mard, 78210 St.-Cyr-l'�cole, France, +33 (0)1 30 23 00 34

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply kanze 9/17/2004 2:27:51 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<3322d.50552$MQ5.12794@attbi_s52>...
> "Bob Hairgrove" <invalid@bigfoot.com> wrote in message
> news:j5jgk0l43ma3ff2fbshtq3v6r0fqa2jfmq@4ax.com...
> > On 15 Sep 2004 05:38:57 -0400, "Walter"
> > <walter@digitalmars.nospamm.com> wrote:
> > >"David Abrahams" <dave@boost-consulting.com> wrote in message
> > >news:uhdq0sxlx.fsf@boost-consulting.com...
> > > > Admittedly, const-ness is a less important component of the type
> > > > system for these purposes than others, but is still important.  Also,
> > > > I value the clarity of a "functional programming style."  It's very
> > > > useful to know (and enforce) that a parameter value will be the same
> > > > near the end of a function as it was at the beginning.  It makes
> > > > thinking about the correctness of my program much easier.
> > >
> > >I understand what you mean. Where we disagree is the value in the very
>  weak
> > >notion of const in C++.
> >
> > Why do you say "weak"? On many platforms, const data can be allocated
> > within a read-only memory segment by the compiler. Attempting to
> > modify data in segments marked as read-only can cause an access
> > violation. IMHO, this is not weak.
> 
> This goes back to const meaning two different things - at top level, it
> means it can be put into read-only segments, and is strong. D implements
> this by having const be a storage class. I am not referring to this meaning
> of const.
> 
> The weak part is const as a type modifier, as in:
> 
>     const int *p;
> 
> That is weak, as it is perfectly legal in C++ for the value of *p to change
> at any point because there may be some other aliased reference to *p that is
> not const. All it restricts is assignment through p itself, although even
> that is weak as it is legal to cast away constness and assign to it anyway.
> Therefore, as a guarantee that *p will be constant, it has little value. 

The fact that an aliased pointer could still change the value of *p
misses the point.  The point is that *p will not be modified via _p_
unless someone goes out of their way to do so by using a cast.  Just
like there is no guarantee that p even points to an int (but that
doesn't seem to discourage most people from using references and typed
pointers instead of void*).

Scott McCaskill

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply scott_mccaskill 9/17/2004 2:37:08 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:bPJ0d.74330$3l3.5033@attbi_s03...
|
| "Jessica B" <jessica_boxer@yahoo.com> wrote in message
| news:642e01d8.0409101150.63271032@posting.google.com...
|  > I was having an argument recently with a fellow programmer.
|  > His take was that const correctness does not add significant
|  > value to your software, and, given that it is quite burdensome
|  > to maintain, he questioned whether it should be ignored.

| Nevertheless, many programmers swear by const and firmly believe it improves
| program readability and that it does find real bugs. I'm just not one of
| them <g>. This is why D makes const a storage class, rather than a type
| modifier

I must admit that I have always wondered why const was not a keyword in D. One
place were I would consider it essential
is in Contract Programming to remove accidential side-effects.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/17/2004 2:51:44 AM

Andrea Griffini <agriff@tin.it> wrote in message news:<ks6hk0tp7dc9ohl2brcmokbnofsb9kcaac@4ax.com>...
> 
> The problem is that for example if you see a function
> starting with
> 
>     void foo(const int& x)
> 
> you can't be sure that the value of x at the end of the
> function will be the same as at the start of the function
> because "const int& x" means that you can't change the
> referenced integer *using that reference* but it doesn't
> mean that the referenced integer is a constant.

But that's rarely what I care about.  I tend to read the "const int&"
specification as making a promise the other way.  It's more of a
guarantee that foo won't change the value of the argument.  So if I do
something like:
int bar = 12;
foo(bar);

I know that bar is still 12 after the call to foo.  If foo needs a
guarantee that the value of x won't change over the course of the
function call, it should take the argument by value.

Brian

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply brian 9/17/2004 2:52:52 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:NE22d.183665$9d6.155903@attbi_s54...
|
| "David Abrahams" <dave@boost-consulting.com> wrote in message

| What does come up often is the nasty uninitialized data problem C++ has in
| multiple manifestations. Plugging *those* holes would have saved me hundreds
| of frustrating hours over the years.

AFAICT, the standard library goes to great length to prohibit this kind of
behavior. My compilers at least warn about uninitialized variables
of builtin types.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/17/2004 2:54:33 AM

"Stefan Heinzmann" <stefan_heinzmann@yahoo.com> wrote in message
news:cial3h$5oq$03$1@news.t-online.com...
|
| I see this situation as a historical artefact. I'm not happy with it
| either. If starting from scratch were an option, my preference would be
| closer to the way Pascal used to pass arguments. If you don't specify
| VAR, the compiler is free to choose between a const reference and a
| copy. The "performance detail" is thus left to the compiler.

hm...I don't like that free choice...it could make it impossible to verify
exception-safety
properties.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/17/2004 2:55:45 AM


"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:V1wKTPSq6XRBFwGQ@robinton.demon.co.uk...
| In article <3a11d.179552$Fg5.58279@attbi_s53>, Walter
| <walter@digitalmars.nospamm.com> writes

| > > The only people I know that have a problem with const are those that
| > > come from a C background. Novices quickly learn to write const correct
| > > code if correctly introduced to the concept.
| >
| >I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and I
| >know how to write const correct code. I just haven't found a practical
| >benefit to it - it doesn't find real bugs, it doesn't look good, and it
| >doesn't help code generation.
|
| But for comparison you would have to be able to write const incorrect
| code that worked. Only then could you claim that const correctness did
| nothing to help avoid bugs. Note that I said 'avoid' not 'find'. A whole
| class of bugs are simply not possible in C++ (well the prime surviving
| case is with string literals)

I would say that const is important in another aspect too. I allows one to
keep a clean separation of
functions that are not making side-effects and those that are. Its a bit like
having procedures and functions in Pascal.
This is a very important conceptual property of a function.

br

Thorsten




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/17/2004 2:56:25 AM

"Balog Pal" <pasa@lib.hu> wrote in message
news:414935da@andromeda.datanet.hu...
> And do you think even the possibility is any common in a
healthily-designed
> and precisely coded program?    I just can;t recall a single case from my
> memory when such aliasing happened -- except for self-assignment.

I once adjusted the optimizer to treat const references as actually const,
and then fed a lot of code through it. Every once in a while, this
assumption breaks. I don't remember if it was healthily-designed or
precisely coded, just that it was legitimate code.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 9/17/2004 3:01:32 AM

"Jean-Marc Bourguet" <jm@bourguet.org> wrote in message
news:pxbu0ty8vwi.fsf@news.bourguet.org...
> We spend a frustratingly large amount of time fixing problems detected
> late because of lack of static type checks (either in lisp or due to
> void*).  The problems are detected late in the development cycle
> (during tests or worse integration test or worse full pre-release
> tests) and late in the program (the problems cause a crash or a
> misfunction at a place unrelated to the cause, sometimes deaper in the
> call stack -- that's easy -- sometimes a long time after having put a
> bad value in a persistent data structure -- finding why this value is
> not of the expected type can be a nightmare).

I know how hard this kind of problem can be to find. What I'd do in C is put
a 'signature' value at the beginning of each type:

struct Foo
{
#ifdef DEBUG
    int signature;
    #define FOO_SIGNATURE 0x12345678
    #define foo_assert(f) assert((f)->signature == FOO_SIGNATURE)
    #define foo_init(f) ((f)->signature = FOO_SIGNATURE)
    #define foo_term(f) ((f)->signature = 0)
#else
    #define foo_assert(f)
    #define foo_init(f)
    #define foo_term(f)
#endif
    ...
}

and then do a foo_assert() when pulling values out of the void*. This worked
amazingly well at flushing out those bugs (the foo_term() would also help
find any dependencies on dangling pointers). You can make it look a little
nicer in C++, but the idea is the same.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/17/2004 3:04:41 AM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:4149b976$0$214$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:bPJ0d.74330$3l3.5033@attbi_s03...
 > | Nevertheless, many programmers swear by const and firmly believe it
improves
 > | program readability and that it does find real bugs. I'm just not one of
 > | them <g>. This is why D makes const a storage class, rather than a type
 > | modifier
 >
 > I must admit that I have always wondered why const was not a keyword in D.
One
 > place were I would consider it essential
 > is in Contract Programming to remove accidential side-effects.

It is a keyword in D. It's a storage class:

     const int x = 3;


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/17/2004 10:39:58 AM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:4149b636$0$202$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:NE22d.183665$9d6.155903@attbi_s54...
 > |
 > | "David Abrahams" <dave@boost-consulting.com> wrote in message
 >
 > | What does come up often is the nasty uninitialized data problem C++ has
in
 > | multiple manifestations. Plugging *those* holes would have saved me
hundreds
 > | of frustrating hours over the years.
 >
 > AFAICT, the standard library goes to great length to prohibit this kind of
 > behavior.

The standard library is fine - but it does not help with my own UDT's.

 > My compilers at least warn about uninitialized variables
 > of builtin types.

Most compilers do, but that's a non-standard extension. I don't know of any
compilers that will warn you that you forgot to add an initializer for data
member 'bar::foo' in the constructor for 'bar'. Then there's the problem of
new'ing an array of basic types will give you an array of uninitialized
garbage.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/17/2004 10:41:19 AM

<kanze@gabi-soft.fr> wrote in message
news:d6652001.0409160430.3737ecd4@posting.google.com...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:<NE22d.183665$9d6.155903@attbi_s54>...
 > > What does come up often is the nasty uninitialized data problem C++
 > > has in multiple manifestations. Plugging *those* holes would have
 > > saved me hundreds of frustrating hours over the years.
 >
 > Curiously enough, I've not had too much problem there.  Just initialize
 > systematically in the declaration, and there shouldn't be too many
 > problems.  (I have had order of initialization problems with static
 > variables.)

That is curious. One of my big problems is adding a member to a class with
multiple constructors, and forgetting to add an initializer for it in one of
the constructors. This has bitten me so many times that I routinely make an
extra effort to go through the constructors line by line and comparing it
with the member declarations.

Uninitialized locals are picked up by the compiler when doing global data
flow optimizations, even though the language spec says such is legal (so it
produces a 'warning' rather than an error).

Another ugly problem I've had is in a complex class heirarchy, with virtual
and non-virtual functions, switching a function from non-virtual to virtual
and forgetting to adjust one of the other non-virtual overridden functions.
Oops.

D addresses all three of these issues so one must go to considerable effort
to cause the problems rather than having to go to considerable effort to
prevent them.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/17/2004 10:41:41 AM

"Brian McKeever" <brian.mckeever@gmail.com> wrote in message
news:ae0609e7.0409160755.fa9b3c5@posting.google.com...
 > But that's rarely what I care about.  I tend to read the "const int&"
 > specification as making a promise the other way.  It's more of a
 > guarantee that foo won't change the value of the argument.  So if I do
 > something like:
 > int bar = 12;
 > foo(bar);
 >
 > I know that bar is still 12 after the call to foo.

Unfortunately, C++ offers no such guarantee. foo() could change bar by using
a const_cast, or foo() could change bar by using another non-const reference
to bar, and changing bar through that. Does this kind of monkey business
happen in practice? Yes, it does, as I found out when I adjusted the DMC++
optimizer to assume that const references really did remain constant. It
happens just often enough to make such optimizations useless.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/17/2004 10:42:26 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<4052d.51578$MQ5.18722@attbi_s52>...
 > "Niklas Borson" <niklasb@microsoft.com> wrote in message
 >  > Casting away constness would result in undefined behavior if the object
 >  > is actually const.
 >
 > I understand your example, but it really looks to me like it should be an
 > error to ever declare a static const instance of it.

I don't see why. It is entirely reasonable and useful to declare an instance
of the class const. Doing so ensures that the object's observable state will
not change; that is, it is "logically const", which is the meaning of const
in C++ and the meaning most relevant to users of the class.

It's true that one of the class' private data members may still change. The
class is therefore not "physically const", and we signify this by declaring
the member mutable. But this is just an implementation detail, and should be
invisible to users of the class just as other aspects of the class' physical
representation are. If having a private mutable member placed a constraint
on how the class could be used then this implemtation detail wouldn't really
be hidden.

I understand that const works differently and means something different
in D. That's fine. D is a different language. But in C++ const refers to
the logical state of an object. As long as one understands it that way,
there's really nothing inconsistent or surprising about the use of mutable.

 > This could be easilly
 > enforced by not allowing objects with constructors to have static const
 > instances - you cannot put such objects in ROM anyway.

This seems like an arbitrary and unnecessary restriction, considering
that in C++ const is primarily used to express a logical constraint, to
facilitate reasoning about programs and writing correct programs. None
of this has anything to do with RAM vs. ROM or with the "physical
constness" of an object's internal representation.

In any case, the presence of a constructor does not necessarily
preclude placing an object in ROM. It is not uncommon for compilers
to elide calls to trivial constructors for static objects, and simply
initialize their memory "as if" the constructors had been called.
For example, given the definitions:

class Trivial {
public:
     Trivial(int val) : val(val) {}
     int Value() const { return val; }
private:
     int val;
};
const Trivial globalConst(10);

My compiler defines the globalConst variable as follows in the
assembly listing:

_DATA   SEGMENT
_globalConst DD 0aH
_DATA   ENDS

Granted, it's not in ROM, but it could be.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply niklasb 9/17/2004 10:43:20 AM

"James Hopkin" <jhopkin@reflectionsinteractive.com> wrote in message
news:a0368c9c.0409160208.37e75c6e@posting.google.com...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
news:<Sj12d.183503$9d6.177977@attbi_s54>...
 > >
 > > But let's combine that with the argument for mutable, that of caching
some
 > > computed value. The only way a const object can be put in ROM is if its
 > > values can be computed at compile time. If so, wouldn't its 'mutable'
member
 > > be const too? What is a mutable member doing in the midst of a bunch of
 > > compile time constants?
 > >
 >
 > Aren't you forgetting your own (very useful) distinction between
 > 'strong' const and 'weak' const.
 >
 > You're asking what place mutable has in 'strong' const-ness, and the
 > answer is clearly none.

I didn't bring that up - others did as a justification for 'mutable', and I
am agreeing with you that it doesn't have a place for objects in readonly
memory. Check the post I was responding to.


 > Ok, you could say that because of the existence of const_cast, const
 > is really only a comment.

Perhaps it's because I'm a compiler writer, and I look at what guarantees
const gives me that are backed up by the language. The answer is "not much",
and const_cast is one reason why, and aliases are another. When I am QAing
another's code, I certainly am not going to take const as a guarantee of
anything - especially when the program is misbehaving.

 > I can accept that, but if your coding
 > standards practically forbid the use of const_cast [*], const is a
 > 'comment' which the compiler checks for you: the most valuable kind of
 > comment.

I understand the value of compiler checking, but I like Design by Contract
better, because I can *know* that specified conditions are valid, rather
than hoping somebody adhered to my coding standard. Compilers don't enforce
coding standards. When QAing code, I cannot assume coding standards are
followed, and in real life, they rarely are.

 > That brings it round to mutable. If you believe in the power of
 > logical const-ness to help write correct programs, mutable is
 > indispensible. It's the exact missing piece of the puzzle that makes
 > the blunt tool of 'const = read only' sharp enough to be incredibly
 > important.

Your argument feels right. The only issue I have with it is I can't remember
ever having a bug that compiler checking of logical const-ness would have
found for me. I've had a lot of frustrating problems with uninitialized
data - I bet a LOT of bugs in C++ could be found if the language lent a hand
to squashing those.

 > On the other hand, I can see that if you don't believe in the power of
 > logical const-ness, getting consts in the right place is just a PITA,
 > while mutable is sitting there grinning at you :-)

I just think all the effort expended on const would have been better spent
addressing holes in the language that produce boatloads of bugs. Like
uninitialized data. But that's obviously water long gone under that bridge,
and I'm not suggesting that const be changed in C++.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/17/2004 10:45:29 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:1XxiLbZgvXSBFw42@robinton.demon.co.uk...
 > In article <x952d.51613$MQ5.13088@attbi_s52>, Walter
 > <walter@digitalmars.nospamm.com> writes
 > >I can understand the frustration of that. But I can also suggest that
 > >abstracting away the modification of the mutable member can be done with
a
 > >function call with an appropriate name. That would lessen the annoyance
of
 > >it, and make it a bit more self-documenting.
 >
 > How? Please give a simple example.

Sure:

inline void set_mutable_int(const int *p, int newvalue)
{
     int *q = (int *)p;
     *q = newvalue;
}

Of course, this could be dressed up as a template, etc., and it could be
customized to the specific application.

 > BTW what can be more self-documenting than 'mutable'?

I won't argue that mutable isn't self-documenting, it is. It's just that
keywords are a precious commodity, and one should be parsimonious in
spending them.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/17/2004 10:46:42 AM

On 16 Sep 2004 07:11:05 -0400, "Balog Pal" <pasa@lib.hu> wrote:

 >Hmm, it's easy to say there's no error if you fail to recognize the logic
 >error as one.

The error disappears if you remove the const-ness castle. In other
words the error is not in the algorithm, but in the specification
of what is constant and what is not.

That I've to tell the compiler what is constant and what is not
is an incidental fact of the C++ language, not something related
to the algorithm I'm implementing.

 >If a function takes Foo* when it is not supposed to mutate foo is an error.
 >It's just when a 'turn left' table is put on right turn.

It's just a typo when declaring the function. In my experience
when that happened the error was just that... in other words
the function had no intention at all to modify the object, but
simply was declared without stating this in the prototype.

To make a concrete example I saw many times things like

    int strlen(char *s);

but quite rarely strlen functions that were modifying the
passed string.

I do not remember even a single case in which when the compiler
complained about violation of const-correctness the problem
was that a function that was actually going to mutate an object
was being called on something that shouldn't have been modified.
Of course may be it's me.

 >Seems there are two casts of people, one being 'const-blind'.

I simply happened to stop thinking to what are the advantages
that const-correctness gave me and compared them to the cost
that const-correctness had. It never really helped me, it costed
me quite a bit.

I'm not const-blind, but I'm not so sure if specifiying to a
compiler what is const and what is not (with const refs) is
useful. I just think that what is const and what is not is so
obvious that stating it is just a waste of time... add to this
that the compiler couldn't care less about my const declarations
when it comes to code generation because of aliasing.

What about references and pointers to objects that are meant
to be used only as a destination for writes ?
Imagine a "writeonly" declaration for refs and pointers...

   Would this allow the compiler to generate more diagnostic
   messages ? Sure!
   Would this describe more how the function is going to use
   the parameters ? Sure!
   Would this be a nice addition to C++ ? I'm NOT so sure.

I'm already upset that I've to write

   std::map<std::string,int>::const_iterator i = mymap.begin();

just to be able to iterate over a map; I would love
the possibility to write instead

   mymap::const_iterator i = mymap.begin();

or just

   i = mymap.begin();

I don't want more things to write when that is not going
to provide some useful feedback (I don't think that being
informed that I made a typo in the unneeded information
I'm forced to type is a useful feedback).

 >I wonder how you follow the state of the program if everything is mutable?
 >
 > > Sure for other humans reading
 > > the code there's a little documenting power of the "const" word...
 >
 >Little?  IMHO it is *tremendous*.

It's little because the "writing" or "calling only const-members"
is really raw stuff. Often there are methods you can call in a
certain state and methods you can't call. There are methods
you can call in a certain order but not in another.
There are mutually exclusive methods (you can call either of
two, but not both).

That the *type system* is the best actor for handling
these issues is IMO quite questionable.

With just the bare "writable" concept dropping this burden
on the type system is not infeasible... (C++ shows that)
but this being useful or not is a quite different issue.

 >Team of one means no communication of any kind.  You can read your
 >own mind. Also you're probably young enough to keep most of
 >your program in your memory as long as you work on the project.

If you ever maintained a complex program for several years
(while developing others) you sure know that it's quite easy
to forget what you did. You can read your own mind right now,
but not 4 years from now; and even if you do often you risk
remembering wrong.

 >You may try to grab some code other people wrote, and figure out how it
 >works and what it does.

I'm often called exactly for that. To be specific I'm often
asked for help to spot errors in other's people code.
Not the best job I can think to... but I've shown some
ability in it so sometimes happens.

 >An interesting experiment: global-replace const to nothing before
 >your first peek.  Then start reading.   When you got stuck
 >fetch the original code, and look if you can get further.

I'm doing this in the very moment. I'm investing some time
in a language where there's no const-correctness concept
in parameter passing (there are not even type declarations
for variables!!!). So far it's like breathing fresh
mountain air...

And like I said you simply cannot refuse to jump on the
const-correctness train with C++ because of how the rules
of the language work (e.g. temporaries). Just dropping "const"
from a C++ program will quite unlikely result in a still
legal C++ program.

Andrea

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrea 9/17/2004 10:51:30 AM

On 16 Sep 2004 07:13:49 -0400, "Balog Pal" <pasa@lib.hu> wrote:

 >"Andrea Griffini" <agriff@tin.it> wrote in message
 >news:ks6hk0tp7dc9ohl2brcmokbnofsb9kcaac@4ax.com...
 >
 > > The problem is that for example if you see a function
 > > starting with
 > >
 > >     void foo(const int& x)
 > >
 > > you can't be sure that the value of x at the end of the
 > > function will be the same as at the start of the function
 >
 >For one, why is that a problem? I mean why is that a problem of this
 >function or prototype?
 >
 >The function sig is pretty clear, it says it will not change the passed x.
 >So I can be sure it will not.

False. The prototype says that it can't change it *using that
reference*. That's it. Anything else is a false assumption.
The risk of using a const reference like it is a value is
is exactly that... shifting the const-ness from the reference
to the object. That function may change the referenced value
in other ways, volutarely or not.

 >What can subvert this assumptation is:
 >1/ the function casts away sonst and goes on modifying.  Well, it could also
 >just dereference 0 pointers or format the harddrive instead of doing foo --
 >deliberately broken stuff is not a blame on language.

Casting away const-ness from a reference is NOT illegal.
You're just saying that you know what you're doing and
that you need to change the object. A const reference
is NOT a way to tell that the object can't change; you do
that declaring const objects... it's a very different thing.
Dereferencing a NULL pointer is a *completely* different
issue.

 >2/ It may have obtained a nonconst pointer to x through other channels.
 >Well. That can be a problem -- then it point a serious design error in the
 >whole project, again not related to this particular const.    Or it is a
 >well awaited modification of x-alias on that other channel -- then what is
 >the problem, really?
 >
 >And do you think even the possibility is any common in a healthily-designed
 >and precisely coded program?    I just can;t recall a single case from my
 >memory when such aliasing happened -- except for self-assignment.

Here is a simple example of what can happen:

    //
    // A 2d point; supports addition and subtraction
    // in the geometrical sense.
    //
    struct Pt
    {
      int x,y;
      Pt(int x,int y) : x(x),y(y) {}
      Pt& operator+=(const Pt& other)
      {
        x+=other.x; y+=other.y;
        return *this;
      }
      Pt& operator-=(const Pt& other)
      {
        x-=other.x; y-=other.y;
        return *this;
      }
    };
    Pt operator+(Pt a,const Pt& b) { return a+=b; }
    Pt operator-(Pt a,const Pt& b) { return a-=b; }


    //
    // A 2d rect; supports addition and subtraction
    // of points as translation.
    //
    struct Rect
    {
      Pt tl,br;
      Rect(const Pt& tl,const Pt& br) : tl(tl),br(br) {}
      Rect& operator+=(const Pt& delta)
      {
        tl+=delta; br+=delta;
        return *this;
      }
      Rect& operator-=(const Pt& delta)
      {
        tl-=delta; br-=delta;
        return *this;
      }
    };
    Rect operator+(Rect a,const Pt& delta) { return a+=delta; }
    Rect operator-(Rect a,const Pt& delta) { return a-=delta; }

Now suppose I've a rect r representing a window in screen
coordinates (e.g. (50,50)-(200,200)) and I want to translate
this rect in local coordinates (where the topleft is (0,0)).
I may be tempted to write r -= r.tl ... but this wouldn't
work.. because in the middle of the Rect::operator-=() method
the delta will change, so the correct offset will be subtracted
from tl, but (0,0) will be subtracted from br.

Please take some time considering what was the error here...
of course there can be a jillion workarounds but the key
point is... "delta" is used by operator -= as a value but
passing a const reference instead is a common idiom in C++.
Operator -= couldn't care less about the identity of the
delta object being passed... so a "reference" is highly
inappropriate. Declaring it "const" doesn't help with the
fact that the referenced object can be changed at any time.

If you wanna think to other cases then consider the following...

   void dup()
   {
     stack.push_back( stack.back() );
   }

Do you see the danger in this expression ? push_back accepts
a const reference... that may reference an object that is
inside the vector itself. In all implementation I saw this
works because in case of resize the deallocation of the "old"
area happens *after* the new element has been copied in.
I think indeed this probably works in all implementations
because push_back is required to be a no-op if the copy
operation fails. But how many times someone accepting a
const reference took the time to carefully think that the
referenced object could change or its lifetime could end
while doing apparently unrelated operations (like
deallocating the old vector area after making room for the
new - not yet accessed - element) ?

 > > because "const int& x" means that you can't change the
 > > referenced integer *using that reference* but it doesn't
 > > mean that the referenced integer is a constant.
 >
 >Yep.
 >
 > > "const" with references and pointers is an attribute of
 > > the reference and pointer and *NOT* of the referenced or
 > > pointed-to object.
 > >
 > > In C++ the quite commonly used ugly performance detail of
 > > passing const refs instead of values reinforces this
 > > dangerous logical shift.
 >
 >I guess you had real-life problems with that, could you give an example?

The rect example was in real code (I retyped it on the fly
and made it shorter... but the issue was exactly about r-=r.tl).
I see nothing in that code that is not commonly used in C++.
IMO the problem is in the cheapo "passing a const ref is a
smart way of passing a value" C++ idiom. Indeed doesn't look that
smart... it's a quite ugly performance detail that unfortunately
pervades C++. Also I'm not sure if passing (and using) a Pt& is
faster than passing a Pt... but changing that requires *massive*
changes in the source code.

 >Sure the baseline method to pass something is by constref. Byval is reserved
 >for builtin types, and plain ref for output.

 >And passing cref is not a 'performance detail' but the primary
 >intent.  You want to pass the original object most times not a copy.  The
 >performance detail is the case when you pass that int by value.  :)

In the operator-= I don't want to pass that specific instance of
Pt. I just wanted to pass an offset. Much cleaner would be stating
this in the source by writing

     Rect& operator-=(Pt delta);

without the risk of aliasing or of having lifetime of delta
terminating before the operator returns.

The "const" part of passing a const ref doesn't do much
about aliasing and lifetime, but somehow I've the impression
that without that "const" part many would have been quite
reluctant of using that as the default value-passing trick.

Considering that "const" helps this use of references is
exactly the (il)logical shift I was talking about... who
reads "const" often psychologically transfers this const-ness
to the object; and this is quite bad because it's NOT what
it means for the language.

Andrea

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrea 9/17/2004 10:53:27 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<Sj12d.183503$9d6.177977@attbi_s54>...
 > "Bob Hairgrove" <invalid@bigfoot.com> wrote in message
 > news:5cjgk0hq03rk668gjauuoqbgrfoar0qnj1@4ax.com...
 > > On 15 Sep 2004 05:54:28 -0400, "Walter"
 > > <walter@digitalmars.nospamm.com> wrote:
 > > >I forgot another option - one can just cast away const'ness when
 >  assigning
 > > >to the 'mutable' member.
 > >
 > > Casting away const'ness can lead to undefined behavior when the
 > > compiler allocates const data in a memory segment marked as read-only
 > > (see my other reply in this thread).
 >
 > Ok, I can see that (Francis made the same argument, and I'll reply to both
 > here).
 >
 > But let's combine that with the argument for mutable, that of caching some
 > computed value. The only way a const object can be put in ROM is if its
 > values can be computed at compile time. If so, wouldn't its 'mutable' member
 > be const too?

You are confusing the notion of physical constness with logical
constness. "Const" and "mutable" let us express logical constness
without being forced to use physical constness. This is appropriate,
because as programmers we rarely care about physical constness;
logical constness is what matters. However, the compiler can tell when
our usage allows physical constness, and can then place such objects
into ROM.

Obviously, any object with mutable members cannot be physically const,
and so cannot be placed into ROM; the answer to your question is "any
object with mutable members won't be in ROM in the first place."

On the other hand, casting away the constness of the (non-mutable)
member of a const object can have problems, because in this case the
compiler might have actually placed the object into ROM.

 > What is a mutable member doing in the midst of a bunch of
 > compile time constants?

If we're not talking about physical constness (and by definition we're
not; mutable members can't be physically const), who cares?

Bob

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply belvis 9/17/2004 10:56:13 AM

On 16 Sep 2004 22:27:51 -0400, kanze@gabi-soft.fr wrote:

>I've seen actual errors where a function accidentally modified a
>temporary, instead of the object it was supposed to modify.  Typically
>as a result of an unintentional implicit conversion.

This however is probably a sign that implicit conversion is the
problem. Const-correctness is a bad way to deal with it.
Sometimes it's a perfectly legitimate desire to pass a temporary
object to a function that needs to call mutators on it.
The problem is only when that happens unintentionally.

Imagine for example something like

    obj->serialize( Zipper(datastream) );

where zipper is a filter object.

Currently you're instead forced to write

    {
      Zipper zip_it(datastream);
      obj->serialize(zip_it);
    }

Andrea

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrea 9/17/2004 5:27:19 PM

In article <4149b636$0$202$14726298@news.sunsite.dk>, Thorsten Ottosen 
<nesotto@cs.auc.dk> writes
>AFAICT, the standard library goes to great length to prohibit this kind of
>behavior. My compilers at least warn about uninitialized variables
>of builtin types.

And there is really little, if any, excuse, for C++ code to include 
uninitialised variables, nor C now that it too allows late declaration 
of local variables.


-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/17/2004 5:34:27 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:ES42d.51535$MQ5.25980@attbi_s52...

> Yes, that's exactly the issue I was referring to. Not only is this the
> reason why the optimizer can't make use of const, it confuses users into
> falsely assuming that the const referenced data cannot change, leading to
> faulty code that may, for example, cache portions of that data into local
> variables.

Could you give a real-life case where that actually happened?



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Balog 9/17/2004 5:36:59 PM

In article <a0368c9c.0409160208.37e75c6e@posting.google.com>, James 
Hopkin <jhopkin@reflectionsinteractive.com> writes
>[*] In my code, const_casts show up only in very rare language
>work-arounds (which boost normally handles for me), or desperate last
>minute hacks (not recommended, obviously).

And in my code they largely appear when dealing with cases where a 
function returns a reference or pointer based on a parameter:

int const * foo(int const * arg){
// process
   return arg;
}

Has the unfortunate side effect of adding const when called with a 
non-const argument. The idiom for dealing with this is to add:


inline
int * foo(int * arg){
   return const_cast<int *>foo(const_cast<int const *> arg);
}

Which should have no impact on the resulting object code, but allows the 
compiler to deal with non-const cases without adding const to the 
return. It is clearly safe because you are only casting away a const 
qualifier that you added in the first place.



-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/17/2004 5:40:28 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:f1l2d.204283$Fg5.126141@attbi_s53...
> > And do you think even the possibility is any common in a
> healthily-designed
> > and precisely coded program?    I just can;t recall a single case from
my
> > memory when such aliasing happened -- except for self-assignment.
>
> I once adjusted the optimizer to treat const references as actually const,
> and then fed a lot of code through it. Every once in a while, this
> assumption breaks. I don't remember if it was healthily-designed or
> precisely coded, just that it was legitimate code.

That has nothing to do with my question.  Optimizer is certainly not allowed
to expect it never happens, unless you provide a switch to force that
assumptation.

I'm interested what people do, think and enconter. The design of the program
and the programmer shall track the lifetime of objects and know what and
when happens. The information 'this object I play with is registered
elsewhere, and stuff can happen to it' is vital to correct programming, and
is not related to playing with some 'const' functions.

Btw. I found another ill case besides self-assignments -- I recall problems
with CString::Format:

template<class T> inline const char * f_s(const T & in) {return in;}

CString str("World");
str.Format("Hello, %s\n", f_s(str));

Here the contents of str are nuked before use.

But this again is a case of 'leaking references to internals may cause
trouble', and 'look out for invalidation conditions of such references'.

Pau





      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Balog 9/17/2004 5:43:26 PM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:1XxiLbZgvXSBFw42@robinton.demon.co.uk...

> How? Please give a simple example. BTW what can be more self-documenting
> than 'mutable'?

mutable is self-documenting alright.   The 'problem' is those mutable
members are generally private.  So the user of the class will not go after
them. The public documentation may just forget to mention they exist.

And another thread (on COW vs. threads) just showed how mutable state may
wreak havoc.

My conclusion:  classes meant fot MT use shall precisely document whether
they have mutable members or equvivalent inderground tech, and it shall be a
fixed part of the public contract even if it's really a private
implementation detail.

Herb suggested programmers shall suspect mutable members present -- what is
after all the only safe assumptation, but my experience shows mutable
members are pretty rare (if I discount internal sync objects), and public
awareness of their existance is low.

Paul



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Balog 9/17/2004 5:44:11 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:tDu2d.63286$D%.56208@attbi_s51...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:4149b636$0$202$14726298@news.sunsite.dk...
|  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
|  > news:NE22d.183665$9d6.155903@attbi_s54...
|  > |
|  > | "David Abrahams" <dave@boost-consulting.com> wrote in message
|  >
|  > | What does come up often is the nasty uninitialized data problem C++ has
| in
|  > | multiple manifestations. Plugging *those* holes would have saved me
| hundreds
|  > | of frustrating hours over the years.
|  >
|  > AFAICT, the standard library goes to great length to prohibit this kind
of
|  > behavior.
|
| The standard library is fine - but it does not help with my own UDT's.

well, it does, when you start from building on the standard library instead of
escaping it.

|  > My compilers at least warn about uninitialized variables
|  > of builtin types.
|
| Most compilers do, but that's a non-standard extension. I don't know of any
| compilers that will warn you that you forgot to add an initializer for data
| member 'bar::foo' in the constructor for 'bar'.

I wonder why?

| Then there's the problem of
| new'ing an array of basic types will give you an array of uninitialized
| garbage.

that's because one should use std::vector<>.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/18/2004 11:13:27 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:UMu2d.58209$MQ5.15821@attbi_s52...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:4149b976$0$214$14726298@news.sunsite.dk...
|  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
|  > news:bPJ0d.74330$3l3.5033@attbi_s03...
|  > | Nevertheless, many programmers swear by const and firmly believe it
| improves
|  > | program readability and that it does find real bugs. I'm just not one
of
|  > | them <g>. This is why D makes const a storage class, rather than a type
|  > | modifier
|  >
|  > I must admit that I have always wondered why const was not a keyword in
D.
| One
|  > place were I would consider it essential
|  > is in Contract Programming to remove accidential side-effects.
|
| It is a keyword in D. It's a storage class:
|
|      const int x = 3;

yeah, sorry, I meant a keyword behaving like the C++ keyword which is the
essential proporty needed for
contract programming.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/18/2004 11:14:08 AM

"Andrea Griffini" <agriff@tin.it> wrote in message
news:jdujk0defvc210rnr4ro533fkaevkb6k60@4ax.com...

 >  > > The problem is that for example if you see a function
 >  > > starting with
 >  > >
 >  > >     void foo(const int& x)
 >  > >
 >  > > you can't be sure that the value of x at the end of the
 >  > > function will be the same as at the start of the function
 >  >
 >  >For one, why is that a problem? I mean why is that a problem of this
 >  >function or prototype?
 >  >
 >  >The function sig is pretty clear, it says it will not change the passed
x.
 >  >So I can be sure it will not.
 >
 > False. The prototype says that it can't change it *using that
 > reference*. That's it.

Yeah. That's what I'm talking about.  In the above foo, the function agrees
not to change x. (And even the complier helps to sort out mistakes wrt. that
intent).  Then how will foo change x?    I see only 2 real-life ways:

1. someone left address of x in a global variable.   Guess nowadays no one
use (nonconst) globals, and whoever does likely deserves the consequences.

2. foo is a member function so receives a 'this' and x may be a member, and
foo may change that member.    But that shall be a preserved case, part of
foo's contract, and an expected behavior completely unrelated to the
incident I pass x as argument to the function.  For all practical purposes I
see the modofocation as nonrelated stuff.   (And passing nstance members to
member functions as arguments is also uncommon IMHO.)

 > Anything else is a false assumption.

But there's no 'anythong else' ;)

 > The risk of using a const reference like it is a value is
 > is exactly that... shifting the const-ness from the reference
 > to the object.

In a random-driven system, yes.  In a designed system, no.

 > That function may change the referenced value
 > in other ways, volutarely or not.

Those other ways shall be either eliminated or well guarded by design.   If
you leave room for those ways the system will collapse pretty soon, and no
spoilers will prevent that.

 >  >What can subvert this assumptation is:
 >  >1/ the function casts away sonst and goes on modifying.  Well, it could
also
 >  >just dereference 0 pointers or format the harddrive instead of doing
foo --
 >  >deliberately broken stuff is not a blame on language.
 >
 > Casting away const-ness from a reference is NOT illegal.

Really, the policeman will not grab you, but if you break const contract
without really express documentation chances are high you'll get fired.
It is not illegal but more than rude.

 > You're just saying that you know what you're doing and
 > that you need to change the object.

If it is really allowed in the sohop, you;re right in all your points, then
const eally has no meaning at all, and is just a source of extra work and
trouble.

Now please imagine an environment where people refrain from modifying const
objects, that is enforced and you can rely on that assumpltation.   Is that
a different world?

 >  A const reference
 > is NOT a way to tell that the object can't change; you do
 > that declaring const objects... it's a very different thing.

Declaring const objects do little for you -- as in most scopes you deal with
stuff passed as *argument*.  At the spot you can't tell whether an object
you hot by ref was or was not declared const.   And that is also an argument
on why one shall never modify an object he got through const ref -- it can
introduce UB and leaves pretty little room to discover the problem until
very very late.

 > Dereferencing a NULL pointer is a *completely* different  issue.

Not in my opinion.  Both are similarly rude behavior that is not acceptable.

[Pt/rect example.]
 > Please take some time considering what was the error here...

On the first run I thought it was a plain' old case of spoiled
self-assignment issue. And even wrote some rants on why we don't learn from
mistakes described in Ec for almost a decade.
Then I realized the op='s of Pt worked correctly, and that was not the
problem.

The problem was in Rect's such operators with the blurring point the
argument was not your full object which would kick in the if(this == &That)
engine.
Though it was one of the sub-objects, and you shall be sure to assume the
possibility to get it too.

 > of course there can be a jillion workarounds but the key
 > point is... "delta" is used by operator -= as a value but
 > passing a const reference instead is a common idiom in C++.

Be aware of self-assignment is just equally common IMHO. Though such case
can slip past attention. Hope you don't object I sent your fragment to Scott
Meyers in hope it could get into recised EC.

 > If you wanna think to other cases then consider the following...
 >
 >    void dup()
 >    {
 >      stack.push_back( stack.back() );
 >    }

That's a funny one.  Hopefully the i = i++; kicks in the writer. My
tie-breaker question would be 'which back you intend to push here, and how
you know you get that knowing order is unspecified.

 > Do you see the danger in this expression ?

I'd discard in on a review first pass as 'confusing' without going circles
to prove it can actually work well in some cases.     Sorry if I spoil the
fun of this discussin, but I consider 'any code not crystal clear is wrong'.

 > push_back accepts
 > a const reference... that may reference an object that is
 > inside the vector itself.

puch_back is not the issue here, back() is.  If it's value based, we're
okey.  But then the assumptation shpuld be documented at the spot -- and it
is easier to just rearrange the code:

   const value_type top(stack.back());
   stack.push_back( top );

 > In all implementation I saw this
 > works because in case of resize the deallocation of the "old"
 > area happens *after* the new element has been copied in.

If you're interested I would also guess the code as 'likely works' with both
places being ref, but rather chose not rely on any implementation detail.
(And if I did for some efficiency reason it should be documented right
there.)

 > I think indeed this probably works in all implementations
 > because push_back is required to be a no-op if the copy
 > operation fails. But how many times someone accepting a
 > const reference took the time to carefully think that the
 > referenced object could change or its lifetime could end
 > while doing apparently unrelated operations (like
 > deallocating the old vector area after making room for the
 > new - not yet accessed - element) ?

Theoretically you should care abut lifetime issues for wrt all pointers and
refs anywhere in the program.

And for collections you shall document both requirements on refs passed in,
and ones you gave away.  It is very important to know when all those
iterators and pointers/refs to internals are stable or get invalidated.  And
what are the aliasing rules.
Like for memcpy() it is UB to pass overlapping regions, while memmove
handles them correctly  (at some cost).

 >  >I guess you had real-life problems with that, could you give an example?
 >
 > The rect example was in real code

Hope Io I wasn't excessively rure pointing it out as a really dumb mistake.
I came to C++ years before Meyers and learnt those most basic screwups in
the bloody way.
Including case similar to yours just it was not Rect but Bignumber, and
broke at simple = too.    But I see little exuse to do it these days.

 > I see nothing in that code that is not commonly used in C++.

Hm, IMO you should.

 > IMO the problem is in the cheapo "passing a const ref is a
 > smart way of passing a value" C++ idiom. Indeed doesn't look that
 > smart... it's a quite ugly performance detail that unfortunately
 > pervades C++. Also I'm not sure if passing (and using) a Pt& is
 > faster than passing a Pt

Depends on how large the object is. It's probably safe to expect any UDT
being fat, and copy-unfriendly.   Light objects being a special case.

 >  >Sure the baseline method to pass something is by constref. Byval is
reserved
 >  >for builtin types, and plain ref for output.
 >
 >  >And passing cref is not a 'performance detail' but the primary
 >  >intent.  You want to pass the original object most times not a copy.
The
 >  >performance detail is the case when you pass that int by value.  :)
 >
 > In the operator-= I don't want to pass that specific instance of
 > Pt. I just wanted to pass an offset. Much cleaner would be stating
 > this in the source by writing
 >
 >      Rect& operator-=(Pt delta);

Probably so -- or explicitly look after aliasing.  If you see a ponter/ref
of a type matching something in the environment, you shall tell whether they
allowed to match, and work with described behavior, or say it is a no-no.
Note how the type system helps here -- most times you can dismiss the issue
just by telling the types are different enough to exclude possible aliasing.

OTOH.  Plain assignment for certain objects nowadays is
T& operator=(T rhs) {this->swap(rhs);}
to avoid exception problems.

Note I said *baseline* method.  Wherever I know the implementation will
create a copy of the argument anyway I too use byval.   Or in general if I
want a new and distinct copy.   (While in other cases passing by ref is
mandatory in order to preserve the object's identity.)

 > without the risk of aliasing or of having lifetime of delta
 > terminating before the operator returns.
 >
 > The "const" part of passing a const ref doesn't do much
 > about aliasing and lifetime, but somehow I've the impression
 > that without that "const" part many would have been quite
 > reluctant of using that as the default value-passing trick.

Yeah, and we got java after some time.    IMHO killing cool and functional
stuff leaving only suboptimal choices is not a way to keep people writing
good programs. ;*)

 > Considering that "const" helps this use of references is
 > exactly the (il)logical shift I was talking about... who
 > reads "const" often psychologically transfers this const-ness
 > to the object; and this is quite bad because it's NOT what
 > it means for the language.

It sounds like flushing the baby along with the bathwater.  Your bug would
get caught by a review, and similar cases are uncommon -- the possibility of
them shall not be base for a more general phylosophy.
And I see great value in the original promise without shifts.

Paul



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Balog 9/18/2004 2:28:44 PM

"Andrea Griffini" <agriff@tin.it> wrote in message
news:hc3kk0la3strt0gjmg77fbie1l1qejjops@4ax.com...

 >  >Hmm, it's easy to say there's no error if you fail to recognize the
logic
 >  >error as one.
 >
 > The error disappears if you remove the const-ness castle.

Sure. :)  Castle is a good parable, you have to put some effort to have one,
then you gein pretty much benefit.  However if your castle have cracks here
and there, the benefits are void, just the work remains.   Too bad people
still tend to call it 'castle' even if a wall is gone.

 >  >If a function takes Foo* when it is not supposed to mutate foo is an
error.
 >  >It's just when a 'turn left' table is put on right turn.
 >
 > It's just a typo when declaring the function. In my experience
 > when that happened the error was just that... in other words
 > the function had no intention at all to modify the object, but
 > simply was declared without stating this in the prototype.
 >
 > To make a concrete example I saw many times things like
 >
 >     int strlen(char *s);
 >
 > but quite rarely strlen functions that were modifying the
 > passed string.

Yeah. Exactly match 'error' in my vocabulary -- and not in yours.   Your
mindset tells it is not really important, an innocent typo. In my it says
'you just lied'.

To me, non-constness is a valuable resource.  You can get it if you need it,
but wasting it is rude. (I and others mentioned the wrong default -- stuff
should be const as is, and tell var if not -- but that's fixed.)

When I read code
 >     int strlen(char *s);
reads as a function taking a string it will modify in some cases.  And one
that  changes the state of the program.    It is tremendous difference
compared to a function reading
 >     int strlen(const char *s);

that I recon as a pure function producing a result without any side effects
noticeable.

Suppose I discovered the int result no longer interests me.  For the latter
case I just scrap the line and move on.  For the former I'm not allowed to
do that. My options are:  leave an orphaned call there as LAVA (antipattern)
for my successor to wonder why is it there.   Or dig the spec/code of the
function to see what it actually does with the pesky string.   And that work
step is (potentially) repeated for all its uses.

Telling liesid not bad 'cos immoral or what -- it is bad because misleads
people and cause them extra work.    If it is the function's role not to
change something it shall talk about it in the interface.

 >  >Seems there are two casts of people, one being 'const-blind'.
 >
 > I simply happened to stop thinking to what are the advantages
 > that const-correctness gave me and compared them to the cost
 > that const-correctness had. It never really helped me, it costed
 > me quite a bit.

No wonder.  :)

'const-blind' I meant like color-blind.  When it is examined  there are
pictures with colored spots. color-blind people just see a uniform spot, and
others see a red letter A in green field.

Another parable -- you see a tap, there's a blue spot and a red spot.  You
know blue will give cold water and red will give hot, and just ho on and use
the tap.  Suppose some country in infested by sloppy plumbers, and some
percent of taps wear the wrong marks. And you had some accidents with them
too -- so you carefully examine what water you get.  Color marks became
redundant, as you can't rely on them anyway.   Don't you think that system
is way inefficient compared to the first?   And how many people do extra
work just because a few plumbers forgot a 3 second check, and there were no
QA after them.

 > I'm not const-blind, but I'm not so sure if specifiying to a
 > compiler what is const and what is not (with const refs) is
 > useful.

Are you not?  Read that sentence.  You're right, talking to the compiler is
not so fruitful.  You also miss the whole point,  you also talk to people.
You express the design in code.  When you miss a const, and just refrain to
poke the object the compiler will not be more happy if you fix that part.
And the issue of expressing intent is not a thing you're interested in.

 > I just think that what is const and what is not is so
 > obvious that stating it is just a waste of time... add to this
 > that the compiler couldn't care less about my const declarations
 > when it comes to code generation because of aliasing.

Code generation is too rarely an issue in RL projects, it falls far behind
correctness.

And to have correctness you need code-reading, reviews. You need it even
more later, when you maintain the code in the folloing years.  Missing and
incorrect information is a general pain in the ass.

 > What about references and pointers to objects that are meant
 > to be used only as a destination for writes ?
 > Imagine a "writeonly" declaration for refs and pointers...

Well?

In state-orineted programming approash the main difference lies between
state-changed and state-not-changed.  For state changed it is interesting
how far the effect is seen -- thet's why we try encapsulate more, to lessen
the possible scope shanges can bee seen. Reading is only interesting wrt
changed state/cached stuff.   I can't see much value in restricting reads.

 >    Would this allow the compiler to generate more diagnostic
 >    messages ? Sure!
 >    Would this describe more how the function is going to use
 >    the parameters ? Sure!
 >    Would this be a nice addition to C++ ? I'm NOT so sure.

Sure -- as that piece of information is one we don;t care about while the
other is vital.

 > I'm already upset that I've to write
 >
 >    std::map<std::string,int>::const_iterator i = mymap.begin();
 >
 > just to be able to iterate over a map; I would love
 > the possibility to write instead
 >
 >    mymap::const_iterator i = mymap.begin();
 >
 > or just
 >
 >    i = mymap.begin();

I agree.  While iterators is a great concept, patern, abstraction, the std::
iterators are far from perfect.   (I currently dont's use std:: at all, but
my own set of collection classes, I attacked the problem to add a uniform
iterator several times then dropped it as 'not worth'.  const_iterator
problems had a big role in decision -- not the verbosity but what Scott
Meyers described.   In practice I too rarely change list for [] collections,
and the iterations are pretty clear in code.  And despite i'm a great fun of
algorithms and high order functions, C++ is too far away from LISP and
what's more important, too far from providing a well-readable way to use
them. At least for my RL problems, where I use for()  and insert
domain-specific algos on as-needed base. )

But that is not a thing really related to the const castle IMHO. :)

 > I don't want more things to write when that is not going
 > to provide some useful feedback (I don't think that being
 > informed that I made a typo in the unneeded information
 > I'm forced to type is a useful feedback).

I generally write (this is actually a macro :)

for(int i=0, maxI = arr.GetSize(); i<maxI; ++i)
{
     const type & item = arr[i];
     // rest
}

I have to rewrite 'type' to actual type, too bad the auto proposal is
nowhere -- and 2 instances of 'arr'.  Then after // all that counts is item,
arr and i hardly ever is interesting.

And certainly the first const is removed if item is to be modified.

 >  >I wonder how you follow the state of the program if everything is
mutable?
 >  >
 >  > > Sure for other humans reading
 >  > > the code there's a little documenting power of the "const" word...
 >  >
 >  >Little?  IMHO it is *tremendous*.
 >
 > It's little because the "writing" or "calling only const-members"
 > is really raw stuff. Often there are methods you can call in a
 > certain state and methods you can't call. There are methods
 > you can call in a certain order but not in another.
 > There are mutually exclusive methods (you can call either of
 > two, but not both).
 >
 > That the *type system* is the best actor for handling
 > these issues is IMO quite questionable.

IMO it's quite evident the type system has nothing to offer in the described
case. :)
If you have cool omniscent flowcharts -- the work is easy mapping the code
to the chart, or verifying then mapping.    But I found it a minority of
cases.

While it is pretty common you have a set of pre/postconditions and
invariants, and have to create code implementing them or making sure the
implementation is correct.

 > With just the bare "writable" concept dropping this burden
 > on the type system is not infeasible... (C++ shows that)
 > but this being useful or not is a quite different issue.

Think again.  Grab some code of yours, and imagine any function could use
some globals at will.   Wouldn't make your head spin?    You can manage the
stuff 'cos functions introduce locality -- they restrict to work with their
arguments and that's it.  (Certainly I mean the majority of them.)   Micing
in const increases that locality even more.

 >  >Team of one means no communication of any kind.  You can read your
 >  >own mind. Also you're probably young enough to keep most of
 >  >your program in your memory as long as you work on the project.
 >
 > If you ever maintained a complex program for several years
 > (while developing others) you sure know that it's quite easy
 > to forget what you did. You can read your own mind right now,
 > but not 4 years from now; and even if you do often you risk
 > remembering wrong.

Sure I did -- piece of cake compared to maintaining anything you did not
write.  :-)))   Guess why so many programmers have the urge to write much
stuff for scratch.

 >  >You may try to grab some code other people wrote, and figure out how it
 >  >works and what it does.
 >
 > I'm often called exactly for that. To be specific I'm often
 > asked for help to spot errors in other's people code.
 > Not the best job I can think to... but I've shown some
 > ability in it so sometimes happens.

Good start. :)

 >  >An interesting experiment: global-replace const to nothing before
 >  >your first peek.  Then start reading.   When you got stuck
 >  >fetch the original code, and look if you can get further.
 >
 > I'm doing this in the very moment. I'm investing some time
 > in a language where there's no const-correctness concept
 > in parameter passing (there are not even type declarations
 > for variables!!!). So far it's like breathing fresh
 > mountain air...

Guess you're pythoning.  I too picked up that language a little while ago as
my next favorite language, and plan to put it to some real work in the near
future.  ;-)   But that's again a differnt issue.   (I also gave it a
thought that const could be pretty useful there too as a qualifier, but it's
just a really light idea -- but I'll check it out.)

 > And like I said you simply cannot refuse to jump on the
 > const-correctness train with C++ because of how the rules
 > of the language work (e.g. temporaries). Just dropping "const"
 > from a C++ program will quite unlikely result in a still
 > legal C++ program.

I didn't say you should actually compile in the mental experiment, just take
the text.  But as said at the top -- if spotting const or spotting const is
not there in a prototype does not bring any info for you, it's really
pointless.    Just try to rekon -- other people actually percieve a
difference where you do not.    (Then whether that difference is relevant in
your actual environment is again a different issue.)

Paul



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Balog 9/18/2004 2:41:59 PM

"Balog Pal" <pasa@lib.hu> wrote in message news:<414ab483@andromeda.datanet.hu>...
 > "Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
 > news:1XxiLbZgvXSBFw42@robinton.demon.co.uk...
 >
 > > How? Please give a simple example. BTW what can be more self-documenting
 > > than 'mutable'?
 >
 > mutable is self-documenting alright.   The 'problem' is those mutable
 > members are generally private.  So the user of the class will not go after
 > them. The public documentation may just forget to mention they exist.
 >
 > And another thread (on COW vs. threads) just showed how mutable state may
 > wreak havoc.
 >
 > My conclusion:  classes meant fot MT use shall precisely document whether
 > they have mutable members or equvivalent inderground tech, and it shall be a
 > fixed part of the public contract even if it's really a private
 > implementation detail.

No, classes meant for MT shall document the level of thread safety
they offer, regardless of mutable member use.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply pdimov 9/18/2004 7:53:03 PM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:Mcmc5SC$kqSBFwPh@robinton.demon.co.uk...
 > And there is really little, if any, excuse, for C++ code to include
 > uninitialised variables, nor C now that it too allows late declaration
 > of local variables.

Consider the following:

void foo(int& x) { x = ...; }
.....
int x;
foo(x);

x is not used before its initialization, so there's no bug here. But if I
wrote instead:

int x = 0;
foo(x);

now the maintainer looks at the code, and wonders why x is set to 0 when the
value is never used. I don't agree that inserting dead code to satisfy a
coding standard is a good practice.

Other cases besides locals where C++ uninitialized data causes problems:

1) adding a member to a class and forgetting to add an initializer for it to
each and every constructor.
2) data returned by new int[x]; is uninitialized garbage.
3) padding holes in data structures like:
     struct Foo { int a; char b; int c; };
are filled with garbage.
4) Arrays on the stack are filled with garbage:
     void *x[100];

Statically allocated data, however, is sensibly set to 0 as the default.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/18/2004 7:55:07 PM

"Balog Pal" <pasa@lib.hu> wrote in message
news:414a94b7@andromeda.datanet.hu...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:ES42d.51535$MQ5.25980@attbi_s52...
 >
 > > Yes, that's exactly the issue I was referring to. Not only is this the
 > > reason why the optimizer can't make use of const, it confuses users into
 > > falsely assuming that the const referenced data cannot change, leading
to
 > > faulty code that may, for example, cache portions of that data into
local
 > > variables.
 >
 > Could you give a real-life case where that actually happened?

I ran into these issues about 10 years ago, but I unfortunately don't
remember the specific cases, but here's a not unreasonable one:

struct Value { int x; };

/* Add v to both v2 and v3 */
void addValues(const Value& v, Value& v2, Value& v3)
{
      v2.x += v.x;
     v3.x += v.x;
}

......
Value s,t;
.....
addValues(s, s, t);        // oops

Sure, there's a bug in the code, but it's a real easy trap to fall into when
thinking that v is 'const'. Let's look at the local caching issue. Suppose
we have the mouse position on the screen represented by x,y:

struct Pos { int x,y; };

Now let's have a function that gets a const reference to the current mouse
position, does some computations,
then returns the current mouse position:

Pos DoSomethingWithMousePos(const Pos& p)
{
     Pos localp = p;        // create cache on stack because we feel the need
for speed
     ... lots of computations on localp ...
     DoFoo();                // a long running function, running so long that
the mouse position p changes
     return localp;            // our cached value is now out of date, and
not in sync with p any more
}

Is that a farfetched mistake to make? I don't think so. I know I often cache
pointer dereferences into locals in order to get them into registers for
fast computation. The confusion is that const sometimes guarantees that a
value won't change, and sometimes does not. I don't believe there is much
bug finding value in the latter meaning of const, and its weakness can lead
to the creation of bugs.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/18/2004 7:57:12 PM

"Balog Pal" <pasa@lib.hu> wrote in message
news:414ab481@andromeda.datanet.hu...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:f1l2d.204283$Fg5.126141@attbi_s53...
 > > > And do you think even the possibility is any common in a
 > > healthily-designed
 > > > and precisely coded program?    I just can;t recall a single case from
 > my
 > > > memory when such aliasing happened -- except for self-assignment.
 > >
 > > I once adjusted the optimizer to treat const references as actually
const,
 > > and then fed a lot of code through it. Every once in a while, this
 > > assumption breaks. I don't remember if it was healthily-designed or
 > > precisely coded, just that it was legitimate code.
 >
 > That has nothing to do with my question.  Optimizer is certainly not
allowed
 > to expect it never happens, unless you provide a switch to force that
 > assumptation.

Of course, what I meant was that trying such an optimization made it
possible to find out if such cases existed in real code (not just contrived
test cases). And they do.

 > I'm interested what people do, think and enconter.

We're both on the same page with that.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/18/2004 7:57:51 PM

Francis Glassborow <francis@robinton.demon.co.uk> wrote in message
news:Js7dlFDI0qSBFwMQ@robinton.demon.co.uk...
 > In article <a0368c9c.0409160208.37e75c6e@posting.google.com>, James
 > Hopkin <jhopkin@reflectionsinteractive.com> writes
 > >[*] In my code, const_casts show up only in very rare language
 > >work-arounds (which boost normally handles for me), or desperate last
 > >minute hacks (not recommended, obviously).
 >
 > And in my code they largely appear when dealing with cases where a
 > function returns a reference or pointer based on a parameter:
 >
 > int const * foo(int const * arg){
 > // process
 >    return arg;
 > }
 >
 > Has the unfortunate side effect of adding const when called with a
 > non-const argument. The idiom for dealing with this is to add:
 >
 >
 > inline
 > int * foo(int * arg){
 >    return const_cast<int *>foo(const_cast<int const *> arg);
 > }


I avoid const cast altogether...

inline int * foo(int * const arg)
{
    int const* const carg = arg;

    return arg+(foo(carg)-arg);
}

Louis.



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Louis 9/18/2004 8:00:15 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<sDu2d.63285$D%.61590@attbi_s51>...
 > <kanze@gabi-soft.fr> wrote in message
 > news:d6652001.0409160430.3737ecd4@posting.google.com...
 >  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 >  > news:<NE22d.183665$9d6.155903@attbi_s54>...
 >  > > What does come up often is the nasty uninitialized data problem C++
 >  > > has in multiple manifestations. Plugging *those* holes would have
 >  > > saved me hundreds of frustrating hours over the years.
 >  >
 >  > Curiously enough, I've not had too much problem there.  Just initialize
 >  > systematically in the declaration, and there shouldn't be too many
 >  > problems.  (I have had order of initialization problems with static
 >  > variables.)
 >
 > That is curious. One of my big problems is adding a member to a class with
 > multiple constructors, and forgetting to add an initializer for it in one of
 > the constructors. This has bitten me so many times that I routinely make an
 > extra effort to go through the constructors line by line and comparing it
 > with the member declarations.

Walter, as you (should) know through having reviewed writings of mine
on this very issue, one use of const is for precisely this problem.
Naturally, this is only useful when the member does not have to change
through the life of the instance, but there are a surprising number of
such cases.

(Not germane to the const debate, but another option is to use
references, again where appropriate.)

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply stlsoft 9/18/2004 8:02:57 PM

"Bob Bell" <belvis@pacbell.net> wrote in message
news:c87c1cfb.0409161332.7da186e4@posting.google.com...
 > If we're not talking about physical constness (and by definition we're
 > not; mutable members can't be physically const), who cares?

I obviously need to work on writing more clearly <g>.

I wrote that in response to two different people bringing up putting const
objects into read-only memory as a justification for mutable rather than
just using const_cast, because then (as you pointed out) the compiler would
know not to put the object into ROM.

I have a hard time seeing any real world object that has a need for all
three of the following:

1) compile time initialization
2) the members never change
3) mutable members.

What is a mutable member doing in a class where the other members are all
constant and compile time initialized? Even if there are any of these odd
birds, are there enough to justify a keyword?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/18/2004 8:06:27 PM

In article <fRK2d.61495$MQ5.36692@attbi_s52>,
  "Walter" <walter@digitalmars.nospamm.com> wrote:

 > "Bob Bell" <belvis@pacbell.net> wrote in message
 > news:c87c1cfb.0409161332.7da186e4@posting.google.com...
 >  > If we're not talking about physical constness (and by definition we're
 >  > not; mutable members can't be physically const), who cares?
 >
 > I obviously need to work on writing more clearly <g>.

You and me both... ;-)

 > I wrote that in response to two different people bringing up putting const
 > objects into read-only memory as a justification for mutable rather than
 > just using const_cast, because then (as you pointed out) the compiler would
 > know not to put the object into ROM.

Actually, I understood the context of your post better after I posted. I
hate when I do that.

 > I have a hard time seeing any real world object that has a need for all
 > three of the following:
 >
 > 1) compile time initialization
 > 2) the members never change
 > 3) mutable members.
 >
 > What is a mutable member doing in a class where the other members are all
 > constant and compile time initialized? Even if there are any of these odd
 > birds, are there enough to justify a keyword?

But since you asked, I'll give you an answer.

I think your questions in the previous paragraph are kind of a red
herring. The justification for mutable doesn't really have anything to
do with compile time initialization or ROM; after all, those are really
optimization issues. The justification is simply that if we didn't have
mutable, we wouldn't be able to express logical constness that isn't (or
can't be) implemented with physical constness.

Bob

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Bob 9/19/2004 10:40:51 AM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:414adcfb$0$213$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > | Then there's the problem of
 > | new'ing an array of basic types will give you an array of uninitialized
 > | garbage.
 >
 > that's because one should use std::vector<>.

That does illustrate a very different point of view we have. I'd rather fix
the language than tell people not to use the core features of it.

I'd also rather write:

     int a[100];

or:

     int[] a = new int[100];

rather than:

     std::vector<int> a(100);

Wouldn't you? Even setting aside for the moment the limitations of
std::vector resulting from it not being part of the core language?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/19/2004 10:41:42 AM

"Balog Pal" <pasa@lib.hu> writes:

 > Yeah. That's what I'm talking about.  In the above foo, the function agrees
 > not to change x. (And even the complier helps to sort out mistakes wrt. that
 > intent).  Then how will foo change x?    I see only 2 real-life ways:
 >
 > 1. someone left address of x in a global variable.   Guess nowadays no one
 > use (nonconst) globals, and whoever does likely deserves the consequences.
 >
 > 2. foo is a member function so receives a 'this' and x may be a member, and
 > foo may change that member.    But that shall be a preserved case, part of
 > foo's contract, and an expected behavior completely unrelated to the
 > incident I pass x as argument to the function.  For all practical purposes I
 > see the modofocation as nonrelated stuff.   (And passing nstance members to
 > member functions as arguments is also uncommon IMHO.)

3. x contains a pointer to another object that holds a mutable
    pointer to x.

;-)

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/19/2004 10:49:08 AM

In article <0uu2d.206703$Fg5.40765@attbi_s53>, Walter
<walter@digitalmars.nospamm.com> writes
> Unfortunately, C++ offers no such guarantee. foo() could change bar by 
> using
> a const_cast, or foo() could change bar by using another non-const 
> reference
> to bar, and changing bar through that. Does this kind of monkey 
> business
> happen in practice? Yes, it does, as I found out when I adjusted the 
> DMC++
> optimizer to assume that const references really did remain constant. 
> It
> happens just often enough to make such optimizations useless.

Within certain limits C++ guarantees more than nothing.

A function with a single parameter* whose definition does not access a
static object can only violate the 'read only' guarantee if it uses
const_cast. The compiler can verify all the above and act accordingly.
Of course the problem is at the call site, but a compiler could
implement optimisations by a switch (#pragma) assuming that the function
will not change what is passed to it. Given a suitable linker breaches
of the assumption could be diagnosed.

It seems to me, unsurprisingly, that D is tuned to your own coding
style. That is normal for most languages with a single designed. C++ is
a notable exception because Bjarne Stroustrup designed a language that
allows people with very different style and priorities to use the same
tools.

I have always described the two major meanings of const as:
1) top level const is an instruction to the compiler that the programmer
will not ever try to change the state of this object. Any attempt by the
programmer to breach this results in consequences that are entirely the
programmers responsibility.

2) pointers and references to const qualified types have two sides.
Outwardly they promise read-only access to objects that are either const
qualified (logically immutable, or marked as read only) and plain,
unqualified objects. Inwardly they ask the compiler to ensure that the
promise is kept.



-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: 
http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Francis 9/19/2004 6:03:35 PM

In article <sDu2d.63285$D%.61590@attbi_s51>, Walter
<walter@digitalmars.nospamm.com> writes
> That is curious. One of my big problems is adding a member to a class 
> with
> multiple constructors, and forgetting to add an initializer for it in 
> one of
> the constructors. This has bitten me so many times that I routinely 
> make an
> extra effort to go through the constructors line by line and comparing 
> it
> with the member declarations.

But that is a tools problem, one that you are particularly well placed
to solve.

-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: 
http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/19/2004 6:04:25 PM

In article <kUt2d.58068$MQ5.53222@attbi_s52>, Walter
<walter@digitalmars.nospamm.com> writes
>
> Sure:
>
> inline void set_mutable_int(const int *p, int newvalue)
> {
>     int *q = (int *)p;
>     *q = newvalue;
> }
>
> Of course, this could be dressed up as a template, etc., and it could 
> be
> customized to the specific application.

Sorry but that is of very little use. We are talking about udts. The
above is fine for humans and useless for compilers. We need a way to
tell the compiler that even logically immutable objects of the given
type are not physically immutable. mutable was introduced exactly
because of that need.


-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: 
http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Francis 9/19/2004 6:13:54 PM

In article <tDu2d.63286$D%.56208@attbi_s51>, Walter
<walter@digitalmars.nospamm.com> writes
> Most compilers do, but that's a non-standard extension. I don't know 
> of any
> compilers that will warn you that you forgot to add an initializer for 
> data
> member 'bar::foo' in the constructor for 'bar'. Then there's the 
> problem of
> new'ing an array of basic types will give you an array of uninitialized
> garbage.

Why doesn't your compiler do that? It does not appear to be hard to
diagnose.


-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: 
http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/19/2004 6:16:14 PM

"Bob Bell" <belvis@pacbell.net> wrote in message
news:belvis-E2AD79.00400819092004@news.la.sbcglobal.net...
 > Actually, I understood the context of your post better after I posted. I
 > hate when I do that.

Hey, no problem.

 >  > I have a hard time seeing any real world object that has a need for all
 >  > three of the following:
 >  >
 >  > 1) compile time initialization
 >  > 2) the members never change
 >  > 3) mutable members.
 >  >
 >  > What is a mutable member doing in a class where the other members are
all
 >  > constant and compile time initialized? Even if there are any of these
odd
 >  > birds, are there enough to justify a keyword?
 >
 > But since you asked, I'll give you an answer.
 >
 > I think your questions in the previous paragraph are kind of a red
 > herring.

So do I - but I made the effort because Francis and Bob brought up those
objects as a justification for mutable.

 > The justification for mutable doesn't really have anything to
 > do with compile time initialization or ROM; after all, those are really
 > optimization issues. The justification is simply that if we didn't have
 > mutable, we wouldn't be able to express logical constness that isn't (or
 > can't be) implemented with physical constness.

I understand that, and I agree that it has some value. My disagreement stems
from my estimation that the value of the feature compared to the cost of the
feature is not compelling. I can't, of course, prove that because it's
subjective.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 9/20/2004 5:48:11 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:F81eBmaxgsSBFwsM@robinton.demon.co.uk...
 > In article <kUt2d.58068$MQ5.53222@attbi_s52>, Walter
 > <walter@digitalmars.nospamm.com> writes
 > >
 > > Sure:
 > >
 > > inline void set_mutable_int(const int *p, int newvalue)
 > > {
 > >     int *q = (int *)p;
 > >     *q = newvalue;
 > > }
 > >
 > > Of course, this could be dressed up as a template, etc., and it could
 > > be
 > > customized to the specific application.
 >
 > Sorry but that is of very little use. We are talking about udts. The
 > above is fine for humans and useless for compilers. We need a way to
 > tell the compiler that even logically immutable objects of the given
 > type are not physically immutable. mutable was introduced exactly
 > because of that need.

I wrote that in response to Steven's issue with the self-documenting nature
of writing 'mutable', it is not meant to provide semantic information to the
compiler. You brought up the issue in another subthread of informing the
compiler that an object cannot be placed in read-only memory, and I
responded to that there. I am glad, though, that you agree that it does
resolve the self-documentation issue.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/20/2004 5:49:07 AM

On 18 Sep 2004 10:41:59 -0400, "Balog Pal" <pasa@lib.hu> wrote:

 > > The error disappears if you remove the const-ness castle.
 >
 >Sure. :)  Castle is a good parable, you have to put some effort to have one,
 >then you gein pretty much benefit.  However if your castle have cracks here
 >and there, the benefits are void, just the work remains.   Too bad people
 >still tend to call it 'castle' even if a wall is gone.

I'm sorry I cand find no way to explain you that const-ness is just
about const-ness; and that programming is about something else.

I would say that anyone that is still mentally flexible enough
to question if what is found in books is always the only
necessary truth or not can at least understand what I mean.

I know it may come as a surprise to you... but
(hold your hat):

  - you can program without the concept of const-correctness
  - you can program without static type checking
  - you can program without "public/private/protected"
  - you can approach object orientation even without
    introducing the concept of "class"

Const-correctness is a level of description where something
is left to the type checking system (instead for example to
the runtime system or just left unchecked).

Before yelling nonsenses like "no one uses non-const globals"
please at least try to understand what is discussed.

Can be const-ness useful for programming (and not for const-ness
itself) ? Sure... in theory. But in practice in my experience
it has been NOT useful as I can't remember a single case in which
it really helped programming. Moreover seems I'm not the only
one with this opinion.

This doesn't mean however that IMO static type checking is also
pointless (even if I must say that I'm not on the ship "static
type checking is the only reasonable way to program").

Const-correctness in addition of being of questionable utility
in itself (it has a cost and gives something in return; it's
all about the ROI point) is also daily ruined and insulted by
the C++ idiom of "a const ref parameter is a nifty way to
pass value". This is a philosophical error... a ref or a
pointer (const or non-const) is about identities... a value
is getting past the concept of identity.

Can passing logical values as const-refs bite back ? Sure.
I've seen that happening. And please take some time thinking
to what was the problem in the rect/pt example I posted...
Please consider how much thinking should have been done
about accepting a "const Pt&" parameter and how much thinking
should be in theory used before accepting any "const X&"
parameter in general.

I'm sure that if you think to it at least a bit probably you'll
realize that the phrase "there's no 'anything else'" is
absolutely ridicolous... problems may arise if there is *any*
aliasing; it's not only about passing const ref objects
that are members of the class defining the method being called.

If you accept a "const X&" parameter and use it like being a
value you should be sure that for example no object that
you know (or that knows any object that you know, or that
kowns and object that knows any object that you know ...)
and of which you're calling methods holds any way to reach
the object instance you're using as a value.

Also stating that this should be forbidden in the contract is
IMO distilled nonsense in the general case (note for example
that aliasing lives at the implementation level, not at the
interface level; there's no way someone could call you
remaining compliant about details you don't like to disclose).

To make it concrete... can I pass you that Pt instance ?
It all depends on what you do in the implementation of
the method (including what other classes that such an
implementation may decide to call are doing in their
implementation... and so on recursively).

 >Yeah. Exactly match 'error' in my vocabulary -- and not in yours.   Your
 >mindset tells it is not really important, an innocent typo. In my it says
 >'you just lied'.

I'm just saying that the instruction that were going to be
compiled would have solved the problem. The function:

    int strlen(char *s)
    {
      int n = 0;
      while (s[n])
        ++n;
      return n;
    }

is not "broken" from a programming point of view. It's not
even "lying" in the interface. None the less this is the
most common source of "problems" I found about
const-correctness.

 > >     int strlen(const char *s);
 >
 >that I recon as a pure function producing a result without any side effects
 >noticeable.

Whoa! That's a big jump. How can you tell that function is
not going to store the passed pointer in a dictionary ?
How can you be sure that function is not going to open a
database connection ?
Do you know you can delete[] a "const char *" ?

 >Suppose I discovered the int result no longer interests me.  For the latter
 >case I just scrap the line and move on.

Yikes! I can only hope you never get to maintain nuclear
plants or airplane control software.

 >Telling liesid not bad 'cos immoral or what -- it is bad because misleads
 >people and cause them extra work.    If it is the function's role not to
 >change something it shall talk about it in the interface.

But that "const" is terribly far from being enough.

I once stopped thinking a bit about a "pure" function
specifier for cases like "strlen" or "sqrt"; but things
are not trivial in the general case.

 > > It never really helped me, it costed
 > > me quite a bit.
 >
 >No wonder.  :)
 >
 >'const-blind' I meant like color-blind.  When it is examined  there are
 >pictures with colored spots. color-blind people just see a uniform spot, and
 >others see a red letter A in green field.

It costed you also quite a bit; but this is a problem for
your manager. You don't see the cost because you simply
are not able to think to other possibility...
I can think to programming both with const-correctness and
without; why are you saying I'm color-blind ?

I used const-correctness also with "C" (where you're basically
not forced to) for long time before starting wondering what
is *really* useful for...

With C++ however there's no choice. You must use it. Fullstop.

(with most of C++ you pay only what you use... but there
is a "minimum two drinks" sign inside).

 >You also miss the whole point,  you also talk to people.
 >You express the design in code.  When you miss a const, and just refrain to
 >poke the object the compiler will not be more happy if you fix that part.
 >And the issue of expressing intent is not a thing you're interested in.

In a similar thread of several months ago this was the
only reasonable point I found. May be I never ever had
const-correctness spotting a true problem in the code
but surely I programmed while reading the documenting
part of const parameters declarations.
This is surely true... but IMO it's not such a big plus
(and just the prototype is never enough anyway).

 >Code generation is too rarely an issue in RL projects, it falls far behind
 >correctness.

True that performance is basically irrelevant in many
cases (I happen to work in an environment where it's
far from irrelevant, but it's probably an exception).
Still there is a diffused misconception that const with
references and pointers helps the compiler.

However compilers luckyly enough are required to be
more accurate than you and your "there's no 'anything
else'" problem analysis...

 >Sure -- as that piece of information is one we don;t care about while the
 >other is vital.

Please state who is "we"; I for one am not among the
ones that consider const-correctness "vital".

Make a petition about "is const-correctnes *vital*
for programming ?" and see how many would sign that.
Stop also thinking to how many millions of lines of
code you're using daily are written with languages in
which there was no const-correctness concept at all.

 >(I currently dont's use std:: at all, but
 >my own set of collection classes, ...

no comment ...

 >Just try to rekon -- other people actually percieve a
 >difference where you do not.    (Then whether that difference is relevant in
 >your actual environment is again a different issue.)

I didn't say I see no difference. I said that I never
happen to meet a case in which a const-correctness
related error was spotting out a real essential
coding problem. Seems I'm not alone in this view.

You did ? Can you provide an example ?

Please don't re-start talking about how nicely in theory
this could spot errors. I've read that stuff, I lived
in that town for many years... just tell how many times
the cost gave you back the spotting of a genuine logic
error in the program.

Andrea

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Andrea 9/20/2004 6:02:41 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:YMrZt5YKasSBFwsC@robinton.demon.co.uk...
 > In article <0uu2d.206703$Fg5.40765@attbi_s53>, Walter
 > <walter@digitalmars.nospamm.com> writes
 > > Unfortunately, C++ offers no such guarantee. foo() could change bar by
 > > using
 > > a const_cast, or foo() could change bar by using another non-const
 > > reference
 > > to bar, and changing bar through that. Does this kind of monkey
 > > business
 > > happen in practice? Yes, it does, as I found out when I adjusted the
 > > DMC++
 > > optimizer to assume that const references really did remain constant.
 > > It
 > > happens just often enough to make such optimizations useless.
 >
 > Within certain limits C++ guarantees more than nothing.
 >
 > A function with a single parameter* whose definition does not access a
 > static object can only violate the 'read only' guarantee if it uses
 > const_cast. The compiler can verify all the above and act accordingly.
 > Of course the problem is at the call site, but a compiler could
 > implement optimisations by a switch (#pragma) assuming that the function
 > will not change what is passed to it. Given a suitable linker breaches
 > of the assumption could be diagnosed.

Adding specialized #pragma's to direct optimization assumptions is, I can
tell you from experience, fraught with disaster. It takes someone who is
pretty familiar with how optimizers work to be able to reliably use such
features, the rest who try will get frustrated with the odd misbehavior it
produces.

One of the goals of D is to make quality D tools fairly straightforward to
implement. Having to rely on theoretical advanced optimizers and
unimplemented-as-yet linkers is not a viable option. It hurts C++ as well to
have to rely on such.

 > It seems to me, unsurprisingly, that D is tuned to your own coding style.

I think if you read the D newsgroups at news.digitalmars.com, you'll see
that the D community has enormous influence over D's design. The idea that D
is designed by myself in an isolation ward is way off base. I agree that I
have strong opinions on right ways and wrong ways to do things, but if you
look closely at D you'll see that you *can* do things every wrong way if
you're determined to <g>.

It would be inaccurate to assert that C++ doesn't have a builtin notion of
the right and wrong way to do things, as well. The idea that static
type-checking is superior permeates the language, as does the idea of
const-correctness.

 > That is normal for most languages with a single designed. C++ is
 > a notable exception because Bjarne Stroustrup designed a language that
 > allows people with very different style and priorities to use the same
 > tools.

D is a multi-paradigm language just as C++ is - in fact, it supports styles
that are not (or very inadequately) supported by C++, such as automatic
memory management, function literals, modules, and dynamic closures.

 > I have always described the two major meanings of const as:
 > 1) top level const is an instruction to the compiler that the programmer
 > will not ever try to change the state of this object. Any attempt by the
 > programmer to breach this results in consequences that are entirely the
 > programmers responsibility.

D has top level const objects.

 > 2) pointers and references to const qualified types have two sides.
 > Outwardly they promise read-only access to objects that are either const
 > qualified (logically immutable, or marked as read only) and plain,
 > unqualified objects. Inwardly they ask the compiler to ensure that the
 > promise is kept.

Since it's legal C++ for const qualified types to change, the compiler
cannot ensure the promise is kept. I don't see much added value in such a
weak concept, just as you don't (I infer from your posts, correct me if I'm
wrong) see the value in the language putting a stop to uninitialized data. I
don't think either of these positions is sufficient to justify the notion
that D is single minded and C++ is multi-minded, or vice versa.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/20/2004 6:05:27 AM

Louis Lavery wrote:
 > Walter <walter@digitalmars.nospamm.com> wrote in message
 > news:3a11d.179552$Fg5.58279@attbi_s53...
 >  > "Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
 >  > news:1A2S+CRwpCRBFwxc@robinton.demon.co.uk...
 >  >  > The only people I know that have a problem with const are those that
 >  >  > come from a C background. Novices quickly learn to write const correct
 >  >  > code if correctly introduced to the concept.
 >  >
 >  > I come from a C background <g> (and BASIC, FORTRAN, Pascal, and asm), and I
 >  > know how to write const correct code. I just haven't found a practical
 >  > benefit to it - it doesn't find real bugs, it doesn't look good, and it
 >  > doesn't help code generation.
 >
 > So FORTRAN taught you nothing of the perils of pass by non-const reference?

Why should it?  In FORTRAN -- at least, standard-conforming FORTRAN 77
-- it's illegal for a function to attempt to modify an argument that was
passed in as an expression rather than a variable.

The fact that some compilers produced legendary unpleasant behavior in
response to code which violated this constraint -- and which therefore
would be expected to exhibit undefined behavior, as the FORTRAN 77
standard says nothing about what a compiler will do with illegal
instructions -- should merely teach one to avoid expressions that
produce undefined behavior when one is writing programs.  :)

More seriously, does const correctness in C++ really have anything to do
with fixing the problem of trying to redefine a literal?  (I presume
that's what you're alluding to with the FORTRAN comment.)  The following
code does exactly the same thing as the FORTRAN code that supposedly
redefined the numeric constant 2 on some implementations, and it seems
that my C++ compiler catches the problem by methods that would exist
regardless of whether I was writing const-correct code.

   #include<iostream>
   int set_to_three(int &x) { x = 3; return x; }
   int main() {
     std::cout << set_to_three(2) << ' ' << 2 << '\n';
   }

- Brooks


-- 
The "bmoses-nospam" address is valid; no unmunging needed.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Brooks 9/20/2004 6:06:08 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:AchfJfZjbsSBFwOx@robinton.demon.co.uk...
 > In article <sDu2d.63285$D%.61590@attbi_s51>, Walter
 > <walter@digitalmars.nospamm.com> writes
 > > That is curious. One of my big problems is adding a member to a class
 > > with
 > > multiple constructors, and forgetting to add an initializer for it in
 > > one of
 > > the constructors. This has bitten me so many times that I routinely
 > > make an
 > > extra effort to go through the constructors line by line and comparing
 > > it
 > > with the member declarations.
 >
 > But that is a tools problem, one that you are particularly well placed
 > to solve.

I refer you to my other post to you on the subject, where I argue that it is
in the proper sphere of language design.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/20/2004 6:08:00 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:OcMcZ8ZXdsSBFwOa@robinton.demon.co.uk...
 > In article <tDu2d.63286$D%.56208@attbi_s51>, Walter
 > <walter@digitalmars.nospamm.com> writes
 > > Most compilers do, but that's a non-standard extension. I don't know
 > > of any
 > > compilers that will warn you that you forgot to add an initializer for
 > > data
 > > member 'bar::foo' in the constructor for 'bar'.
 >
 > Why doesn't your compiler do that? It does not appear to be hard to
 > diagnose.

Warnings are extensions to the language, and are not part the language
specification. Extensions are not portable, and quality of implementation
varies. What do you think of a language that implementations are apparently
expected to extend to make useful? Warnings can be properly viewed as
attempts to paper over defects in the language design. Why not fix the
language?

Secondly, it's not so easy to 100% determine this. If a member is
initialized by another function in another source file, the compiler isn't
going to pick that up, and will warn spuriously. Generating spurious
warnings is a fine way to convince programmers to turn it off.

It would not be hard to adjust the C++ language spec to fix this problem.
But first it needs to be recognized that it is a problem. Is it your opinion
that unininitialized members are not a problem?

 > > Then there's the problem of
 > > new'ing an array of basic types will give you an array of uninitialized
 > > garbage.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/20/2004 6:08:22 AM

In article <%za3d.9557$wV.1922@attbi_s54>, Walter
<walter@digitalmars.nospamm.com> writes
>That does illustrate a very different point of view we have. I'd rather fix
>the language than tell people not to use the core features of it.
>
>I'd also rather write:
>
>     int a[100];
>
>or:
>
>     int[] a = new int[100];
>
>rather than:
>
>     std::vector<int> a(100);

Which is presumably one of the reasons you designed D.
>
>Wouldn't you?

No, I am quite happy to use the mechanisms provided by a language in
ways that meet my design needs.

>Even setting aside for the moment the limitations of
>std::vector resulting from it not being part of the core language?

No, there are many things that I would do different if I were a designer
of a language for the twenty-first century but that comes pretty far
down the list. Two things I find important:

1) A general purpose language should try to be as neutral as possible
wrt to programming styles.

2) Given a choice, core features of a language should be those that
support better library development.


--
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 9/20/2004 3:29:45 PM

"Andrea Griffini" <agriff@tin.it> wrote in message
news:j4uqk017lq7iv19r4em5k47sdtbmdeotqg@4ax.com...

> I'm sorry I cand find no way to explain you that const-ness is just
> about const-ness; and that programming is about something else.

Andrea, you explained it very well, and i believe I understand all your
points.  And I rried to bring in the 'const-blindness' issue to make you
understand the other half.

> I would say that anyone that is still mentally flexible enough
> to question if what is found in books is always the only
> necessary truth or not can at least understand what I mean.

Then let me add another point:  *I* discovered the value of const way before
I read any books on it.  Before I arrived to C++ from a set of other
languades, I had a good collection of 'pain in the ass', problems I had, and
languages did not give a handy way to deal with. C++ covered a plenty of
those needs, with *const* being one of the top contenders.

That others too find it good, and even write books on the topic just shows
me there are other people around with similar mindset to me, but I'd value
it as much even if the literature were chill or hostile.

> I know it may come as a surprise to you... but
> (hold your hat):
>
>   - you can program without the concept of const-correctness
>   - you can program without static type checking
>   - you can program without "public/private/protected"
>   - you can approach object orientation even without
>     introducing the concept of "class"

Yeah, and you can swim across the ocean, but I'd rather use a boad, a ship
or a plane. :)

You know I came from mainly assembly background, and still keep mysel fresh,
and enjoy being able to 'do anything possible'  if there's  a real need.
But I wouldn;t consider discarding spoilers without reason.

For the same reason I hate anything that brings in restraints without
granting enough benefit.  History again -- as I started C on IBM PC I was
completely baffled on how any sane programmer would chose that s**t.  I
could do everything with greater ease and freedom in macro assy, and the
code emitted by compilers at the time was just unacceptable.   (And infact
pretty well noticeable on a turbo XT.)    I only made truce with C meeting
Unix.  Where it actually made sense. :)

IOW believe me, if I wouldn't observe real benefit in const, just burden I
would be the loudest protestor here.

> Before yelling nonsenses like "no one uses non-const globals"
> please at least try to understand what is discussed.

That wasn't what I said.  Please pay attention to the actual meaning -- if
you transform it to nonsense, it really become nonsense.

Any nonconst global punches a big hole on the encapsulation -- and broadens
the 'visible state' enormously.

> Can be const-ness useful for programming (and not for const-ness
> itself) ? Sure... in theory. But in practice in my experience
> it has been NOT useful

Yes.  In *your* practice it turned out not beenficial.  In *my* practie it
turned out beneficial.  I can't discard your experience or don't think you
lie.  Why can you not take my words at face value?

> as I can't remember a single case in which
> it really helped programming. Moreover seems I'm not the only
> one with this opinion.

Sure you are not alone -- I described an envorinment in the previous posts,
where it is near impossible to have benefits of const.

Just that you, and [long list of shops] didn't find benefits does not mean
such benefit is a mere theory.

> Const-correctness in addition of being of questionable utility
> in itself (it has a cost and gives something in return; it's
> all about the ROI point) is also daily ruined and insulted by
> the C++ idiom of "a const ref parameter is a nifty way to
> pass value". This is a philosophical error... a ref or a
> pointer (const or non-const) is about identities... a value
> is getting past the concept of identity.

The point is moot here. :)   For identity type you want to pass identity.
For a real value type identity is not imporrant, as any value will do, that
means you could as well pass what you already grabbed -- by ref -- if it
will be noticeable.
Also a C-grounded philosolhy is 'don't waste cycles for no reason'. If I
have a vector of vectors of strings -- containing a million strings overall,
it would make me uneasy to pass it by value.  My head work that way, where I
see a copy operaion, it visions the million copies happening, then I can't
sleep at night.    Probably a bad thing.
Then one could argue -- okey, that is really a fat object, *I* would not
copy that one either, but pass ref...  Where is a line?  And why draw it at
all, if we accept that method of passing, we better acept it full-heartedly.

Having no const would certainly break that thought.  As giving away a
nonconst ref to my vlauable vector woul make me even more uneasy -- wonder
how java people deal with that issue.

> Can passing logical values as const-refs bite back ? Sure.
> I've seen that happening. And please take some time thinking
> to what was the problem in the rect/pt example I posted...

Was I not clear you messed up the 'be aware of asignment to self' part in
the implementation?


I completely agree that *passing identities around* (!!!) is not a thing
without cost -- it introduces exra opportunities of aliasing, and you got
hit by a case you did not look after a possible conflict.

But it is not about *const* being good or bad, what is our primary topic.

This argument is like 'seat belts and airbags are generally bad, because now
drivers will keep sepeeding, and will crash more due to that'.   I don't
think so.  I at least never chose speed depending on having a belt, and
would not drive differently in a car without security. Belts are there for
another, *distinct* purpose.

So, even though we'd probably see much less identity-passing around without
const, I'd never think to blame those problems on having const.

> Please consider how much thinking should have been done
> about accepting a "const Pt&" parameter and how much thinking
> should be in theory used before accepting any "const X&"
> parameter in general.

IMHO not much, and just going by the checklist.  These days at least -- if
the code id decade-old it is more like a common naive mistake.   I too
learned the bloody way, having no Meyers at the time, and messed up stuff in
BigNumber library.  Just I didn;t come to blame the use, when it was a slip
in my mind in the implementation.

> I'm sure that if you think to it at least a bit probably you'll
> realize that the phrase "there's no 'anything else'" is
> absolutely ridicolous... problems may arise if there is *any*
> aliasing; it's not only about passing const ref objects
> that are members of the class defining the method being called.

Aliasing is a design issue, you must look after it always, and on the other
side take measures to constrain it.  It is rarely so lo-level an issue as in
your rect/pt case, and if you let it creep in, it is yay much nastier to
track down.
That I tried to express, if one drops the basic means of managing complecity
on several fronts, he creates a situation unmanageable -- then no wonder the
whole project collapses somewhere.

While normal measures weed out the problems you fear as a side effect.

> If you accept a "const X&" parameter and use it like being a
> value you should be sure that for example no object that
> you know (or that knows any object that you know, or that
> kowns and object that knows any object that you know ...)
> and of which you're calling methods holds any way to reach
> the object instance you're using as a value.

Yes, yes, yes.  :-)      Or in the rare case it doesn't hold, I have a red
flag with it.

> Also stating that this should be forbidden in the contract is
> IMO distilled nonsense in the general case (note for example
> that aliasing lives at the implementation level, not at the
> interface level; there's no way someone could call you
> remaining compliant about details you don't like to disclose).

Errr, come again?

> To make it concrete... can I pass you that Pt instance ?

Sure. If Rect's dox does not explicitely state there are restrictions,
you're free to pass any legal Pt.  (And it is a good principle to avoid
restrictions in general classes as programmers easily miss them.)

> It all depends on what you do in the implementation of
> the method (including what other classes that such an
> implementation may decide to call are doing in their
> implementation... and so on recursively).

Uh-oh.  just the other way around.   The *implementation* is the thing that
shall reflect the intent.  And the contract.

And you have all the choices here, get the argument by val, or create an
internal copy always, or only on address match, or provide an implementation
that works for any cases.

>  >Yeah. Exactly match 'error' in my vocabulary -- and not in yours.   Your
>  >mindset tells it is not really important, an innocent typo. In my it
says
>  >'you just lied'.
>
> I'm just saying that the instruction that were going to be
> compiled would have solved the problem. The function:
>
>     int strlen(char *s)
>     {
>       int n = 0;
>       while (s[n])
>         ++n;
>       return n;
>     }
>
> is not "broken" from a programming point of view. It's not
> even "lying" in the interface. None the less this is the
> most common source of "problems" I found about
> const-correctness.

For people not seeing a difference between 'char*' and 'const char*' there's
no lie, no brokenness and no nothing.  Just like many people call anything
little with many legs a 'bug' and that's no lie, but their view. While for
others bug means a bug, and there are butterflies, spiders, crabs and a
multitude of other things.
Precise comunication may be unnecessary for some situations, and pointing
out a difference may be a case of rude nitpicking -- but in programming the
baseline case is that any piece of information is close to vital.

>  > >     int strlen(const char *s);
>  >
>  >that I recon as a pure function producing a result without any side
effects
>  >noticeable.
>
> Whoa! That's a big jump. How can you tell that function is
> not going to store the passed pointer in a dictionary ?

As I told you -- general design.  If it does, that must be very well
documented and everyone warned around. (That is, if it does it in a
discoerable way -- if no one can tell the difference it does as it pleases.)

> How can you be sure that function is not going to open a
> database connection ?
> Do you know you can delete[] a "const char *" ?

Sure I know a multitude of ways to break a program -- but I work to create a
correct one.  And expect everyone around to do the same.    If some
component breaks the trust, it is unlikely impressive to use.

As mentioned earlier, try to look at things as parts of a *system* built for
the purpose to create working programs -- and minimize the effort along the
way.    In that systems the functions  checked in the codebase  are not
allowed to do any stuff theoretically possible, and you *can* rely on benign
behavior.

>  >Suppose I discovered the int result no longer interests me.  For the
latter
>  >case I just scrap the line and move on.
>
> Yikes! I can only hope you never get to maintain nuclear
> plants or airplane control software.

Surprise, that's what I actually do, write stuff for nuke plant, for the
monetary backbone, and similar.  And not in theory, my programs run there
this very monent, and did for years, or almost a decade.

The rule here, the more serious stuff you do, the less you can allow to
sloppiness, and the more you value any kind of automatic support. Especially
ststic checks at compile time.

>  >Telling liesid not bad 'cos immoral or what -- it is bad because
misleads
>  >people and cause them extra work.    If it is the function's role not to
>  >change something it shall talk about it in the interface.
>
> But that "const" is terribly far from being enough.

Sure it is not some silver bullet that could do anythong against people
alone. Like anythong else.  It's just a tool, and you must wield it in a
correct way.

> I once stopped thinking a bit about a "pure" function
> specifier for cases like "strlen" or "sqrt"; but things
> are not trivial in the general case.

That 'pure' function spec is a great miss for me. :)

>  > > It never really helped me, it costed
>  > > me quite a bit.
>  >
>  >No wonder.  :)
>  >
>  >'const-blind' I meant like color-blind.  When it is examined  there are
>  >pictures with colored spots. color-blind people just see a uniform spot,
and
>  >others see a red letter A in green field.
>
> It costed you also quite a bit; but this is a problem for
> your manager. You don't see the cost because you simply
> are not able to think to other possibility...

Come again?  Can you support that statement some way?

> I can think to programming both with const-correctness and
> without; why are you saying I'm color-blind ?

As said earlier,  anything can be done in assy, and correctly too.  To think
that is not a sign of anything in my eye, that's comon knowledge.  Your
blindness is elsewhere.

> I used const-correctness also with "C" (where you're basically
> not forced to) for long time before starting wondering what
> is *really* useful for...

And didn't see any real benefit in having also a compiler support for it.
Exactly what I'm talking about.

> With C++ however there's no choice. You must use it. Fullstop.

Did i mention by the time I started C++ it happily bound nonconst ref to a
temporary?  (Actually I still use compilers that just give a low-level
warning, that I tuned up by myself.)

> (with most of C++ you pay only what you use... but there
> is a "minimum two drinks" sign inside).

'Pay' in that meant runtime cost IMHO.    And the restriction was a laer
addition.  Not summoned from the thin air, but after observation of
problems.

(I personally didn't find much benefit in that change, but it is a big
community. Especially I could live with just a mandated warning. Too bad
there's no concept of warinings in C++ std.)

> However compilers luckyly enough are required to be
> more accurate than you and your "there's no 'anything
> else'" problem analysis...

As I pointed out to Walter in another post, it is completely different thing
what is for humans and what is for the compiler.

>  >Sure -- as that piece of information is one we don;t care about while
the
>  >other is vital.
>
> Please state who is "we"; I for one am not among the
> ones that consider const-correctness "vital".

Yeah, I stated it long time before, and you keep fending off that even in
this post. :)  Would you chose a single opinion?  ;-)

Let me restate the former term -- I call 'const-blind' someone who does not
consider const a vital difference.  Are we finally on the same track?  [And
please note there's no offensive in that, it's pure *terminology*.]

> Make a petition about "is const-correctnes *vital*
> for programming ?" and see how many would sign that.

In the past I thought up some proposals for even more tools to use const for
benefit -- people on csc didn't find them impressive enough.  And many never
caught the point.  I'm widely aware how many people are const-blind.

> Stop also thinking to how many millions of lines of
> code you're using daily are written with languages in
> which there was no const-correctness concept at all.

I don't give a damn about lines of code, the only thong interests me is a
*correct program*. Too bad I have to write one if I want to see one.  I'll
consider such arguments only after the scene cleans up.   And even then the
interesting point is not what is possible to achieve, but what helps and
what drags down.

>  >(I currently dont's use std:: at all, but
>  >my own set of collection classes, ...
>
> no comment ...

Loud no-comment. :)   Guess you wouldn't use it with the set of old,
pre-standard compilers I have to support either.

>  >Just try to rekon -- other people actually percieve a
>  >difference where you do not.    (Then whether that difference is
relevant in
>  >your actual environment is again a different issue.)
>
> I didn't say I see no difference. I said that I never
> happen to meet a case in which a const-correctness
> related error was spotting out a real essential
> coding problem. Seems I'm not alone in this view.
>
> You did ? Can you provide an example ?

Sorry, I can't *provide* an example out of thin air.  I can give you my boy
scout's word as a seasoned programmer writing serious stuff that actually
works, that *I* found benefit of const and a plenty of it.  You can take it
or discard it.

Paul



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Balog 9/20/2004 7:03:27 PM

Hello Brooks Moses

Brooks Moses schrieb:

>[snip]
>More seriously, does const correctness in C++ really have anything to do
>with fixing the problem of trying to redefine a literal?  (I presume
>that's what you're alluding to with the FORTRAN comment.)  The following
>code does exactly the same thing as the FORTRAN code that supposedly
>redefined the numeric constant 2 on some implementations, and it seems
>that my C++ compiler catches the problem by methods that would exist
>regardless of whether I was writing const-correct code.
>
>   #include<iostream>
>   int set_to_three(int &x) { x = 3; return x; }
>   int main() {
>     std::cout << set_to_three(2) << ' ' << 2 << '\n';
>   }
>

The above provided code should not compile successfully on a conforming 
compiler.

Greetings from Bremen,

Daniel


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply ISO 9/20/2004 7:08:06 PM

Brooks Moses <bmoses-nospam@cits1.stanford.edu> wrote in message
news:414E28D9.5A2139AE@cits1.stanford.edu...
> Louis Lavery wrote:
>  > Walter <walter@digitalmars.nospamm.com> wrote in message
>  > news:3a11d.179552$Fg5.58279@attbi_s53...
>  >  > "Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
>  >  > news:1A2S+CRwpCRBFwxc@robinton.demon.co.uk...
>  >  >  > The only people I know that have a problem with const are those
that
>  >  >  > come from a C background. Novices quickly learn to write const
correct
>  >  >  > code if correctly introduced to the concept.
>  >  >
>  >  > I come from a C background <g> (and BASIC, FORTRAN, Pascal, and
asm), and I
>  >  > know how to write const correct code. I just haven't found a
practical
>  >  > benefit to it - it doesn't find real bugs, it doesn't look good, and
it
>  >  > doesn't help code generation.
>  >
>  > So FORTRAN taught you nothing of the perils of pass by non-const
reference?
>
> Why should it?  In FORTRAN -- at least, standard-conforming FORTRAN 77
> -- it's illegal for a function to attempt to modify an argument that was
> passed in as an expression rather than a variable.
>

That's a bit harsh on the function, how would it know it'd been passed
an expression rather than a variable?

    Had FORTRAN const variables then it could refuse to compile a call to a
function with a literal passed as an arg unless that arg was declared const.

Louis.




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Louis 9/20/2004 7:13:32 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:ZmJ2d.209533$Fg5.15673@attbi_s53...

 > struct Value { int x; };
 >
 > /* Add v to both v2 and v3 */
 > void addValues(const Value& v, Value& v2, Value& v3)
 > {
 >       v2.x += v.x;
 >      v3.x += v.x;
 > }
 > addValues(s, s, t);        // oops
 >
 > Sure, there's a bug in the code, but it's a real easy trap to fall into
when
 > thinking that v is 'const'.

And my question here is: Do you seriously believe that if there's no const
the writer will be more aware for this case, and will properly think the
aliasing?

Suppose language has no const at all.   Will that prevent anyone to pass the
in param as ref instead of byval?  Or just as we read on this thread with
strlen(char *), it will be okey for the implementer, as he sees no
assignment to v then it is as good.

I don't know, but am skeptic it would improve anything.

To me looking at several refs for the same types tend to ring the bell, at
least it did fo this case -- but half asleep I could possibly spoil the
thing. Though writing the test cases it is hard to imagine an any good
tester will not include all kinds of same params, so it shall be discovered.

 > Let's look at the local caching issue. Suppose
 > we have the mouse position on the screen represented by x,y:
 >
 > struct Pos { int x,y; };
 >
 > Now let's have a function that gets a const reference to the current mouse
 > position, does some computations,
 > then returns the current mouse position:
 >
 > Pos DoSomethingWithMousePos(const Pos& p)
 > {
 >      Pos localp = p;        // create cache on stack because we feel the
need
 > for speed
 >      ... lots of computations on localp ...
 >      DoFoo();                // a long running function, running so long
that
 > the mouse position p changes
 >      return localp;            // our cached value is now out of date, and
 > not in sync with p any more
 > }
 >
 > Is that a farfetched mistake to make?

Honestly I couldn't make sense of this example.  If mouse pos is asked from
the system by a call, this function shall either get it for itself, and deal
with possible \change, or if it is an external feed, it shall treat it
nonchangign, I mean it shall work with the param, and the caller shall take
into account it might be moved at return.
If the mouse pos is some mem-mapped stuff, it shall be volatile, not const,
and consequences are even more interesting.

 > I don't think so. I know I often cache
 > pointer dereferences into locals in order to get them into registers for
 > fast computation.

Yes, and I dont's see how you fall on a difference.

 > The confusion is that const sometimes guarantees that a
 > value won't change, and sometimes does not.

Const guarantees exactly what it guarantees -- it is not a magic thing, nor
is its meaning hidden.    If someone fails to learn the language rules,
certainly he will produce incorrect programs with greal likehood, but that
stands for *whatever* set of rules, and for any language feature at all.

 > I don't believe there is much
 > bug finding value in the latter meaning of const, and its weakness can
lead
 > to the creation of bugs.

Well, everyone is entitled to his opinion.  Though I must tell you, that I
just dropped D from my watchlist -- I'm definitely not interested in any
watered-down version of C++ that removes functionality actually used.    The
same thing may distract other programmers.

Paul



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Balog 9/21/2004 11:22:43 AM

In message <j4uqk017lq7iv19r4em5k47sdtbmdeotqg@4ax.com>, Andrea Griffini
<agriff@tin.it> writes

   <snip>
 >Const-correctness in addition of being of questionable utility
 >in itself (it has a cost and gives something in return; it's
 >all about the ROI point) is also daily ruined and insulted by
 >the C++ idiom of "a const ref parameter is a nifty way to
 >pass value". This is a philosophical error... a ref or a
 >pointer (const or non-const) is about identities... a value
 >is getting past the concept of identity.
   <snip>

It's not an error, not even a philosophical one. A reference parameter
trait-converts a C++ object into an object with identity, whatever the
object might be in other parts of the program. There's nothing wrong
with that.

   John
-- 
John Harris

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply John 9/21/2004 11:23:39 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:b6Y5QZQCRYTBFwGa@robinton.demon.co.uk...
 > In article <%za3d.9557$wV.1922@attbi_s54>, Walter
 > <walter@digitalmars.nospamm.com> writes
 > >That does illustrate a very different point of view we have. I'd rather
fix
 > >the language than tell people not to use the core features of it.
 > >
 > >I'd also rather write:
 > >
 > >     int a[100];
 > >
 > >or:
 > >
 > >     int[] a = new int[100];
 > >
 > >rather than:
 > >
 > >     std::vector<int> a(100);
 >
 > Which is presumably one of the reasons you designed D.
 > >
 > >Wouldn't you?
 >
 > No, I am quite happy to use the mechanisms provided by a language in
 > ways that meet my design needs.

Does that mean aesthetics not an issue for you? I must say, they are very
important to me. Not only that, when one has to eschew the straightforward
syntax (in C++) and go with the much more complex one, that's a red flag
that something is wrong.


 > >Even setting aside for the moment the limitations of
 > >std::vector resulting from it not being part of the core language?
 > No, there are many things that I would do different if I were a designer
 > of a language for the twenty-first century but that comes pretty far
 > down the list. Two things I find important:
 >
 > 1) A general purpose language should try to be as neutral as possible
 > wrt to programming styles.

I'm curious what the rationale is for your opinion (that I infer from your
comments) that C++ is neutral and D is not. (C++ is hardly neutral, it is
very solidly in the camp of static type checking, for example. So is D.)


 > 2) Given a choice, core features of a language should be those that
 > support better library development.

I'd like to see any proposals for extending the language (C++ or D) to fix
problems with library features outlined in
www.digitalmars.com/d/cppstrings.html and
www.digitalmars.com/d/cppcomplex.html. I'd love to be able to improve the
metaprogramming ability of D.

I should also emphasize that despite D's core language support for arrays
and strings, that does not in any way prevent one from creating their own
library versions of arrays and strings using facilities equivalent to the
C++ ones. It's not either or. Libraries are not being shortchanged.

-Walter
www.digitalmars.com/d/ The D Programming Language


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/21/2004 11:28:00 AM

I had a question about const-ness some time ago.


It's related to generic programming.

Suppose I have a class C,

class C{
....
public:
   template<class F>
   static void f3(F f, C& c1, C& c2, C& c3){
     f(c1,c2,c3);
   }
};

f3 does not change the state of c1, c2, c3 by itself, but I don't know
if f will change them.

Now assume I have two functor classes:

struct MutableF{
   void operator()(C& c1, C& c2, C& c3);
};
struct ConstF{
   void operator()(const C&&#12288;c1, const C& c2, const C& c3);
};

And now I want this compile:

const C c;
C::f3(ConstF(), c, c, c);

This compiles too:
C c;
C::f3(MutableF(), c, c, c);


And I expect error from this:
const C c;
C::f3(MutableF(), c, c, c);


The problem is: I don't know if the functor f will change c1, c2 or
c3.
Making c1, c2 and c3 template parameter will work but it undermines
type safety. (I only really want objects of type C but it is possible
somebody pass in some IrrelevantC.)

Creating different versions of f3 for const and non-const sounds ok.
But for 3 parameter I need 2^3 = 8 functions. Exponential!

So far, I have not seen a perfect solution yet.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply fishifish 9/21/2004 11:28:57 AM

"Walter" <walter@digitalmars.nospamm.com> wrote
 > Now let's have a function that gets a const reference to the current mouse
 > position, does some computations, then returns the current mouse position:
 >
 > Pos DoSomethingWithMousePos(const Pos& p) {
 >    // create cache on stack
 >    // because we feel the need for speed
 >    Pos localp = p;
 >
 >    ... lots of computations on localp ...
 >
 >    // a long running function,
 >    // running so long that the mouse position p changes
 >    DoFoo();
 >
 >    // our cached value is now out of date,
 >    // and not in sync with p any more
 >    return localp;
 > }
 >
 > Is that a farfetched mistake to make?

If your comments really explain the purpose of the local copy, then
perhaps not... but the mistake would have been easily spotted in a
code review (or failing that, in the first round of testing). I think
it's more likely that the mistake is in the comments:

   Pos DoSomethingWithMousePos(const Pos& p) {
      // Save the original mouse position
      Pos localp = p;

      ... lots of computations on localp ...

      // a long running function,
      // running so long that the mouse position p changes
      DoFoo();

      // Original caller needs to know if the mouse moved while
      // this processing was going on. Original caller can easily
      // get the new mouse position, but for some reason it wasn't
      // able to save the original mouse position... fortunately
      // we were able to copy the data here, and we're happy to
      // send it back now.
      return localp;
   }

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply allan_w 9/21/2004 11:30:19 AM

 > hm...I don't like that free choice...it could make it impossible to verify
 > exception-safety
 > properties.
 >
Yes, such free choice can only be applied when the copy-ctor has no
side-effect and the object is reference-transparent. C++ certainly
doesn't honor this.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply fishifish 9/21/2004 11:31:33 AM

"Walter" <walter@digitalmars.nospamm.com> wrote
 > [An anecdote about the limits of implementation hiding: a friend of mine was
 > learning programming for the first time. He read the FORTRAN manual, and
 > based on it wrote a program that calculated some things and wrote the
 > results to a file. The program worked, but ran incredibly slowly. Stumped,
 > he showed it to a colleague, who asked him why, in the inner loop, he opened
 > the file, appended a character, and closed the file? Didn't he know that
 > that was terribly inefficient because of how file I/O worked? My friend said
 > he had no idea, since the manual said nothing about how the implementation
 > worked.]

Is there a moral to this story... perhaps "implementation hiding causes as
many problems as it solves?" (Coming to this "conclusion" would be no more
bizarre than concluding that const doesn't help find problems either.)

The mistake your friend make is completely understandable and perhaps
inevitable. In the early days of my career I did something similar (in my
case it was getting the current date thousands of times instead of once).
I doubt that there's a person reading this newsgroup that hasn't made a
similar mistake early in their career. By the same token, there was a
time in my childhood when I could crawl but was not yet able to walk. I
outgrew this by the time I was 30 years old! :-) We all learn.

The idea that implementation hiding somehow caused this learning curve is
both ludicrous and impossible to prove. (Name an operating system that
offers file I/O but doesn't hide *any* of the details!)

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply allan_w 9/21/2004 11:31:59 AM

jessica_boxer@yahoo.com (Jessica B) wrote
 > His take was that const correctness does not add significant
 > value to your software, and, given that it is quite burdensome
 > to maintain, he questioned whether it should be ignored.

 > Often some of the strangest syntax errors arise from const
 > correctness errors, and they sometimes take some
 > detangling. Further, many external libraries one uses
 > often have const correctness errors in them, which have
 > to be eliminated by tangled casts and inappropriate
 > mutables.
 >
 > I wonder if any of you can offer some convincing arguments
 > for the effort involved in const correctness. Or perhaps
 > offer some class of bug caught by it use.

For me, the biggest example is in the C library: strcpy. I remember
when I was new to the C language, the compiler I used prototyped it
like this:
     void strcpy(char*,char*); // I think... many years ago
without even parameter names to give clues about which pointer was
source and which was destination.

Today that same library function is prototyped like this:
     char* strcpy(char*, const char*);
which means that in many cases, if you get the arguments backwards,
it will be caught.
     strcpy("Error in function foo", errormessage); // Diagnostic required
Of course, I've used strcpy a few hundred thousand times since then, so
I've pretty much committed it to memory even without this aid -- but
that's the main point. For whatever functions you provide to your users
(or yourself), there are always going to be some functions that are
relatively new, or at the very least some functions that are used, but not
as often as other functions. Give the user a clue.

(Better yet would have been
     char* strcpy(char*dest, const char*source);
The IDE of this particular compiler has a very helpful feature -- when
you type the name of a function and start entering parameters, it shows
you the prototype including parameter names. And yet, the library that
still to this day ships with the compiler, (mostly?) does not have
parameter names... a missed opportunity to give helpful clues to the
users.)

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply allan_w 9/21/2004 11:32:44 AM

 > I wonder if any of you can offer some convincing arguments
 > for the effort involved in const correctness.

Would you like to look at the following articles?
1. http://www.parashift.com/c++-faq-lite/const-correctness.html
2. http://www.possibility.com/Cpp/const.html
3. http://c2.com/cgi/wiki?ConstCorrectness
4. http://codecraft.pool-room.com/Cpp/const-correctness-1.html
5. http://linuxjournal.com/article.php?sid=7629&mode=thread&order=0
6. http://gotw.ca/gotw/081.htm
7. http://gotw.ca/gotw/006.htm
8. http://adtmag.com/joop/crarticle.asp?ID=1550

Regards,
Markus Elfring

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Markus 9/21/2004 11:35:06 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:%za3d.9557$wV.1922@attbi_s54...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:414adcfb$0$213$14726298@news.sunsite.dk...
|  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
|  > | Then there's the problem of
|  > | new'ing an array of basic types will give you an array of uninitialized
|  > | garbage.
|  >
|  > that's because one should use std::vector<>.
|
| That does illustrate a very different point of view we have. I'd rather fix
| the language than tell people not to use the core features of it.

I  guess so.

| I'd also rather write:
|
|      int a[100];
|
| or:
|
|      int[] a = new int[100];
|
| rather than:
|
|      std::vector<int> a(100);
|
| Wouldn't you?

I don't mind either syntax.

| Even setting aside for the moment the limitations of
| std::vector resulting from it not being part of the core language?

What limitations are you referring to? So far it has been able to handle all
my problems.

And compiler vendors are free to employ whatever magic they feel is necessary
too for types in the standard library.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/21/2004 11:45:02 AM

On 21 Sep 2004 07:23:39 -0400, John G Harris
<news0@nospam.demon.co.uk> wrote:

>It's not an error, not even a philosophical one. A reference parameter
>trait-converts a C++ object into an object with identity, whatever the
>object might be in other parts of the program. There's nothing wrong
>with that.

There is no problem when you're passing what is basically
a value and the function receives an object with identity
(especially if the function indeed wanted a value and doesn't
care about the identity); the problem arises when *no*
conversion is done and you end up passing the object when
what was intended was passing its value instead...
More specifically the issue is defining functions accepting
const refs when they indeed want (aliasing-free) values.
This is done all over the place in C++ for performance reasons.

For example the "translate" function in the rect/pt example I
made was expecting a "delta" value and received a object instead.
Being an addressable object it is subject of being changed
as a side effect of other operations and this may break the
logic of the function that considers it a value.

That case was very simple (the method was changing a data
member of the same type of the passed pseudo-value and the
aliasing was direct), but can be much more complex (for
example the change can be the result of calling any method
of any other apparently unrelated object instance; this
is the paranoia that compilers are required to live with
and for which the "const" part doesn't mean anything at all).

Sometimes you need the object X, sometimes you need just the
value of X. The C++ idiom of passing values as const refs
for performance reasons make you always work with objects
and this can bit back. The true shame is that "const" is
completely immaterial in this context; but yet is IMO
perceived as providing a sense of security.

Andrea

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Andrea 9/21/2004 6:23:42 PM

Balog Pal wrote:
> "Andrea Griffini" <agriff@tin.it> wrote in message

>  > I'm doing this in the very moment. I'm investing some time
>  > in a language where there's no const-correctness concept
>  > in parameter passing (there are not even type declarations
>  > for variables!!!). So far it's like breathing fresh
>  > mountain air...
> 
> Guess you're pythoning.  I too picked up that language a little while ago as
> my next favorite language, and plan to put it to some real work in the near
> future.  ;-)   But that's again a differnt issue.   (I also gave it a
> thought that const could be pretty useful there too as a qualifier, but it's
> just a really light idea -- but I'll check it out.)

Python does have immutable objects (like string).
In my opinion C++ and Python nicely complement each other. However 
language features that work well for Python are not necessarily good for 
C++. This has in part to do with other aspects of the language, but also 
with the kind of programs one typically writes in that language. For the 
programs I write in Python (typically quite small, one person) its is 
great that it is dynamically typed, it is almost like writing pseudo 
code. However for the programs I write in C++ (typically large, with 
several people) I consider static type checking (and const-correctness) 
to be a big advantage if not essential.

I prefer compile-time errors over run-time errors. With Python being 
dynamically typed means that many errors can only be caught during 
run-time, and even extensive testing may not reveal all type errors.

-- 
Peter van Merkerk
peter.van.merkerk(at)dse.nl

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Peter 9/21/2004 6:27:44 PM

"Allan W" <allan_w@my-dejanews.com> wrote in message
news:7f2735a5.0409201017.1d00f355@posting.google.com...
> "Walter" <walter@digitalmars.nospamm.com> wrote
>  > Is that a farfetched mistake to make?
>
> If your comments really explain the purpose of the local copy, then
> perhaps not... but the mistake would have been easily spotted in a
> code review (or failing that, in the first round of testing).

Sure, there's a good chance that a code review or test code will pick it up,
especially in a necessarilly short example suitable for posting. All the
example tries to do is point out how a bug can result from misconstruing
what const does and does not guarantee, which is not so farfetched since the
meaning of const changes depending on where in the type declaration it is.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/21/2004 6:57:03 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:414feead$0$39258$14726298@news.sunsite.dk...
> "Walter" <walter@digitalmars.nospamm.com> wrote in message
> news:%za3d.9557$wV.1922@attbi_s54...
> | Even setting aside for the moment the limitations of
> | std::vector resulting from it not being part of the core language?
>
> What limitations are you referring to? So far it has been able to handle
all
> my problems.

o    Can't have vector data allocated in static data.
o    Can't have vector data allocated on the stack.
o    Can't have a static initializer for vector data.
o    No array literals.
o    No array slicing syntax.
o    No interoperability with string literals or std::string.
o    Poor integration with core arrays, such as:
        char a[], b[];
        ... (a + b) ...;    // what does that mean?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 9/21/2004 6:59:52 PM

"Balog Pal" <pasa@lib.hu> wrote in message
news:414f422d@andromeda.datanet.hu...
> I don't know, but am skeptic it would improve anything.

I'm thinking more about what improvement const brings to the table relative
to its cost.


> Though writing the test cases it is hard to imagine an any good
> tester will not include all kinds of same params, so it shall be
discovered.

Nearly all bugs can be found with suitable test cases <g>.

> Honestly I couldn't make sense of this example.  If mouse pos is asked
from
> the system by a call, this function shall either get it for itself, and
deal
> with possible \change, or if it is an external feed, it shall treat it
> nonchangign, I mean it shall work with the param, and the caller shall
take
> into account it might be moved at return.

We both know how to write it correctly since we both understand const, the
issue is how easy is it to misunderstand const and thereby create a bug.

>  > The confusion is that const sometimes guarantees that a
>  > value won't change, and sometimes does not.
> Const guarantees exactly what it guarantees -- it is not a magic thing,
nor
> is its meaning hidden.    If someone fails to learn the language rules,
> certainly he will produce incorrect programs with greal likehood, but that
> stands for *whatever* set of rules, and for any language feature at all.

Of course. But I see much value in having a set of rules that follow an
intuitive pattern. This reduces the amount a programmer has to learn to use
a language successfully. As several have pointed out in this thread, const
has two very different meanings depending on where the const appears in the
type declaration. The first meaning of const is that it is "constant", i.e.,
will not change. That makes perfect, intuitive sense. The second meaning is
that the data cannot be changed through that reference. It does not mean it
is "constant". This is surprising behavior.

A good set of language rules should be helpful in minimizing errors caused
by a programmer not thoroughly understanding those rules. Only a small
percentage of C++ programmers understand C++ as thoroughly as you and I do.


>  > I don't believe there is much bug finding value in the latter meaning
of const, and its weakness can
> > lead to the creation of bugs.
> Well, everyone is entitled to his opinion.  Though I must tell you, that I
> just dropped D from my watchlist -- I'm definitely not interested in any
> watered-down version of C++ that removes functionality actually used.
The
> same thing may distract other programmers.

There isn't anyone who agrees 100% on the feature set of D, or C++, or Java,
or any language. But to give up on D because of one issue means you're also
willing to give up a great deal of bug-finding features that D has that C++
does not have:

1) guaranteed initialization of data, locals, class members, etc.
2) array bounds checking
3) class invariants
4) function pre and post conditions
5) typesafe variadic function arguments
6) detection of missing cases in switch statements
7) disallowing of assignments in conditionals, i.e. if(a=b) is illegal
8) padding 'holes' in structs are guaranteed to be set to 0
9) ; is not allowed as the body of several constructs such as if, while,
etc., avoiding mistakes such as:
    if (x);
        foo;
and:
    while (baz);
     { ... }
10) builtin unit testing
11) separation of function arguments into in, out, and inout storage
classes. This is arguably more expressive and straightforward than using
const for that purpose.
12) no more problems from mismatched .h and .c declarations
13) default initialization of floating point values to NaN
14) no name space 'pollution' bugs from macro names
15) no implicit conversion of floating point values to integers
16) guaranteed sizes of basic types

-Walter
www.digitalmars.com/d/ the D Programming Language


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 9/21/2004 7:02:53 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:ZDY3d.86006$D%.26675@attbi_s51...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:414feead$0$39258$14726298@news.sunsite.dk...
| > "Walter" <walter@digitalmars.nospamm.com> wrote in message
| > news:%za3d.9557$wV.1922@attbi_s54...
| > | Even setting aside for the moment the limitations of
| > | std::vector resulting from it not being part of the core language?
| >
| > What limitations are you referring to? So far it has been able to handle
| all
| > my problems.
|
| o    Can't have vector data allocated in static data.

you're referring to ROMability, right? won't boost::array<T,sz> do here?

| o    Can't have vector data allocated on the stack.

just use a custom allocator that does stack allocation.

| o    Can't have a static initializer for vector data.

yeah, won't boost::array<T,sz> do here?

| o    No array literals.

I believe a library solution is superior here. For example, in boost.assign

list_of( 1 )( 2 )( 3 )( 4 )( 5 )

makes an anonymous "array" convertible to any container or usable in container
algorithms. In the next version I will provide
a compile-time sized one + support for references:

list_of<5>( 1 )( 2 )( 3 )( 4 )( 5 );

so you might say

Foo a,b,c,d,e;
....
std::sort( list_of<5,Foo&>( a )( b )( c )( d )( e ) );
Foo& max = std::max( list_of<5,Foo&>( a )( b )( c )( d )( e ) );

| o    No array slicing syntax.

yeah, but this operation can be expensive when you're not in a gc language. so
it does not
naturally belong in vector, but more in lists.

| o    No interoperability with string literals or std::string.

there is definitely some problems with strings. Did you have anyone in mind?

| o    Poor integration with core arrays, such as:
|         char a[], b[];
|         ... (a + b) ...;    // what does that mean?

yeah, in C++ we don't want that integration because one should not use those
arrays in the first place.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/21/2004 10:45:11 PM

"Allan W" <allan_w@my-dejanews.com> wrote in message
news:7f2735a5.0409200945.13598d9d@posting.google.com...
> "Walter" <walter@digitalmars.nospamm.com> wrote
>  > [An anecdote about the limits of implementation hiding: a friend of
mine was
>  > learning programming for the first time. He read the FORTRAN manual,
and
>  > based on it wrote a program that calculated some things and wrote the
>  > results to a file. The program worked, but ran incredibly slowly.
Stumped,
>  > he showed it to a colleague, who asked him why, in the inner loop, he
opened
>  > the file, appended a character, and closed the file? Didn't he know
that
>  > that was terribly inefficient because of how file I/O worked? My friend
said
>  > he had no idea, since the manual said nothing about how the
implementation
>  > worked.]
>
> Is there a moral to this story... perhaps "implementation hiding causes as
> many problems as it solves?" (Coming to this "conclusion" would be no more
> bizarre than concluding that const doesn't help find problems either.)

I wrote that in response to Francis asking whether or not I "believe" in
implementation hiding. On balance, I'd say it's a good thing. It just isn't
perfect, that's all.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/21/2004 10:47:02 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:415085ca$0$39255$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:ZDY3d.86006$D%.26675@attbi_s51...
 > |
 > | "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
 > | news:414feead$0$39258$14726298@news.sunsite.dk...
 > | > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > | > news:%za3d.9557$wV.1922@attbi_s54...
 > | > | Even setting aside for the moment the limitations of
 > | > | std::vector resulting from it not being part of the core language?
 > | >
 > | > What limitations are you referring to? So far it has been able to
handle
 > | all
 > | > my problems.
 > |
 > | o    Can't have vector data allocated in static data.
 >
 > you're referring to ROMability, right?

No, I'm referring to:

static int foo[100];

 > won't boost::array<T,sz> do here?

I'm not familiar with that.

 > | o    Can't have vector data allocated on the stack.
 > just use a custom allocator that does stack allocation.

I challenge you to write a custom allocator that does that, compare it with:

     char foo[100];

compile both and examine the code generated. As an aside, wouldn't you
rather write:

     char foo[100];

than write a custom stack allocator? Shouldn't simple, straightforward
things be expressible in a simple, straightforward manner?

 > | o    Can't have a static initializer for vector data.
 > yeah, won't boost::array<T,sz> do here?

I'm not familiar with boost::array. What would:

static int foo[100] = { 1, 3, 27, };

look like with boost::array?

 > | o    No array literals.
 >
 > I believe a library solution is superior here. For example, in
boost.assign
 >
 > list_of( 1 )( 2 )( 3 )( 4 )( 5 )
 >
 > makes an anonymous "array" convertible to any container or usable in
container
 > algorithms. In the next version I will provide
 > a compile-time sized one + support for references:
 >
 > list_of<5>( 1 )( 2 )( 3 )( 4 )( 5 );
 >
 > so you might say
 >
 > Foo a,b,c,d,e;
 > ...
 > std::sort( list_of<5,Foo&>( a )( b )( c )( d )( e ) );
 > Foo& max = std::max( list_of<5,Foo&>( a )( b )( c )( d )( e ) );

I admire the technical skill needed to invent that using templates.


 > | o    No array slicing syntax.
 > yeah, but this operation can be expensive when you're not in a gc
language.

True, but you can get gc to work with C++. I know, I use it routinely.

 > so it does not naturally belong in vector, but more in lists.

There's still no syntactic sugar for it <g>.

 > | o    No interoperability with string literals or std::string.
 > there is definitely some problems with strings. Did you have anyone in
mind?

When would you use std::string vs when would you use std::vector<char>? What
if you need to work with a library that made a different choice?

 > | o    Poor integration with core arrays, such as:
 > |         char a[], b[];
 > |         ... (a + b) ...;    // what does that mean?
 >
 > yeah, in C++ we don't want that integration because one should not use
those
 > arrays in the first place.

We both apparently agree that core arrays are broken in C++. Your answer is
to ignore them and use something else, my answer is to fix them. I've gotten
the distinct impression in this n.g. (not necessarilly from you) that some
think that by fixing core features like arrays and strings, that D has
"taken away" something. D takes nothing away, you can create a vector class
in D using equivalent constructs to the way std::vector is implemented. (You
can even overload the slice operators!) The only thing is, it's just
pointless to write a vector class in D because the core arrays work. It'd be
as pointless as writing a template in C++ for ints.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 9/22/2004 10:01:57 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:SW44d.88205$D%.24859@attbi_s51...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:415085ca$0$39255$14726298@news.sunsite.dk...
|  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
|  > news:ZDY3d.86006$D%.26675@attbi_s51...
|  > |

|  > | o    Can't have vector data allocated in static data.
|  >
|  > you're referring to ROMability, right?
|
| No, I'm referring to:
|
| static int foo[100];
|
|  > won't boost::array<T,sz> do here?
|
| I'm not familiar with that.
|
|  > | o    Can't have vector data allocated on the stack.
|  > just use a custom allocator that does stack allocation.
|
| I challenge you to write a custom allocator that does that, compare it with:
|
|      char foo[100];
|
| compile both and examine the code generated. As an aside, wouldn't you
| rather write:
|
|      char foo[100];
|
| than write a custom stack allocator? Shouldn't simple, straightforward
| things be expressible in a simple, straightforward manner?

of course. but we can use boost::array here too, see below.

|  > | o    Can't have a static initializer for vector data.
|  > yeah, won't boost::array<T,sz> do here?
|
| I'm not familiar with boost::array. What would:
|
| static int foo[100] = { 1, 3, 27, };
|
| look like with boost::array?

static boost::array<int,100> = { 1, 3, 27 };

fairly simple and with no overhead. It's also in the first standard technical
report as tr1::array

|  > | o    No array literals.
|  >
|  > I believe a library solution is superior here. For example, in
| boost.assign
|  >
|  > list_of( 1 )( 2 )( 3 )( 4 )( 5 )
|  >
|  > makes an anonymous "array" convertible to any container or usable in
| container
|  > algorithms. In the next version I will provide
|  > a compile-time sized one + support for references:
|  >
|  > list_of<5>( 1 )( 2 )( 3 )( 4 )( 5 );
|  >
|  > so you might say
|  >
|  > Foo a,b,c,d,e;
|  > ...
|  > std::sort( list_of<5,Foo&>( a )( b )( c )( d )( e ) );
|  > Foo& max = std::max( list_of<5,Foo&>( a )( b )( c )( d )( e ) );
|
| I admire the technical skill needed to invent that using templates.

he he, thanks. another reason I consider it superior is also that we can craft
hooks into the sequence, eg.

Foo random_foo();
list_of<Foo>( a ).repeat( 10, b )( c )( d )( e ).repeat_fun( 10,
&random_foo );

|  > | o    No interoperability with string literals or std::string.
|  > there is definitely some problems with strings. Did you have anyone in
| mind?
|
| When would you use std::string vs when would you use std::vector<char>? What
| if you need to work with a library that made a different choice?

its not clear how this would be solved by integrating strings into the
langauage. you would still have to deal
with legacy code.

|  > | o    Poor integration with core arrays, such as:
|  > |         char a[], b[];
|  > |         ... (a + b) ...;    // what does that mean?
|  >
|  > yeah, in C++ we don't want that integration because one should not use
| those
|  > arrays in the first place.
|
| We both apparently agree that core arrays are broken in C++. Your answer is
| to ignore them and use something else, my answer is to fix them.

agreed. I think the time would be better spend on other features for C++0x.

| I've gotten
| the distinct impression in this n.g. (not necessarilly from you) that some
| think that by fixing core features like arrays and strings, that D has
| "taken away" something.

yeah, ok, I wouldn't agree to that. :-)

| pointless to write a vector class in D because the core arrays work. It'd be
| as pointless as writing a template in C++ for ints.

I once imagined that a class template for an int could be used to make
run-time checks of all narrowing conversions, but I
havn't gotten the time to implement it. should be fairly trivial with the
numeric conversion library in boost.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/22/2004 6:30:29 PM

Here's a possibility (if your compiler is up to it: probably needs to
be EDG or IBM):

  #include <boost/utility/enable_if.hpp>
  #include <boost/type_traits/is_same.hpp>
  #include <boost/type_traits/remove_const.hpp>

  class C{
  ...
     
   public:
     template<class F, typename T1, typename T2, typename T3>
     static typename boost::enable_if_c<
              boost::is_same<typename boost::remove_const<T1>::type,
C>::value &&
              boost::is_same<typename boost::remove_const<T2>::type,
C>::value &&
              boost::is_same<typename boost::remove_const<T3>::type,
C>::value
            >::type
      f3(F f, T1& c1, T2& c2, T3& c3){
       f(c1,c2,c3);
     }
  };


Quite a bit of noise (which you can factor out to some extent), but I
think it does roughly what you're asking.

Unfortunately it disallows implicit conversions on parameters to f,
which should probably be allowed in the const case. That problem is
not solvable in the language as it stands (the r-value reference
proposal addresses that). Currently, in this context, you can allow
conversions or non-const references, but not both.



James

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply jhopkin 9/22/2004 6:31:34 PM

> | o    Can't have vector data allocated on the stack.
> 
> just use a custom allocator that does stack allocation.
> 

Exactly what would that look like?  (not a rhetorical question)

Scott McCaskill

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply scott_mccaskill 9/22/2004 6:53:55 PM

"Balog Pal" <pasa@lib.hu> wrote:
>  > /* Add v to both v2 and v3 */
>  > void addValues(const Value& v, Value& v2, Value& v3)
>  > {
>  >       v2.x += v.x;
>  >      v3.x += v.x;
>  > }
>  > addValues(s, s, t);        // oops
>  >
>  > Sure, there's a bug in the code, but it's a real easy trap to fall into
> when
>  > thinking that v is 'const'.
>
> And my question here is: Do you seriously believe that if there's no const
> the writer will be more aware for this case, and will properly think the
> aliasing?

void addValues(Value v, Value& v2, Value& v3)
{
  v2.x += v.x;
  v3.x += v.x;
}

Const reference is a poor man's value object.  The speed advantage is
undeniable, but using true value objects is usually the cleaner, more
robust design.

Were there no const, value objects would be put to much better use.

- Anders




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Anders 9/22/2004 6:56:25 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:41515bc6$0$39265$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > | I'm not familiar with boost::array. What would:
 > |
 > | static int foo[100] = { 1, 3, 27, };
 > |
 > | look like with boost::array?
 >
 > static boost::array<int,100> = { 1, 3, 27 };
 >
 > fairly simple and with no overhead. It's also in the first standard
technical
 > report as tr1::array

Does it actually statically allocate the array? Or does it just look like it
does <g>?


 > |  > | o    No interoperability with string literals or std::string.
 > |  > there is definitely some problems with strings. Did you have anyone
in
 > | mind?
 > |
 > | When would you use std::string vs when would you use std::vector<char>?
What
 > | if you need to work with a library that made a different choice?
 >
 > its not clear how this would be solved by integrating strings into the
 > langauage. you would still have to deal
 > with legacy code.

It's solved, at least for D, by the fact that core strings, string literals,
and arrays of chars are all the same thing. Literally.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/23/2004 8:09:22 AM

"Scott McCaskill" <scott_mccaskill@yahoo.com> wrote in message
news:4d4ad750.0409220551.1d82efd9@posting.google.com...
| > | o    Can't have vector data allocated on the stack.
| >
| > just use a custom allocator that does stack allocation.
| >
|
| Exactly what would that look like?  (not a rhetorical question)

roughly as

StackAllocator<T> sa( size );
std::vector<T, StackAllocator<T> > l( sa );
....

see "Custom STL allocators" by Pete Isensee in "Game Programming Gems 3"  for
details.

There is however little usage when one can use boost::array<T,size>.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/23/2004 8:13:03 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<ZDY3d.86006$D%.26675@attbi_s51>...
 > "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
 > news:414feead$0$39258$14726298@news.sunsite.dk...
 > > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > > news:%za3d.9557$wV.1922@attbi_s54...
 > > | Even setting aside for the moment the limitations of
 > > | std::vector resulting from it not being part of the core language?
 > >
 > > What limitations are you referring to? So far it has been able to handle
 >  all
 > > my problems.
 >
 > o    Can't have vector data allocated in static data.
 > o    Can't have vector data allocated on the stack.
 > o    Can't have a static initializer for vector data.
 > o    No array literals.
 > o    No array slicing syntax.
 > o    No interoperability with string literals or std::string.
 > o    Poor integration with core arrays, such as:
 >         char a[], b[];
 >         ... (a + b) ...;    // what does that mean?

Despite these "limitations" (I cannot remember ever wanting/needing
std::vector to do any of these things), std::vector manages to somehow
be usable. None of these things justify core language changes, IMHO.

Bob

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply belvis 9/23/2004 8:15:13 AM

"Bob Bell" <belvis@pacbell.net> wrote in message
news:c87c1cfb.0409221106.4087a5b4@posting.google.com...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
news:<ZDY3d.86006$D%.26675@attbi_s51>...
 >  > "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
 >  > news:414feead$0$39258$14726298@news.sunsite.dk...
 >  > > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 >  > > news:%za3d.9557$wV.1922@attbi_s54...
 >  > > | Even setting aside for the moment the limitations of
 >  > > | std::vector resulting from it not being part of the core language?
 >  > >
 >  > > What limitations are you referring to? So far it has been able to
handle
 >  >  all
 >  > > my problems.
 >  >
 >  > o    Can't have vector data allocated in static data.
 >  > o    Can't have vector data allocated on the stack.
 >  > o    Can't have a static initializer for vector data.
 >  > o    No array literals.
 >  > o    No array slicing syntax.
 >  > o    No interoperability with string literals or std::string.
 >  > o    Poor integration with core arrays, such as:
 >  >         char a[], b[];
 >  >         ... (a + b) ...;    // what does that mean?
 >
 > Despite these "limitations" (I cannot remember ever wanting/needing
 > std::vector to do any of these things), std::vector manages to somehow
 > be usable. None of these things justify core language changes, IMHO.

I agree it's usable. Things like allocating arrays on the stack are speed
optimizations, not usability issues. So are static initializers. Can I refer
you to Allan Odgaard's vector<char> version of a D benchmark at
www.digitalmars.com/d/cppstrings.html? Not only is it an interesting
aesthetic comparison, but D's version runs 3 times faster.

BTW, there are two C++ versions - neither written by myself - one uses
std::string and the other uses std::vector<char>. Which do you prefer to use
for strings, and why? What would you do if you used one and had to use a
library that used the other, or that used core arrays?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 9/24/2004 11:40:49 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:Nml4d.4816$He1.3208@attbi_s01...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:41515bc6$0$39265$14726298@news.sunsite.dk...
|  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
|  > | I'm not familiar with boost::array. What would:
|  > |
|  > | static int foo[100] = { 1, 3, 27, };
|  > |
|  > | look like with boost::array?
|  >
|  > static boost::array<int,100> = { 1, 3, 27 };
|  >
|  > fairly simple and with no overhead. It's also in the first standard
| technical
|  > report as tr1::array
|
| Does it actually statically allocate the array? Or does it just look like it
| does <g>?

yes and no. It's simply a wrapped built-in array. When that array is public
and there are no constructors
, you can use the brace-initializer list.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/24/2004 11:42:42 AM

Bob Bell wrote:
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message news:<ZDY3d.86006$D%.26675@attbi_s51>...
 >  > "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
 >  > news:414feead$0$39258$14726298@news.sunsite.dk...
 >  > > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 >  > > news:%za3d.9557$wV.1922@attbi_s54...
 >  > > | Even setting aside for the moment the limitations of
 >  > > | std::vector resulting from it not being part of the core language?
 >  > >
 >  > > What limitations are you referring to? So far it has been able to handle
 >  >  all
 >  > > my problems.
 >  >
 >  > o    Can't have vector data allocated in static data.
 >  > o    Can't have vector data allocated on the stack.
 >  > o    Can't have a static initializer for vector data.
 >  > o    No array literals.
 >  > o    No array slicing syntax.
 >  > o    No interoperability with string literals or std::string.
 >  > o    Poor integration with core arrays, such as:
 >  >         char a[], b[];
 >  >         ... (a + b) ...;    // what does that mean?
 >
 > Despite these "limitations" (I cannot remember ever wanting/needing
 > std::vector to do any of these things), std::vector manages to somehow
 > be usable. None of these things justify core language changes, IMHO.

Strange, why do I have the feeling so often in discussions like this
that many people think when they don't feel the need for a certain
feature that the feature is unnecessary? I'm not addressing you
specifically, Bob. I have followed this thread (and indeed others) for a
while and I can not help feeling slightly annoyed.

I think in discussions about features that are reported "missing" in C++
(either language or library) it is worth keeping the following in mind:

o  If you don't see the value of adding a new feature that doesn't mean
it is not valuable for someone else. It may just be a matter of
different programming styles or different application domains. Don't
ever say: I never needed this, so it must be useless. Don't generalize
your own viewpoint. C++ is a multi-style, multi-purpose, multi-paradigm
language, and that is one of its strengths.

o  You can get accustomed to idiosyncrasies so much that you don't
perceive them as idiosyncrasies anymore. If a particular idiosyncrasy
has faded from your radar, it doesn't mean it isn't there anymore. It
may produce a big fat spot on other people's radar. Staying aware of
this keeps you away from language bigotry.

o  Even if adding a feature does not enable you to do anything that
couldn't be done somehow before, it may be valuable just because of the
clarity, simplicity or robustness it brings. (Provided it really does
bring this).

o  Including a new feature in C++ will always be a compromise between
the value of the new feature and the impact it has on what's already
there. That is, what impact it has on the software and documents already
written, and what impact it has on the complexity of the language.
Because it is a compromise, everyone quite legitimately will have
his/her own view of where the right balance is.

o  Because of the widespread usage C++ enjoys and the large body of
software already written in it, any addition or change to language or
library will rightly be subject to tight scrutiny. The prudence of the
language maintainers is well founded: A few less-than-optimal choices
have already been made in the past, and the effect is very hard to undo.

My own viewpoint is that we need a more aggressive scheme of how to
deprecate stuff in the standard that over time was found to be wanting.
We can not continue to pile one thing onto another, we need some
cleaning up. This applies to both the language and the library. We need
a way to get rid of std::vector<bool> and such things, and we need to
clean up the syntax of the language (template >> problem, declaration
syntax, etc.). This will break old code, so the transition needs to be
managed. If we have an agreed upon way how to do this, we have a better
chance of keeping the language up-to-date without causing a complexity
explosion.

It is interesting to watch what the .NET people are doing with C++ right
now. They are adding new keywords without the double underscore prefix,
for example. This is a deviation from the standard. They are not doing
that lightheadedly; they obviously feel the standard proper is too rigid
for what they want to achieve. I tend to agree.

At some point, however, you are better off creating a new language
instead. This is what Walter did with D. I'm glad it exists. It is a
good playground for ideas that would be too radical for C++. Maybe one
day some of its features get backported to C++, similar to what happened
to C.

-- 
Cheers
Stefan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stefan 9/24/2004 3:25:42 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:

 > roughly as
 >
 > StackAllocator<T> sa( size );
 > std::vector<T, StackAllocator<T> > l( sa );

Unless sa() is a macro, I don't see how StackAllocator could possibly
provide storage from anywhere but the heap, unless it's pulling space
from some statically allocated pool. The "size" argument given to
StackAllocator sa's constructor cannot be seen at compile-time, so no
array known to be of sufficient size could be allocated until
run-time.

-- 
Steven E. Harris

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Steven 9/24/2004 3:37:09 PM

Walter wrote:
 > "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
 > news:41515bc6$0$39265$14726298@news.sunsite.dk...
 > > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > > | I'm not familiar with boost::array. What would:
 > > |
 > > | static int foo[100] = { 1, 3, 27, };
 > > |
 > > | look like with boost::array?
 > >
 > > static boost::array<int,100> = { 1, 3, 27 };
 > >
 > > fairly simple and with no overhead. It's also in the first standard
 > > technical report as tr1::array
 >
 > Does it actually statically allocate the array? Or does it just look like it
 > does <g>?
<snip>

The raw array is a public member (the only one, in fact).  If it were
dynamically allocated, the aggregate initialisation syntax wouldn't
work in the current language.

-- 
Ben Hutchings
Horngren's Observation:
                    Among economists, the real world is often a special case.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Ben 9/24/2004 3:40:31 PM

> | o    Can't have vector data allocated in static data.
> 
> you're referring to ROMability, right? won't boost::array<T,sz> do here?

Isn't it sufficient to have a (globally) const object and a smart
compiler?

> | o    Can't have vector data allocated on the stack.
> 
> just use a custom allocator that does stack allocation.

I think the issue here is how to get stack space that persists as long
as the vector does and/or what API there is to allocate such stack
space. Typically a stack allocation only lasts as long as a (actual)
function call or (logical) scope e.g. you can allocate through the
alloca call but that only lasts as long as the function that contains
it. Furthermore, a custom allocator would be expected to deallocate
and allocate by the will of the vector, it's not clear how to wrap
alloca to do this.

> | o    Can't have a static initializer for vector data.
> 
> yeah, won't boost::array<T,sz> do here?

Static initializers can be simulated with template non-type
parameters. Of course this isn't in the vector spec. E.g.

vec <int> v = {1, 2, 3, 4}; // can't do this to an object
vec <int> v = vec <int>::set <1, 2, 3, 4> (); // OK...

What's the difference between this and a simple constructor call?
Because all the parameters are compile time constants, the compiler
can substitute static data so that no load happens during run time.
The price you pay is you can't use variables.

> | o    No array literals.
> 
> I believe a library solution is superior here. For example, in boost.assign
> 
> list_of( 1 )( 2 )( 3 )( 4 )( 5 )

Strikes me as being rather obtuse. C99 compatibility may eventually
rescue C++ here, since C99 has array initializers similar to the way
C# and Java do it e.g.

vec <int> v ((int []) {1, 2, 3, 4})

> | o    No array slicing syntax.
> 
> yeah, but this operation can be expensive when you're not in a gc language. so
> it does not
> naturally belong in vector, but more in lists.

Depends on what you mean by slicing. std::valarray has a neat slicing
syntax based on overloading operator[].

> | o    No interoperability with string literals or std::string.
> 
> there is definitely some problems with strings. Did you have anyone in mind?

Pass.

> | o    Poor integration with core arrays, such as:
> |         char a[], b[];
> |         ... (a + b) ...;    // what does that mean?
> 
> yeah, in C++ we don't want that integration because one should not use those
> arrays in the first place.

What should it mean? Is it concatenation like std::string or
arithmetic across elements like std::valarray?

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply glenlow 9/25/2004 5:34:57 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message news:<GQw4d.140866$3l3.119949@attbi_s03>...
 > "Bob Bell" <belvis@pacbell.net> wrote in message
 > news:c87c1cfb.0409221106.4087a5b4@posting.google.com...
 >  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 >  news:<ZDY3d.86006$D%.26675@attbi_s51>...
 >  >  > "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
 >  >  > news:414feead$0$39258$14726298@news.sunsite.dk...
 >  >  > > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 >  >  > > news:%za3d.9557$wV.1922@attbi_s54...
 >  >  > > | Even setting aside for the moment the limitations of
 >  >  > > | std::vector resulting from it not being part of the core language?
 >  >  > >
 >  >  > > What limitations are you referring to? So far it has been able to
 >  handle
 >  all
 >  >  > > my problems.
 >  >  >
 >  >  > o    Can't have vector data allocated in static data.
 >  >  > o    Can't have vector data allocated on the stack.
 >  >  > o    Can't have a static initializer for vector data.
 >  >  > o    No array literals.
 >  >  > o    No array slicing syntax.
 >  >  > o    No interoperability with string literals or std::string.
 >  >  > o    Poor integration with core arrays, such as:
 >  >  >         char a[], b[];
 >  >  >         ... (a + b) ...;    // what does that mean?
 >  >
 >  > Despite these "limitations" (I cannot remember ever wanting/needing
 >  > std::vector to do any of these things), std::vector manages to somehow
 >  > be usable. None of these things justify core language changes, IMHO.
 >
 > I agree it's usable. Things like allocating arrays on the stack are speed
 > optimizations, not usability issues. So are static initializers.

Yet you were offering this list as reasons why vector should be part
of the core language. IMHO, optimization isn't a strong enough reason
for making vector part of the language, especially when you consider
that C++ gives me very good tools for optimization already. For
example, and as Thorsten pointed out, boost::array provides a
best-of-both-worlds solution that gives me a vector-like interface
with the peformance characteristics of a built-in array. Further,
making vector part of the core language won't make the problems with
vector and built-in arrays go away, since the language will have to
continue supporting them in their present form. It would just offer
another choice: built-in arrays, std::vector or built-in vector.

 > Can I refer
 > you to Allan Odgaard's vector<char> version of a D benchmark at
 > www.digitalmars.com/d/cppstrings.html? Not only is it an interesting
 > aesthetic comparison, but D's version runs 3 times faster.

You write a lot of text, both in these newsgroups and on your web
pages, that describe lots of problems and issues with C++. What's
interesting is how often you're right, while at the same time how
little it really seems to matter. When you say "there are three
different ways to get the length of a string" or "std::vector can't
allocate its data on the stack", you're right. But I can't remember
these issues ever being real problems.

 > BTW, there are two C++ versions - neither written by myself - one uses
 > std::string and the other uses std::vector<char>. Which do you prefer to use
 > for strings, and why?

In general I prefer std::string for strings, because it presents an
API for string operations.

 > What would you do if you used one and had to use a
 > library that used the other, or that used core arrays?

I would have to write wrappers. If that was inconvenient or
unworkable, I'd have to rethink my original choice.

Bob

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply belvis 9/25/2004 10:26:24 AM

"Steven E. Harris" <seh@panix.com> writes:

 > "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
 >
 >  > roughly as
 >  >
 >  > StackAllocator<T> sa( size );
 >  > std::vector<T, StackAllocator<T> > l( sa );
 >
 > Unless sa() is a macro, I don't see how StackAllocator could possibly
 > provide storage from anywhere but the heap, unless it's pulling space
 > from some statically allocated pool.

It's pulling space from an array embedded in sa itself.  sa is on the
stack, so the array is too.  Actually I think there are some small
details that make this not quite right (sa gets copied into the
vector).


    std::vector<T, StackAllocator<T> > l;

is more likely to work, and if you really need _guarantees_, I think
you want this (**):

    storage<nbytes> storage;
    std::vector<T, BoundedAllocator<T> > l(BoundedAllocator<T>(storage));


(**) because the vector is free to dynamically allocate its
      copy of the allocator (yes, stupid, I know).

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/25/2004 10:29:31 AM

Louis Lavery wrote:
> Brooks Moses <bmoses-nospam@cits1.stanford.edu> wrote in message
> news:414E28D9.5A2139AE@cits1.stanford.edu...
> > Louis Lavery wrote:
> >  > So FORTRAN taught you nothing of the perils of pass by non-const
> reference?
> >
> > Why should it?  In FORTRAN -- at least, standard-conforming FORTRAN 77
> > -- it's illegal for a function to attempt to modify an argument that was
> > passed in as an expression rather than a variable.
> 
> That's a bit harsh on the function, how would it know it'd been passed
> an expression rather than a variable?

It wouldn't.  So one needs to keep track of this when calling the
function.  Or else use a compiler which, as an extension to the
language, does the right thing when one passes in an expression in place
of a variable.  (This may also have been fixed in later versions; I am
not yet very familiar with Fortran 95.)
 
>     Had FORTRAN const variables then it could refuse to compile a call to a
> function with a literal passed as an arg unless that arg was declared const.

Yes, exactly.

My real question was -- on relating this back to C++ and the subject of
this thread -- is "const correctness" as a general programming practice
something that is actually necessary to achieve that benefit?  It would
appear to me that all that's needed to obtain this particular benefit
(when passing variables by reference) is to use const appropriately in
the argument declarations, and that maintaining "const correctness" in
the rest of the code is irrelevant to this.

- Brooks


-- 
The "bmoses-nospam" address is valid; no unmunging needed.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Brooks 9/25/2004 6:09:52 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:GQw4d.140866$3l3.119949@attbi_s03...

| I agree it's usable. Things like allocating arrays on the stack are speed
| optimizations, not usability issues. So are static initializers. Can I refer
| you to Allan Odgaard's vector<char> version of a D benchmark at
| www.digitalmars.com/d/cppstrings.html? Not only is it an interesting
| aesthetic comparison, but D's version runs 3 times faster.

yeah, I have been a bit sceptical about that benchmark. One of my concerns was
that it shows
several things at one time.

C++ currently lack a really good way of loading the contents of a file and the
used methods are really slow.
I supose

        input = cast(char[])file.read(args[i]);

is optimzed to only writing the file once? Anyway, I made two simple
optimizations:

1. exchanged map<string,int> with map<boost::sub_range<const string>,int> **
2. loaded the file faster

this gave this result:

D: .     0510 sek
C++:  0.730 sek

which is not quite a factor 3 :-)

There is also other obvious improvements which I haven't done:

1. load the file faster, ie, make sure the vector<char> is only written once
2. use hash_map (I assume D uses hashing internally in int[char[]]?)
3. use a custom allocator with the map

Given these I bet the C++ version would be faster. It's also worth noticing
most of these optimizations can be done by simply changing a typedef, the
basic very clear
algorithm by Allan stays the same.

| BTW, there are two C++ versions - neither written by myself - one uses
| std::string and the other uses std::vector<char>. Which do you prefer to use
| for strings, and why?

there are probably good reasons for changing the use of a string type. If you
don't need
to write to strings, they can be immutable, and vice versa. If you don't need
copy
a string around, you can make a string that acts as a substring to another.
Having one string
that is all that is conceptually wierd.

| What would you do if you used one and had to use a
| library that used the other, or that used core arrays?

I would chose to use boost::sub_range<const string> for this purpose.
There is no reason to copy the whole string around.

Anyway, I assume this is how char[] slicing works in D (ie, no copying)?

br

Thorsten

** boost::sub_range<> is part of boost.range which will be in the next version
of boost



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Thorsten 9/25/2004 6:33:38 PM

belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0409241148.724624b0@posting.google.com>...
 >
 > You write a lot of text, both in these newsgroups and on your web
 > pages, that describe lots of problems and issues with C++. What's
 > interesting is how often you're right, while at the same time how
 > little it really seems to matter. When you say "there are three
 > different ways to get the length of a string" or "std::vector can't
 > allocate its data on the stack", you're right. But I can't remember
 > these issues ever being real problems.

They aren't problems because we tend to code around them.  This may
mean a slightly more specialized API or a bit more code, but there are
no major obstacles.  And iterators are tremendously useful in
eliminating the differences between container types.  I don't see this
as a fault of C++ so much as a "nice to have" feature in D.  Sure it's
not necessary, but few things are.  But considering that D is a new
language, why not build in this level of consistency?  It certainly
can't hurt.


Sean

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply complexmath 9/26/2004 9:41:24 AM

Bob Bell wrote:

[...]
 > You write a lot of text, both in these newsgroups and on your web
 > pages, that describe lots of problems and issues with C++. What's
 > interesting is how often you're right, while at the same time how
 > little it really seems to matter. When you say "there are three
 > different ways to get the length of a string" or "std::vector can't
 > allocate its data on the stack", you're right. But I can't remember
 > these issues ever being real problems.

Well, I certainly did have problems with this kind of thing. I did have
situations where I wanted to allocate arrays on the stack, I found it
inconvenient that boost::array needed the array size specified
explicitly rather than deriving it from the initializer, and so on.
You're probably going to say that they haven't been "real problems".
Well, yes, I worked around them, like everybody does, so it probably
wasn't that bad. Nobody is saying that C++ is unusable because of those
niggles. But what is wrong with calling a wart a wart?

-- 
Cheers
Stefan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stefan 9/26/2004 9:42:22 AM

Brooks Moses <bmoses-nospam@cits1.stanford.edu> wrote in message news:<4155189D.8BB7E473@cits1.stanford.edu>...
 > Louis Lavery wrote:
 > > Brooks Moses <bmoses-nospam@cits1.stanford.edu> wrote in message
 > > news:414E28D9.5A2139AE@cits1.stanford.edu...
 > > > Louis Lavery wrote:
 > > >  > So FORTRAN taught you nothing of the perils of pass by non-const
 >  reference?
 > > >
 > > > Why should it?  In FORTRAN -- at least, standard-conforming FORTRAN 77
 > > > -- it's illegal for a function to attempt to modify an argument that was
 > > > passed in as an expression rather than a variable.
 > >
 > > That's a bit harsh on the function, how would it know it'd been passed
 > > an expression rather than a variable?
 >
 > It wouldn't.  So one needs to keep track of this when calling the
 > function.  Or else use a compiler which, as an extension to the
 > language, does the right thing when one passes in an expression in place
 > of a variable.  (This may also have been fixed in later versions; I am
 > not yet very familiar with Fortran 95.)

In Fortran 90 and later versions one can declare a function or
subroutine argument to have "intent(in)", meaning that it must not be
changed within that procedure. Trying to modify an intent(in) argument
within that procedure will be caught by the compiler at run time.
Within the procedure, the argument can appear as the argument of
another procedure where it is not intent(in), but if that other
procedure tries to modify the argument the program will crash at run
time. At least that's my experience with Compaq Visual Fortran and
Lahey/Fujitsu Fortran 95.

Btw the Fortran 2003 standard was recently ratified. It supports OOP
with single inheritance and interoperability with C.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply beliavsky 9/26/2004 9:57:37 AM

David Abrahams <dave@boost-consulting.com> writes:

> It's pulling space from an array embedded in sa itself.  sa is on
> the stack, so the array is too.

But how would sa know how large to make this array? If it has to wait
until it can inspect the "size" constructor argument, then it's too
late for static allocation and that array must come from the
heap. Alternately, sa could just hold some "fairly large" array as a
member variable or static variable and throw an exception if the
"size" argument is greater than "fairly large."

The latter approach reminds me of claims that begin with, "Hey, I
found a way to avoid using the heap! See, I just create this huge
array here, and start using that, and I'm working on some way of
quickly finding the next available chunk, and I haven't figured out
yet how to reuse any of the memory, but that shouldn't be too hard,
right?"

-- 
Steven E. Harris

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Steven 9/27/2004 8:51:14 PM

Steven E. Harris wrote:
 > David Abrahams <dave@boost-consulting.com> writes:
 >
 >
 >>It's pulling space from an array embedded in sa itself.  sa is on
 >>the stack, so the array is too.
 >
 >
 > But how would sa know how large to make this array? If it has to wait
 > until it can inspect the "size" constructor argument, then it's too
 > late for static allocation and that array must come from the
 > heap. Alternately, sa could just hold some "fairly large" array as a
 > member variable or static variable and throw an exception if the
 > "size" argument is greater than "fairly large."

There is another way to do it, at least for arrays.
I assume the caller knows exactly how much memory is required (resizing
a buffer on the stack seems to be fairly difficult ;) ), therefore it
would be fine to allocate a "fairly large array" and throw if no memory
is left.

Some implementations offer routines to dynamically allocate memory from
the stack (e.g. _alloca in VC++), so the following should work:

typedef vector<T, BoundedAllocator<T> > Vec;
int maxsize = rand();
Storage s(maxsize, _alloca(maxsize * sizeof(Vec::value_type)));
Vec v(BoundedAllocator<T>(s));
v.reserve(maxsize);

Of course, this requires much knowledge about the way (when, how much +
how often) a vector internally allocates memory.
It is possible, however, to provide a specialized vector implementation
that deals with these problems.

-- 
Regards,
Tobias

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply ISO 9/28/2004 10:15:27 AM

"Steven E. Harris" <seh@panix.com> writes:

 > David Abrahams <dave@boost-consulting.com> writes:
 >
 >> It's pulling space from an array embedded in sa itself.  sa is on
 >> the stack, so the array is too.
 >
 > But how would sa know how large to make this array? If it has to wait
 > until it can inspect the "size" constructor argument, then it's too
 > late for static allocation

The size of the array might be a template parameter to the allocator.

 > and that array must come from the heap. Alternately, sa could just
 > hold some "fairly large" array as a member variable or static
 > variable and throw an exception if the "size" argument is greater
 > than "fairly large."

Right.  There's a limit on the amount of memory available to the
allocator, just like one on the heap.  Or it could use a hybrid
approach and fall back to the heap when neccessary.

 > The latter approach reminds me of claims that begin with, "Hey, I
 > found a way to avoid using the heap! See, I just create this huge
 > array here, and start using that, and I'm working on some way of
 > quickly finding the next available chunk, and I haven't figured out
 > yet how to reuse any of the memory, but that shouldn't be too hard,
 > right?"

I didn't have anything of the sort in mind.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/28/2004 10:23:41 AM

From: "Glen Low" <glenlow@pixelglow.com>

| > | o    No array literals.
| >
| > I believe a library solution is superior here. For example, in
boost.assign
| >
| > list_of( 1 )( 2 )( 3 )( 4 )( 5 )
|
| Strikes me as being rather obtuse.

what's abtuse about it?

| C99 compatibility may eventually
| rescue C++ here, since C99 has array initializers similar to the way
| C# and Java do it e.g.
|
| vec <int> v ((int []) {1, 2, 3, 4})

yeah, but not for user defined types.

As I said in another mail, this is likely to be much less powerful than a
library solution:

1. how do you call functions that generate part of the list?
2. how do you call constructors of objects?
3. how do you make an array of references?

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 9/29/2004 9:39:13 AM

David Abrahams <dave@boost-consulting.com> writes:

 > The size of the array might be a template parameter to the
 > allocator.

Yes, that was the aspect I was looking for when I first posted in this
thread.� Note that StackAllocator was shown as having only one
(non-defaulted) template parameter: the element or value type. Now,
there could be an elided "size" parameter that again defaults to
something "fairly large," but we couldn't see it in the posted code.

My point in disputing these posted sketches is that there's no way --
short of the non-portable _alloca() example -- to get an array with
capacity of /exactly/ "size" elements on the stack, unless you happen
to have guessed properly when statically sizing the array
beforehand. You may get storage with /enough/ capacity, by allocating
too much ahead of time, but that's more about careful up-front
analysis and luck.

[...]

 > Or it could use a hybrid approach and fall back to the heap when
 > neccessary.

That's kind of like the "small string" optimization that Andrei was
writing about in the last couple of years, right?


Footnotes:
� http://groups.google.com/groups?threadm=jk4k6ult35g.fsf%40W003275.na.alarismed.com

-- 
Steven E. Harris

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Steven 9/29/2004 9:44:22 AM

Tobias G�ntner <fatbull@users.sourceforge.net> wrote in news:cjad79$2er
$03$1@news.t-online.com:
>
> Some implementations offer routines to dynamically allocate memory from
> the stack (e.g. _alloca in VC++), so the following should work:
>

I can't understand why do you guys insist on allocating arrays on stack? 
Stack overflow is one of the worst scenarios of undefined behaviour, and 
it seems it cannot be reliably detected/recovered from even on Win32 (as 
opposed to access violations or divison by zero, for example). The 
default stack size is 1MB for MS linker, which is less than a common 
birthday picture these days ;-)

I'm sure that those who know what they are doing are able to calculate 
the safety limits on stack allocation and/or enlarge the stack space of 
the process as needed, but I'm afraid for the rest of us the suggestion 
to use stack-allocated arrays is a way to disaster instead;-(  To my 
mind, stack-allocated arrays should remain in the domain of critical 
embedded real-time systems which cannot afford the unreliability of 
dynamic allocation (and note that in this case the idea that the size of 
the array might be determined only at runtime is most probably not 
acceptable anyway).

Regards
Paavo


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Paavo 9/29/2004 5:51:48 PM

"Steven E. Harris" <seh@panix.com> writes:

 > That's kind of like the "small string" optimization that Andrei was
 > writing about in the last couple of years, right?

I don't know what Andrei's been writing about it, but yeah, that's
the small string optimization all over again.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 9/30/2004 10:37:09 AM

In article <41599f49$0$55828$14726298@news.sunsite.dk>, Thorsten Ottosen
<nesotto@cs.auc.dk> writes
> | C99 compatibility may eventually
> | rescue C++ here, since C99 has array initializers similar to the way
> | C# and Java do it e.g.
> |
> | vec <int> v ((int []) {1, 2, 3, 4})
>
> yeah, but not for user defined types.

Why not?

-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: 
http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Francis 9/30/2004 2:02:38 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:4154bd3b$0$46881$14726298@news.sunsite.dk...
>
> "Walter" <walter@digitalmars.nospamm.com> wrote in message
> news:GQw4d.140866$3l3.119949@attbi_s03...
>
> | I agree it's usable. Things like allocating arrays on the stack are
speed
> | optimizations, not usability issues. So are static initializers. Can I
refer
> | you to Allan Odgaard's vector<char> version of a D benchmark at
> | www.digitalmars.com/d/cppstrings.html? Not only is it an interesting
> | aesthetic comparison, but D's version runs 3 times faster.
>
> yeah, I have been a bit sceptical about that benchmark. One of my concerns
was
> that it shows several things at one time.

You can try it for yourself if you're skeptical.


> C++ currently lack a really good way of loading the contents of a file and
the
> used methods are really slow.
> I supose
>
>         input = cast(char[])file.read(args[i]);
>
> is optimzed to only writing the file once?

It only reads (sic) the file once, that is correct.

> Anyway, I made two simple
> optimizations:
>
> 1. exchanged map<string,int> with map<boost::sub_range<const string>,int>
**
> 2. loaded the file faster
>
> this gave this result:
>
> D: .     0510 sek
> C++:  0.730 sek
>
> which is not quite a factor 3 :-)

Boost is not part of the standard library. Boost is a very non-trivial piece
of code.


> There is also other obvious improvements which I haven't done:
>
> 1. load the file faster, ie, make sure the vector<char> is only written
once
> 2. use hash_map (I assume D uses hashing internally in int[char[]]?)
> 3. use a custom allocator with the map
>
> Given these I bet the C++ version would be faster.

I could spend time optimizing the D version, too, but the point is I made no
effort to - it's just a straightforward rendering. You have to do a bunch of
careful work, and be very knowledgeable about C++, to compete (and it's
still slower).


> It's also worth noticing
> most of these optimizations can be done by simply changing a typedef, the
> basic very clear
> algorithm by Allan stays the same.

I don't agree that using boost is so simple - and it isn't part of standard
C++. How many C++ programmers would even know about
map<boost::sub_range<const string>,int> and when it's appropriate to use it?
Compare with the D version, which is writable with just basic knowledge of
D.


> | BTW, there are two C++ versions - neither written by myself - one uses
> | std::string and the other uses std::vector<char>. Which do you prefer to
use
> | for strings, and why?
>
> there are probably good reasons for changing the use of a string type. If
you
> don't need
> to write to strings, they can be immutable, and vice versa. If you don't
need
> copy
> a string around, you can make a string that acts as a substring to
another.
> Having one string that is all that is conceptually wierd.

If you can do all that with one string, there's a lot less needed to learn
in order to write effective programs.


> | What would you do if you used one and had to use a
> | library that used the other, or that used core arrays?
>
> I would chose to use boost::sub_range<const string> for this purpose.
> There is no reason to copy the whole string around.
>
> Anyway, I assume this is how char[] slicing works in D (ie, no copying)?

That's right, no copying.

> ** boost::sub_range<> is part of boost.range which will be in the next
version
> of boost

So it isn't even commonly available yet <g>.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 10/1/2004 4:04:39 PM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:B67$gFUZMpWBFwc7@robinton.demon.co.uk...
| In article <41599f49$0$55828$14726298@news.sunsite.dk>, Thorsten Ottosen
| <nesotto@cs.auc.dk> writes
| > | C99 compatibility may eventually
| > | rescue C++ here, since C99 has array initializers similar to the way
| > | C# and Java do it e.g.
| > |
| > | vec <int> v ((int []) {1, 2, 3, 4})
| >
| > yeah, but not for user defined types.
|
| Why not?

Forgive me for this imprecise comment. My claim is that it won't be any
"rescue" for
user defined types. For example, you would need to name the type for each
element as in

vec<Foo> v( (Foo[]){ Foo(1), Foo("foo") ), Foo( "bar", 2 ) );

If Foo is a long name, you'd make a typedef, but my point is that all that
extra syntax is tedious and distort the overview. Compare it with

vec<Foo> v = list_of<Foo>( 1 )( "foo" )( "bar", 2 );

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/1/2004 4:05:01 PM

"Bob Bell" <belvis@pacbell.net> wrote in message
news:c87c1cfb.0409241148.724624b0@posting.google.com...
> "Walter" <walter@digitalmars.nospamm.com> wrote in message
news:<GQw4d.140866$3l3.119949@attbi_s03>...
>  > "Bob Bell" <belvis@pacbell.net> wrote in message
>  > news:c87c1cfb.0409221106.4087a5b4@posting.google.com...
>  >  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
>  >  news:<ZDY3d.86006$D%.26675@attbi_s51>...
>  >  >  > "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
>  >  >  > news:414feead$0$39258$14726298@news.sunsite.dk...
>  >  >  > > "Walter" <walter@digitalmars.nospamm.com> wrote in message
>  >  >  > > news:%za3d.9557$wV.1922@attbi_s54...
>  >  >  > > | Even setting aside for the moment the limitations of
>  >  >  > > | std::vector resulting from it not being part of the core
language?
>  >  >  > >
>  >  >  > > What limitations are you referring to? So far it has been able
to
>  >  handle
>  >  all
>  >  >  > > my problems.
>  >  >  >
>  >  >  > o    Can't have vector data allocated in static data.
>  >  >  > o    Can't have vector data allocated on the stack.
>  >  >  > o    Can't have a static initializer for vector data.
>  >  >  > o    No array literals.
>  >  >  > o    No array slicing syntax.
>  >  >  > o    No interoperability with string literals or std::string.
>  >  >  > o    Poor integration with core arrays, such as:
>  >  >  >         char a[], b[];
>  >  >  >         ... (a + b) ...;    // what does that mean?
>  >  >
>  >  > Despite these "limitations" (I cannot remember ever wanting/needing
>  >  > std::vector to do any of these things), std::vector manages to
somehow
>  >  > be usable. None of these things justify core language changes, IMHO.
>  >
>  > I agree it's usable. Things like allocating arrays on the stack are
speed
>  > optimizations, not usability issues. So are static initializers.
>
> Yet you were offering this list as reasons why vector should be part
> of the core language. IMHO, optimization isn't a strong enough reason
> for making vector part of the language, especially when you consider
> that C++ gives me very good tools for optimization already. For
> example, and as Thorsten pointed out, boost::array provides a
> best-of-both-worlds solution that gives me a vector-like interface
> with the peformance characteristics of a built-in array.

I've seen Thorsten's solution, and while I admire it's technical virtuosity,
it just doesn't look good on paper. There are good, practical, reasons for a
language needing to look good when written out. Second, Boost is not part of
the standard library. Third, Boost's arrays don't resolve all the issues I
mentioned, only a couple of them. Fourth, if C++'s optimizing
metaprogramming capabilities are very good, why does the metaprogramming
strings example I referred to run 3 times slower?


> Further,
> making vector part of the core language won't make the problems with
> vector and built-in arrays go away, since the language will have to
> continue supporting them in their present form. It would just offer
> another choice: built-in arrays, std::vector or built-in vector.

One big rationale for D is to fix core features, and dispense with legacy
compatibility where necessary to achieve the desired improvements.


>  > Can I refer
>  > you to Allan Odgaard's vector<char> version of a D benchmark at
>  > www.digitalmars.com/d/cppstrings.html? Not only is it an interesting
>  > aesthetic comparison, but D's version runs 3 times faster.
>
> You write a lot of text, both in these newsgroups and on your web
> pages, that describe lots of problems and issues with C++. What's
> interesting is how often you're right, while at the same time how
> little it really seems to matter. When you say "there are three
> different ways to get the length of a string" or "std::vector can't
> allocate its data on the stack", you're right. But I can't remember
> these issues ever being real problems.

If C++ is a street paved with cobblestones, D is paved with asphalt. Each
little cobblestone doesn't make much of any difference, but the aggregate is
a bumpy ride. Few of D's features are compelling *by themselves*, but
putting them all together is where the smooth ride comes from. Perhaps, and
I know this can be true for a lot of people, one gets so used to driving
around the potholes in C++ that one gets comfortable doing it and no longer
notices them. But drive on a smooth street for a while, and *then* go back
and work with C++, and the improvement D offers will be much more apparent.

Any way you slice it, having 3 different *standard* string types that mostly
overlap but don't interoperate (or only marginally do so) is a big pothole,
even if you got used to dodging it.



>  > BTW, there are two C++ versions - neither written by myself - one uses
>  > std::string and the other uses std::vector<char>. Which do you prefer
to use
>  > for strings, and why?
> In general I prefer std::string for strings, because it presents an
> API for string operations.
>  > What would you do if you used one and had to use a
>  > library that used the other, or that used core arrays?
> I would have to write wrappers. If that was inconvenient or
> unworkable, I'd have to rethink my original choice.

'nuff said <g>.

-Walter
www.digitalmars.com The D Programming Language


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 10/1/2004 4:05:22 PM

Paavo Helde wrote:
> Tobias G�ntner <fatbull@users.sourceforge.net> wrote in news:cjad79$2er
> $03$1@news.t-online.com:
> 
>>Some implementations offer routines to dynamically allocate memory from
>>the stack (e.g. _alloca in VC++), so the following should work:
> 
> I can't understand why do you guys insist on allocating arrays on stack? 
> Stack overflow is one of the worst scenarios of undefined behaviour, and 
> it seems it cannot be reliably detected/recovered from even on Win32 (as 
> opposed to access violations or divison by zero, for example). The 
> default stack size is 1MB for MS linker, which is less than a common 
> birthday picture these days ;-)
> 
> I'm sure that those who know what they are doing are able to calculate 
> the safety limits on stack allocation and/or enlarge the stack space of 
> the process as needed, but I'm afraid for the rest of us the suggestion 
> to use stack-allocated arrays is a way to disaster instead;-(  To my 
> mind, stack-allocated arrays should remain in the domain of critical 
> embedded real-time systems which cannot afford the unreliability of 
> dynamic allocation (and note that in this case the idea that the size of 
> the array might be determined only at runtime is most probably not 
> acceptable anyway).

Allocating very large arrays on the stack is indeed a bad idea. The more 
interesting cases are the smaller arrays, and there the performance 
difference may very well matter.

On top of that, a virtual memory operating system is typically able to 
grow the stack space automatically with no explicit action required from 
the programmer. As a consequence, you're not more likely to run out of 
stack space than heap space.

-- 
Cheers
Stefan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stefan 10/1/2004 4:14:24 PM

"Stefan Heinzmann" <stefan_heinzmann@yahoo.com> wrote in message
news:ciuhqf$iig$05$1@news.t-online.com...
> At some point, however, you are better off creating a new language
> instead. This is what Walter did with D. I'm glad it exists. It is a
> good playground for ideas that would be too radical for C++. Maybe one
> day some of its features get backported to C++, similar to what happened
> to C.

There are quite a few of D's core languages that could be retrofitted into
C++, even much of the core array stuff, without damaging backwards
compatibility. So the obvious question is why didn't I just put them into
the Digital Mars C++ compiler rather than create a whole new language?

The answer is, I did. DMC++ has, as core language extensions, binary
constants, a with statement, design by contract, and complex numbers. Who
uses them? Nobody. Why don't they use them? Because they are non-standard,
and people want to write only standard conforming code. Why not put them in
the C++ standard? Because I don't want to wait 5 or 10 years, and that's no
guarantee either. After all, C++ still hasn't even added in the C99
features. C++ programmers are very conservative. Adding C++ language
extensions is a waste of time unless they are already or de facto approved
by the standards committee.

On the other hand, those same conservative C++ programmers are very open to
new ways of doing things if the language itself is new. The with statement,
design by contract, etc., have been embraced as important, even crucial,
capabilities of D and are well understood and used. Even a brief perusal of
the D newsgroup at news.digitalmars.com will show that the D community is
enthusiastic about pushing the language capabilities forward.

-Walter
www.digitalmars.com/d/ the D Programming Language


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 10/1/2004 4:15:01 PM

I wanted to expand on a couple of points.

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:4154bd3b$0$46881$14726298@news.sunsite.dk...
> this gave this result:
>
> D: .     0510 sek
> C++:  0.730 sek
>
> which is not quite a factor 3 :-)

You didn't say which compiler you used. The factor of 3 comes from using DMD
and DMC++. Those two compilers share the optimizer and back end code
generator - so the result is comparing the *languages*, not the register
allocator or loop unroller.


> there are probably good reasons for changing the use of a string type. If
you
> don't need to write to strings, they can be immutable, and vice versa. If
you don't need
> copy a string around, you can make a string that acts as a substring to
another.
> Having one string that is all that is conceptually wierd.

It's weird only if you're very used to the need in C++ to expend so much
effort dealing with memory resource management. Much attention has to be
paid to who 'owns' a piece of memory, and the routine way this problem is
dealt with in C++ is by making copies of the data. With automatic memory
management, it doesn't matter who owns the data, so instances can be
arbitrarilly shared, and even parts (slices) of instances can be shared. One
can even mix and match gc allocated, stack, static, and malloc allocated
data into the same data structure. Only when one needs to modify the data
does a copy need to be made (the COW, or Copy On Write technique). For
instance, the routine std.string.toupper(char[] s) only has to make a copy
of its input string if it actually converted any characters in it to upper
case. If the string s is already upper case, that same string instance is
returned.

In your optimization, you used map<boost::sub_range<const string>,int> to
avoid copying the strings. That's fine up until perhaps you want to insert a
string into the map that is a regular string, not a boost::subrange. (The
optimization you mentioned of using a custom allocator makes the C++ version
even more specific and less flexible.) Even worse, you have to be careful
not to delete the string it is a subrange of before the map<> is deleted.
These gotchas and subtle problems are the bane of C++ explicit memory
management.

There are no such worries with the D strings and associative arrays.

Also, just for fun, try opening a file that doesn't exist with the D
version, and then try it with the C++ one. The D one gives you a nice
message, even though there isn't any error handling code written. The C++
one, well, er, doesn't <g>. This illustrates another nice aspect of the D
design, the default handling of errors.

-Walter
www.digitalmars.com/d/ the D Programming Language


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 10/1/2004 4:15:39 PM

In article <415c3390$0$55824$14726298@news.sunsite.dk>, Thorsten Ottosen 
<nesotto@cs.auc.dk> writes
>Forgive me for this imprecise comment. My claim is that it won't be any
>"rescue" for
>user defined types. For example, you would need to name the type for each
>element as in
>
>vec<Foo> v( (Foo[]){ Foo(1), Foo("foo") ), Foo( "bar", 2 ) );
>
>If Foo is a long name, you'd make a typedef, but my point is that all that
>extra syntax is tedious and distort the overview. Compare it with
>
>vec<Foo> v = list_of<Foo>( 1 )( "foo" )( "bar", 2 );

Not necessary, I have a proposal (uniform initialisation syntax) that 
would support:

vec<foo> v({{1}, {"foo"}, {"bar", 1}});
  Now it is early days but it certainly does support the idea of a 
temporary array and arrays in which the different elements are 
initialised by calling different ctors.


-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Francis 10/1/2004 5:28:27 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:AUY6d.182005$3l3.11663@attbi_s03...
| I wanted to expand on a couple of points.
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:4154bd3b$0$46881$14726298@news.sunsite.dk...
| > this gave this result:
| >
| > D: .     0510 sek
| > C++:  0.730 sek
| >
| > which is not quite a factor 3 :-)
|
| You didn't say which compiler you used.

I used vc7.1

| The factor of 3 comes from using DMD
| and DMC++. Those two compilers share the optimizer and back end code
| generator - so the result is comparing the *languages*, not the register
| allocator or loop unroller.

not understood. surely it must be a comparison of two implementations.
any chance you spend more time on the D builtin hash_map/string than
the C++'s libraries map/string?

|
| > there are probably good reasons for changing the use of a string type. If
| you
| > don't need to write to strings, they can be immutable, and vice versa. If
| you don't need
| > copy a string around, you can make a string that acts as a substring to
| another.
| > Having one string that is all that is conceptually wierd.
|
| It's weird only if you're very used to the need in C++ to expend so much
| effort dealing with memory resource management. Much attention has to be
| paid to who 'owns' a piece of memory, and the routine way this problem is
| dealt with in C++ is by making copies of the data. With automatic memory
| management, it doesn't matter who owns the data, so instances can be
| arbitrarilly shared, and even parts (slices) of instances can be shared.

This just creates a new dilemma: now you can't be sure your class is properly
encapsulated
since stuff is referenced (with write access too) all over the place.

| One
| can even mix and match gc allocated, stack, static, and malloc allocated
| data into the same data structure. Only when one needs to modify the data
| does a copy need to be made (the COW, or Copy On Write technique). For
| instance, the routine std.string.toupper(char[] s) only has to make a copy
| of its input string if it actually converted any characters in it to upper
| case. If the string s is already upper case, that same string instance is
| returned.

ok. I'm still learning D from your comments, but you say strings emply copy on
write? Can user defined types do that too?

| In your optimization, you used map<boost::sub_range<const string>,int> to
| avoid copying the strings. That's fine up until perhaps you want to insert a
| string into the map that is a regular string, not a boost::subrange. (The
| optimization you mentioned of using a custom allocator makes the C++ version
| even more specific and less flexible.)

map<sub_range<const string>,int> m1 = ..;
map<string,int> m( m1.begin(), m1.end() );

should work fine if the sub_range provided a conversion operator().

| Even worse, you have to be careful
| not to delete the string it is a subrange of before the map<> is deleted.

you'll know pretty early if that's hapening.

| Also, just for fun, try opening a file that doesn't exist with the D
| version, and then try it with the C++ one. The D one gives you a nice
| message, even though there isn't any error handling code written. The C++
| one, well, er, doesn't <g>. This illustrates another nice aspect of the D
| design, the default handling of errors.

this is a QoI issue.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/2/2004 1:21:27 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:s7X6d.181849$3l3.134844@attbi_s03...

| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
|
| > Anyway, I made two simple
| > optimizations:
| >
| > 1. exchanged map<string,int> with map<boost::sub_range<const string>,int>
| **
| > 2. loaded the file faster
| >
| > this gave this result:
| >
| > D: .     0510 sek
| > C++:  0.730 sek
| >
| > which is not quite a factor 3 :-)
|
| Boost is not part of the standard library. Boost is a very non-trivial piece
| of code.

I'm not sure what you're trying to say, but the sub_range class is pretty
simple. I don't how this relates to
boost at all.

| > There is also other obvious improvements which I haven't done:
| >
| > 1. load the file faster, ie, make sure the vector<char> is only written
| once
| > 2. use hash_map (I assume D uses hashing internally in int[char[]]?)
| > 3. use a custom allocator with the map
| >
| > Given these I bet the C++ version would be faster.
|
| I could spend time optimizing the D version, too, but the point is I made no
| effort to - it's just a straightforward rendering.

that is a good point: you could optimize it; your users *cannot* because it's
not provided as a library but hardcoded into
the language. For quite some time we (at least large parts of the C++
community) have known that libraries are more flexible than core-language
extensions;
this is why there is a focus on extending the language with support for better
library building.

| You have to do a bunch of
| careful work, and be very knowledgeable about C++, to compete (and it's
| still slower).

This is true, but a more mature C++ standard library will probably make this
an obvious approach.

| > It's also worth noticing
| > most of these optimizations can be done by simply changing a typedef, the
| > basic very clear
| > algorithm by Allan stays the same.
|
| I don't agree that using boost is so simple - and it isn't part of standard
| C++. How many C++ programmers would even know about
| map<boost::sub_range<const string>,int>

just a note: this thread started with a discussion about const-correctness and
here we again
see an important use of const to express and document the read-only behavior
of
the code.

| > ** boost::sub_range<> is part of boost.range which will be in the next
| version
| > of boost
|
| So it isn't even commonly available yet <g>.

but anyway, I hope you will distribute boost together with digital mars.

br

Thorsten




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/2/2004 1:22:07 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:%XV6d.181673$3l3.43654@attbi_s03...
|
| "Bob Bell" <belvis@pacbell.net> wrote in message

| > Yet you were offering this list as reasons why vector should be part
| > of the core language. IMHO, optimization isn't a strong enough reason
| > for making vector part of the language, especially when you consider
| > that C++ gives me very good tools for optimization already. For
| > example, and as Thorsten pointed out, boost::array provides a
| > best-of-both-worlds solution that gives me a vector-like interface
| > with the peformance characteristics of a built-in array.
|
| I've seen Thorsten's solution, and while I admire it's technical virtuosity,
| it just doesn't look good on paper.

just to amke it clear, I never invented boost::array..

| There are good, practical, reasons for a
| language needing to look good when written out. Second, Boost is not part of
| the standard library. Third, Boost's arrays don't resolve all the issues I
| mentioned, only a couple of them. Fourth, if C++'s optimizing
| metaprogramming capabilities are very good, why does the metaprogramming
| strings example I referred to run 3 times slower?

because the example show more than strings (file loading, for example)
and because it fits D's programming model better.

C++ is inherently structured to support value-based programming; D is the
opposite
with reference semantics by default.That makes comparisons hard.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/2/2004 1:23:11 AM

"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:i7MnQQXCaYXBFwBV@robinton.demon.co.uk...
| In article <415c3390$0$55824$14726298@news.sunsite.dk>, Thorsten Ottosen
| <nesotto@cs.auc.dk> writes
| >Forgive me for this imprecise comment. My claim is that it won't be any
| >"rescue" for
| >user defined types. For example, you would need to name the type for each
| >element as in
| >
| >vec<Foo> v( (Foo[]){ Foo(1), Foo("foo") ), Foo( "bar", 2 ) );
| >
| >If Foo is a long name, you'd make a typedef, but my point is that all that
| >extra syntax is tedious and distort the overview. Compare it with
| >
| >vec<Foo> v = list_of<Foo>( 1 )( "foo" )( "bar", 2 );
|
| Not necessary, I have a proposal (uniform initialisation syntax) that
| would support:
|
| vec<foo> v({{1}, {"foo"}, {"bar", 1}});
|   Now it is early days but it certainly does support the idea of a
| temporary array and arrays in which the different elements are
| initialised by calling different ctors.

It looks pretty ok too. In my library, () (that is, empty paranthesis)  is
used to make a default constructed element.
How would that look in your proposal?

How will you do something like

Foo a,b,c,d;
....
Foo& f = *max_element( list_of<Foo&,4>( a )( b )( c )( d ) );

?

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/2/2004 1:24:01 AM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:415dba15$0$55825$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:AUY6d.182005$3l3.11663@attbi_s03...
 > | The factor of 3 comes from using DMD
 > | and DMC++. Those two compilers share the optimizer and back end code
 > | generator - so the result is comparing the *languages*, not the register
 > | allocator or loop unroller.
 > not understood. surely it must be a comparison of two implementations.

You can get very different speed results on a benchmark by using different
C++ compilers that have different optimizers and code generators. Therefore,
to compare *languages*, rather than optimizer/codegenerators, one needs to
factor out the effect of different optimizer/codegenerators by using the
same optimizer/codegenerator for each language. That is easy to do for D vs
C++ because DMD and DMC++ share the same optimizer/codegenerator. gdc and
g++ can give good results for the same reason.

Using DMD and VC gives no useful information comparing languages, since you
have two unknowns and only one equation.

 > any chance you spend more time on the D builtin hash_map/string than
 > the C++'s libraries map/string?

You can review D's hashing source code in /dmd/src/phobos/internal/aaA.d and
/dmd/src/phobos/std/typeinfo/ti_Ag.d (in the D zip distribution). I think
you'll find it's straightforward and not supertuned.

 > | > there are probably good reasons for changing the use of a string type.
If
 > | you
 > | > don't need to write to strings, they can be immutable, and vice versa.
If
 > | you don't need
 > | > copy a string around, you can make a string that acts as a substring
to
 > | another.
 > | > Having one string that is all that is conceptually wierd.
 > |
 > | It's weird only if you're very used to the need in C++ to expend so much
 > | effort dealing with memory resource management. Much attention has to be
 > | paid to who 'owns' a piece of memory, and the routine way this problem
is
 > | dealt with in C++ is by making copies of the data. With automatic memory
 > | management, it doesn't matter who owns the data, so instances can be
 > | arbitrarilly shared, and even parts (slices) of instances can be shared.
 >
 > This just creates a new dilemma: now you can't be sure your class is
properly
 > encapsulated
 > since stuff is referenced (with write access too) all over the place.

Maybe it is a dilemma, but if it is, it hasn't been a problem for me and
nobody has brought it up in the D forums (not that anyone is shy about
bringing up issues <g>).


 > | One
 > | can even mix and match gc allocated, stack, static, and malloc allocated
 > | data into the same data structure. Only when one needs to modify the
data
 > | does a copy need to be made (the COW, or Copy On Write technique). For
 > | instance, the routine std.string.toupper(char[] s) only has to make a
copy
 > | of its input string if it actually converted any characters in it to
upper
 > | case. If the string s is already upper case, that same string instance
is
 > | returned.
 >
 > ok. I'm still learning D from your comments, but you say strings emply
copy on
 > write?

Yes, though that is a programming technique, not something enforced by the
compiler. Enforcing it in the language itself, like ECMAscript, would have
unacceptable detrimental effects on performance.

 > Can user defined types do that too?

Sure. After all, it's just a programming technique.

 > | In your optimization, you used map<boost::sub_range<const string>,int>
to
 > | avoid copying the strings. That's fine up until perhaps you want to
insert a
 > | string into the map that is a regular string, not a boost::subrange.
(The
 > | optimization you mentioned of using a custom allocator makes the C++
version
 > | even more specific and less flexible.)
 >
 > map<sub_range<const string>,int> m1 = ..;
 > map<string,int> m( m1.begin(), m1.end() );
 >
 > should work fine if the sub_range provided a conversion operator().

It's not as simple as providing a conversion operator. The issue is who owns
the memory. Converting a string to a subrange implies that one must still
keep track of the original string separately, and remember to only delete it
*after* the map gets deleted. This is not a trivial issue.


 > | Even worse, you have to be careful
 > | not to delete the string it is a subrange of before the map<> is
deleted.
 >
 > you'll know pretty early if that's hapening.

Not necessarilly. Deleting an object in one spot and continuing to refer to
it elsewhere is the dangling pointer problem, and can be very, very hard to
track down.


 > | Also, just for fun, try opening a file that doesn't exist with the D
 > | version, and then try it with the C++ one. The D one gives you a nice
 > | message, even though there isn't any error handling code written. The
C++
 > | one, well, er, doesn't <g>. This illustrates another nice aspect of the
D
 > | design, the default handling of errors.
 >
 > this is a QoI issue.

I disagree. C++'s frequent choice to use error return values and no common
error exception handling mechanism is a design choice that an implementation
must match. (Remember, the C++ version of the benchmark is using *standard*
library functionality.) D has a standardized method of handling errors via
exceptions by design.

-Walter
www.digitalmars.com/d/ the D Programming Language


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 10/2/2004 10:00:49 AM

Thorsten Ottosen wrote:
 >
 > This just creates a new dilemma: now you can't be sure your class is properly
 > encapsulated
 > since stuff is referenced (with write access too) all over the place.

Not with COW strings.  And even if this were the case it's quite simple
to return a copy of the actual value as would normally be done in C++.

 > ok. I'm still learning D from your comments, but you say strings emply copy on
 > write? Can user defined types do that too?

As far as I know, they cannot.  At times I've thought this would be a
nice way to implement const-like classes in D, but I have a feeling the
result would look a lot like how const classes are implemented in C++
(ie. flagging member functions as mutating and non-mutating).

 > map<sub_range<const string>,int> m1 = ..;
 > map<string,int> m( m1.begin(), m1.end() );
 >
 > should work fine if the sub_range provided a conversion operator().

True enough, though I'm not sure it's ideal that the simple answer to
this situation is to create a new map with the required semantics and
copy the values.  But this is more the difference between a value and
reference-based language than anything.


Sean

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Sean 10/2/2004 10:02:51 AM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:415db7b2$0$55830$14726298@news.sunsite.dk...
> "Walter" <walter@digitalmars.nospamm.com> wrote in message
> news:s7X6d.181849$3l3.134844@attbi_s03...
> | > There is also other obvious improvements which I haven't done:
> | >
> | > 1. load the file faster, ie, make sure the vector<char> is only
written
> | once
> | > 2. use hash_map (I assume D uses hashing internally in int[char[]]?)
> | > 3. use a custom allocator with the map
> | >
> | > Given these I bet the C++ version would be faster.
> |
> | I could spend time optimizing the D version, too, but the point is I
made no
> | effort to - it's just a straightforward rendering.
>
> that is a good point: you could optimize it; your users *cannot* because
it's
> not provided as a library but hardcoded into
> the language. For quite some time we (at least large parts of the C++
> community) have known that libraries are more flexible than core-language
> extensions;
> this is why there is a focus on extending the language with support for
better
> library building.

It is a myth that standard library classes are more flexible than core
language features. And here's why:

1) The Standard firmly nails down the behavior of the library classes to the
point where they could even be hardwired into the compiler. That isn't one
iota more flexible than just putting them in the compiler to begin with.

2) D ships with full library source code. There is nothing stopping anyone
from optimizing the heck out of the hash table implementation, just as
there's nothing stopping anyone from optimizing the library source for
std::string. For both, you just need to be sure you didn't change the API.
No difference.

3) Your example demonstrates the inflexibility of Standard library classes -
you didn't use them. You used a different one (boost::sub_range<const
string>) instead of std::string.

4) I'm still getting the impression that people feel that adding core
strings in D has taken away flexibility. Nothing could be more wrong. You
can have a hash table of user defined types like this in D:

    int [ boost.sub_range!(char[]) ] foo;

How is that less flexible than:

    map<boost::sub_range<const string>,int> foo;

?? Of course, the real difference is you rarely need to make such types,
because D core strings are a superset of C strings, std::string,
std::vector<char>, boost::sub_range<string>, etc. It would be as pointless
as writing an Integer<> template in C++. All the need for those classes just
goes away in D, as well as all the problems with trying to make them
interoperate.


> | You have to do a bunch of
> | careful work, and be very knowledgeable about C++, to compete (and it's
> | still slower).
>
> This is true, but a more mature C++ standard library will probably make
this
> an obvious approach.

Isn't it simpler to have one string type rather than the 4 you're proposing
for C++? The point isn't really whether or not if you try hard enough and
you're careful enough and you write enough code you can make it work, you
*can* make it work in C++. The point is why bother going from New York to
Seattle by going around the horn when you can go direct? Not only is it more
convenient, quicker to write, less brittle, easier to learn, but it's
faster, too. I can't think of any practical reason to prefer going around
the horn.

C++ has been around for 20 years now. Haven't we all got better things to do
than be *still* working on the string class trying to make it "more mature"?


> | > ** boost::sub_range<> is part of boost.range which will be in the next
> | version
> | > of boost
> |
> | So it isn't even commonly available yet <g>.
>
> but anyway, I hope you will distribute boost together with digital mars.

I will when boost works with DMC++.

-Walter
www.digitalmars.com free C / C++ / D compilers


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 10/3/2004 1:49:14 AM

In article <415db302$0$55824$14726298@news.sunsite.dk>, Thorsten Ottosen 
<nesotto@cs.auc.dk> writes
>It looks pretty ok too. In my library, () (that is, empty paranthesis)  is
>used to make a default constructed element.
>How would that look in your proposal?
Remember that this is work in progress, however my current thinking is 
that the default ctor is specified with {}.

>
>How will you do something like
>
>Foo a,b,c,d;
>...
>Foo& f = *max_element( list_of<Foo&,4>( a )( b )( c )( d ) );

Foo& f = *max(Foo flist_ptr[]{&a, &b, &c, &d});

assuming the prototype for max is:

Foo* max(Foo* []);




-- 
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Francis 10/3/2004 1:53:32 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:YOr7d.153935$MQ5.102526@attbi_s52...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:415dba15$0$55825$14726298@news.sunsite.dk...
|  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
|  > news:AUY6d.182005$3l3.11663@attbi_s03...
|  > | The factor of 3 comes from using DMD
|  > | and DMC++. Those two compilers share the optimizer and back end code
|  > | generator - so the result is comparing the *languages*, not the
register
|  > | allocator or loop unroller.
|  > not understood. surely it must be a comparison of two implementations.
|
| You can get very different speed results on a benchmark by using different
| C++ compilers that have different optimizers and code generators. Therefore,
| to compare *languages*, rather than optimizer/codegenerators, one needs to
| factor out the effect of different optimizer/codegenerators by using the
| same optimizer/codegenerator for each language. That is easy to do for D vs
| C++ because DMD and DMC++ share the same optimizer/codegenerator. gdc and
| g++ can give good results for the same reason.

its still not obvious why this is not a comparison between two
implementations. Do you disagree that
there is not two physical implementation, one of each lanaguage?

What guarantee do we have that these two implementations are optimal for their
respective languages?
None AFAICT. (remark: I'm pretty sure its simpler to implement a D compiler,
but that is not the issue here)

| Using DMD and VC gives no useful information comparing languages, since you
| have two unknowns and only one equation.
|
|  > any chance you spend more time on the D builtin hash_map/string than
|  > the C++'s libraries map/string?
|
| You can review D's hashing source code in /dmd/src/phobos/internal/aaA.d and
| /dmd/src/phobos/std/typeinfo/ti_Ag.d (in the D zip distribution). I think
| you'll find it's straightforward and not supertuned.

again, this is beyond the application programmer to change the compiler.

I've done some more testing and it turns out that using a hash map is very
important for the
performance. I have a small problem with the hash map, but I don't think it
affects the test.
My result is that the C++ version is now ca. 50% faster. Once I solve the hash
map problem
you can put it on your web page as a non-standard way of counting words in
C++.

|  > | In your optimization, you used map<boost::sub_range<const string>,int>
| to
|  > | avoid copying the strings. That's fine up until perhaps you want to
| insert a
|  > | string into the map that is a regular string, not a boost::subrange.
| (The
|  > | optimization you mentioned of using a custom allocator makes the C++
| version
|  > | even more specific and less flexible.)
|  >
|  > map<sub_range<const string>,int> m1 = ..;
|  > map<string,int> m( m1.begin(), m1.end() );
|  >
|  > should work fine if the sub_range provided a conversion operator().
|
| It's not as simple as providing a conversion operator. The issue is who owns
| the memory.
| Converting a string to a subrange implies that one must still
| keep track of the original string separately, and remember to only delete it
| *after* the map gets deleted. This is not a trivial issue.

actually I imagined a conversion operator that created a new string.
std::string can't accept
a buffer to alias.

|  > | Even worse, you have to be careful
|  > | not to delete the string it is a subrange of before the map<> is
| deleted.
|  >
|  > you'll know pretty early if that's hapening.
|
| Not necessarilly. Deleting an object in one spot and continuing to refer to
| it elsewhere is the dangling pointer problem, and can be very, very hard to
| track down.

It all depends..,if you work with sub_range<string> you know the sub_range
only references
another string. It is part of the way it is supossed to be used. And in the
word counting example, you will know
very easily if you made that error.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Thorsten 10/3/2004 1:58:42 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:Ezw7d.55832$He1.21584@attbi_s01...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message

| > that is a good point: you could optimize it; your users *cannot* because
| it's
| > not provided as a library but hardcoded into
| > the language. For quite some time we (at least large parts of the C++
| > community) have known that libraries are more flexible than core-language
| > extensions;
| > this is why there is a focus on extending the language with support for
| better
| > library building.
|
| It is a myth that standard library classes are more flexible than core
| language features. And here's why:
|
| 1) The Standard firmly nails down the behavior of the library classes to the
| point where they could even be hardwired into the compiler. That isn't one
| iota more flexible than just putting them in the compiler to begin with.

I don't get that statement. Library classes has lots of customizations points.

| 2) D ships with full library source code. There is nothing stopping anyone
| from optimizing the heck out of the hash table implementation, just as
| there's nothing stopping anyone from optimizing the library source for
| std::string. For both, you just need to be sure you didn't change the API.
| No difference.

There's a huge difference. All the optimizations I've done to the example
were high-level optimization, in fact three of them consists mostly of
changing a typedef. And that shows
the power of the C++ library approach.

| 3) Your example demonstrates the inflexibility of Standard library classes -
| you didn't use them. You used a different one (boost::sub_range<const
| string>) instead of std::string.

Yeah, there is a need for some small changes to the string class, but mostly
std::string does its job ok; I only changed the types in order to optimize the
example.

| 4) I'm still getting the impression that people feel that adding core
| strings in D has taken away flexibility. Nothing could be more wrong. You
| can have a hash table of user defined types like this in D:
|
|     int [ boost.sub_range!(char[]) ] foo;
|
| How is that less flexible than:
|
|     map<boost::sub_range<const string>,int> foo;
|
| ??

Tell me how many ways I can affect the behavior of the hash table so
it fits my needs. I assume you can define the hash function somehow. What else
can you control?
Memory? Equality predicate? Can I inheirit from that class?

|Of course, the real difference is you rarely need to make such types,
| because D core strings are a superset of C strings, std::string,
| std::vector<char>, boost::sub_range<string>, etc. It would be as pointless
| as writing an Integer<> template in C++. All the need for those classes just
| goes away in D, as well as all the problems with trying to make them
| interoperate.

I'm not claiming the D string is particular bad (they seem ok, in fact), but
even for strings no size fits all.

| > | You have to do a bunch of
| > | careful work, and be very knowledgeable about C++, to compete (and it's
| > | still slower).
| >
| > This is true, but a more mature C++ standard library will probably make
| this
| > an obvious approach.
|
| Isn't it simpler to have one string type rather than the 4 you're proposing
| for C++? The point isn't really whether or not if you try hard enough and
| you're careful enough and you write enough code you can make it work, you
| *can* make it work in C++. The point is why bother going from New York to
| Seattle by going around the horn when you can go direct? Not only is it more
| convenient, quicker to write, less brittle, easier to learn, but it's
| faster, too. I can't think of any practical reason to prefer going around
| the horn.
|
| C++ has been around for 20 years now. Haven't we all got better things to do
| than be *still* working on the string class trying to make it "more mature"?

std::string does it's job fairly ok and ordinary programmers need not worry
much about it,
 but the standard comittee still looks on enhancements
if necessary.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/3/2004 1:13:29 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:415e979b$0$55820$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:YOr7d.153935$MQ5.102526@attbi_s52...
 > |
 > | "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
 > | news:415dba15$0$55825$14726298@news.sunsite.dk...
 > |  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > |  > news:AUY6d.182005$3l3.11663@attbi_s03...
 > |  > | The factor of 3 comes from using DMD
 > |  > | and DMC++. Those two compilers share the optimizer and back end
code
 > |  > | generator - so the result is comparing the *languages*, not the
 > register
 > |  > | allocator or loop unroller.
 > |  > not understood. surely it must be a comparison of two
implementations.
 > |
 > | You can get very different speed results on a benchmark by using
different
 > | C++ compilers that have different optimizers and code generators.
Therefore,
 > | to compare *languages*, rather than optimizer/codegenerators, one needs
to
 > | factor out the effect of different optimizer/codegenerators by using the
 > | same optimizer/codegenerator for each language. That is easy to do for D
vs
 > | C++ because DMD and DMC++ share the same optimizer/codegenerator. gdc
and
 > | g++ can give good results for the same reason.
 >
 > its still not obvious why this is not a comparison between two
 > implementations. Do you disagree that
 > there is not two physical implementation, one of each lanaguage?

Only the front ends differ. The optimizer and the code generator are the
SAME. That makes it a much better language comparison than if the optimizer
and code generator are wildly different.

 > What guarantee do we have that these two implementations are optimal for
their
 > respective languages?

Irrelevant, they only need to be of equivalent quality. The quality of the
optimizers and code generators are the same, since they are identical. The
DMD front end is arguably of lower quality than the DMC++ front end, since
it is much newer code, yet the generated code is 3x faster.

 > None AFAICT. (remark: I'm pretty sure its simpler to implement a D
compiler,
 > but that is not the issue here)

Since I wrote both of them, and I've been working on improving the C++ front
end for 15 years, many C++ specific code optimizations were pioneered by
DMC++, so it's hard to imagine why I'd sabotage the C++ front end to make it
look bad (Digital Mars is in the C++ compiler business, it's our bread and
butter). I wrote the front end for DMD, it is NOT well tuned for optimal
code generation. My current priority is simply generating semantically
correct code. My inbox is full of suggestions for improving the quality. Yet
the D code generated is 3 times faster than the equivalent C++ code.

 > | Using DMD and VC gives no useful information comparing languages, since
you
 > | have two unknowns and only one equation.
 > |
 > |  > any chance you spend more time on the D builtin hash_map/string than
 > |  > the C++'s libraries map/string?
 > |
 > | You can review D's hashing source code in /dmd/src/phobos/internal/aaA.d
and
 > | /dmd/src/phobos/std/typeinfo/ti_Ag.d (in the D zip distribution). I
think
 > | you'll find it's straightforward and not supertuned.
 >
 > again, this is beyond the application programmer to change the compiler.

He's not changing the compiler. He's changing the runtime library helper
functions. It's no different from changing the implementation of
std::string. All the compiler does is just package up the operands and send
them to the helper functions. I get the impression you think there's
something clever going on here under the hood, but there isn't. This is
easilly verifiable to be true, just compile some snippets and obj2asm the
result.

To reiterate, the 3x speed improvement cannot be explained away as being a
clever D code generator and a crippled C++ one.

 > I've done some more testing and it turns out that using a hash map is very
 > important for the
 > performance. I have a small problem with the hash map, but I don't think
it
 > affects the test.
 > My result is that the C++ version is now ca. 50% faster. Once I solve the
hash
 > map problem
 > you can put it on your web page as a non-standard way of counting words in
 > C++.

Sure, as long as it is a complete set of code that only requires the
addition of the standard library, i.e. not requiring the reader to download
and wrestle with some separate third party code.

 > |  > | In your optimization, you used map<boost::sub_range<const
string>,int> to
 > |  > | avoid copying the strings. That's fine up until perhaps you want to
insert a
 > |  > | string into the map that is a regular string, not a
boost::subrange. (The
 > |  > | optimization you mentioned of using a custom allocator makes the
C++ version
 > |  > | even more specific and less flexible.)
 > |  >
 > |  > map<sub_range<const string>,int> m1 = ..;
 > |  > map<string,int> m( m1.begin(), m1.end() );
 > |  >
 > |  > should work fine if the sub_range provided a conversion operator().
 > |
 > | It's not as simple as providing a conversion operator. The issue is who
owns
 > | the memory.
 > | Converting a string to a subrange implies that one must still
 > | keep track of the original string separately, and remember to only
delete it
 > | *after* the map gets deleted. This is not a trivial issue.
 >
 > actually I imagined a conversion operator that created a new string.
 > std::string can't accept a buffer to alias.

If it creates a new string, then who 'owns' it? (And creating a new string
to move between the various string types is just another example of why
C++'s multiple string types is suboptimal.)


 > |  > | Even worse, you have to be careful
 > |  > | not to delete the string it is a subrange of before the map<> is
 > | deleted.
 > |  >
 > |  > you'll know pretty early if that's hapening.
 > |
 > | Not necessarilly. Deleting an object in one spot and continuing to refer
to
 > | it elsewhere is the dangling pointer problem, and can be very, very hard
to
 > | track down.
 >
 > It all depends..,if you work with sub_range<string> you know the sub_range
 > only references
 > another string. It is part of the way it is supossed to be used. And in
the
 > word counting example, you will know
 > very easily if you made that error.

I don't understand how the sub_range can very easilly detect if someone else
deleted the string it refers to.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 10/4/2004 6:06:45 AM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:415ff121$0$55825$14726298@news.sunsite.dk...
> "Walter" <walter@digitalmars.nospamm.com> wrote in message
> | It is a myth that standard library classes are more flexible than core
> | language features. And here's why:
> |
> | 1) The Standard firmly nails down the behavior of the library classes to
the
> | point where they could even be hardwired into the compiler. That isn't
one
> | iota more flexible than just putting them in the compiler to begin with.
>
> I don't get that statement. Library classes has lots of customizations
points.

Not that much. For example, you had to go outside of it instead of
customizing it.


> | 2) D ships with full library source code. There is nothing stopping
anyone
> | from optimizing the heck out of the hash table implementation, just as
> | there's nothing stopping anyone from optimizing the library source for
> | std::string. For both, you just need to be sure you didn't change the
API.
> | No difference.
> There's a huge difference. All the optimizations I've done to the example
> were high-level optimization, in fact three of them consists mostly of
> changing a typedef. And that shows
> the power of the C++ library approach.

I disagree that you just changed a typedef. You used a different string
class that is not part of the standard library. Essentially, you had to
write a whole new class to replace the library one (even if you just
downloaded the class from someplace else). You could write a new class with
D to replace the core strings. Where is the advantage of C++'s library class
being in a library?


> | 4) I'm still getting the impression that people feel that adding core
> | strings in D has taken away flexibility. Nothing could be more wrong.
You
> | can have a hash table of user defined types like this in D:
> |
> |     int [ boost.sub_range!(char[]) ] foo;
> |
> | How is that less flexible than:
> |
> |     map<boost::sub_range<const string>,int> foo;
> |
> | ??
>
> Tell me how many ways I can affect the behavior of the hash table so
> it fits my needs. I assume you can define the hash function somehow. What
else
> can you control?
> Memory? Equality predicate? Can I inheirit from that class?

There's no reason whatsoever in D that you cannot write your own map
template to do whatever you want, just like in C++. I wish to reiterate that
having associative arrays built-in in D does not take away from you the
ability to write your own hash map. Also, D's associative arrays work with
UDT's.


> |Of course, the real difference is you rarely need to make such types,
> | because D core strings are a superset of C strings, std::string,
> | std::vector<char>, boost::sub_range<string>, etc. It would be as
pointless
> | as writing an Integer<> template in C++. All the need for those classes
just
> | goes away in D, as well as all the problems with trying to make them
> | interoperate.
>
> I'm not claiming the D string is particular bad (they seem ok, in fact),
but
> even for strings no size fits all.

D strings more than cover the sizes of C++ char[], std::string,
std::valarray, and boost::sub_range.


> | Isn't it simpler to have one string type rather than the 4 you're
proposing
> | for C++? The point isn't really whether or not if you try hard enough
and
> | you're careful enough and you write enough code you can make it work,
you
> | *can* make it work in C++. The point is why bother going from New York
to
> | Seattle by going around the horn when you can go direct? Not only is it
more
> | convenient, quicker to write, less brittle, easier to learn, but it's
> | faster, too. I can't think of any practical reason to prefer going
around
> | the horn.
> |
> | C++ has been around for 20 years now. Haven't we all got better things
to do
> | than be *still* working on the string class trying to make it "more
mature"?
>
> std::string does it's job fairly ok and ordinary programmers need not
worry
> much about it,
>  but the standard comittee still looks on enhancements
> if necessary.

I don't mean to imply that std::string doesn't work. It does. But I guess I
want better than "fairly ok" <g>.



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 10/4/2004 6:06:31 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:zXU7d.169234$D%.89094@attbi_s51...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message

|  > | to compare *languages*, rather than optimizer/codegenerators, one needs
| to
|  > | factor out the effect of different optimizer/codegenerators by using
the
|  > | same optimizer/codegenerator for each language. That is easy to do for
D
| vs
|  > | C++ because DMD and DMC++ share the same optimizer/codegenerator. gdc
| and
|  > | g++ can give good results for the same reason.
|  >
|  > its still not obvious why this is not a comparison between two
|  > implementations. Do you disagree that
|  > there is not two physical implementation, one of each lanaguage?
|
| Only the front ends differ. The optimizer and the code generator are the
| SAME. That makes it a much better language comparison than if the optimizer
| and code generator are wildly different.
|
|  > What guarantee do we have that these two implementations are optimal for
| their
|  > respective languages?
|
| Irrelevant, they only need to be of equivalent quality. The quality of the
| optimizers and code generators are the same, since they are identical. The
| DMD front end is arguably of lower quality than the DMC++ front end, since
| it is much newer code, yet the generated code is 3x faster.
|
|  > None AFAICT. (remark: I'm pretty sure its simpler to implement a D
| compiler,
|  > but that is not the issue here)
|
| Since I wrote both of them, and I've been working on improving the C++ front
| end for 15 years, many C++ specific code optimizations were pioneered by
| DMC++, so it's hard to imagine why I'd sabotage the C++ front end to make it
| look bad (Digital Mars is in the C++ compiler business, it's our bread and
| butter). I wrote the front end for DMD, it is NOT well tuned for optimal
| code generation. My current priority is simply generating semantically
| correct code. My inbox is full of suggestions for improving the quality. Yet

| the D code generated is 3 times faster than the equivalent C++ code.

I guess we need to have the same version of what comparing languages means.
From what I can infer, the two languages have and support vastly different
coding idioms
and different views of how to apply optimizations. Something as crusial as
using
a hash map should be used for both pieces of code, otherwise what's the point
of
comparing two somewhat different programs? (Please don't answer C++ don't have
a hash map)

|  > | Using DMD and VC gives no useful information comparing languages, since
| you
|  > | have two unknowns and only one equation.
|  > |
|  > |  > any chance you spend more time on the D builtin hash_map/string than
|  > |  > the C++'s libraries map/string?
|  > |
|  > | You can review D's hashing source code in
/dmd/src/phobos/internal/aaA.d
| and
|  > | /dmd/src/phobos/std/typeinfo/ti_Ag.d (in the D zip distribution). I
| think
|  > | you'll find it's straightforward and not supertuned.
|  >
|  > again, this is beyond the application programmer to change the compiler.
|
| He's not changing the compiler. He's changing the runtime library helper
| functions. It's no different from changing the implementation of
| std::string.

of course it is; there is a huge difference between changing a typedef and
then reimplementing
something from scratch.

|All the compiler does is just package up the operands and send
| them to the helper functions. I get the impression you think there's
| something clever going on here under the hood, but there isn't.

I'm simply interested in what could cause the C++ version to be slower; you
never seemed interesting in
knowing or answering that and that could be why some people at least view your
"comparison" a little biased.
So I did the work for you and told you that it was mostly

1. string copying
2. not using a hash map

but also

3. file loading

| This is
| easilly verifiable to be true, just compile some snippets and obj2asm the
| result.
|
| To reiterate, the 3x speed improvement cannot be explained away as being a
| clever D code generator and a crippled C++ one.

I agree.

|  > I've done some more testing and it turns out that using a hash map is
very
|  > important for the
|  > performance. I have a small problem with the hash map, but I don't think
| it
|  > affects the test.
|  > My result is that the C++ version is now ca. 50% faster. Once I solve the
| hash
|  > map problem
|  > you can put it on your web page as a non-standard way of counting words
in
|  > C++.
|
| Sure, as long as it is a complete set of code that only requires the
| addition of the standard library, i.e. not requiring the reader to download
| and wrestle with some separate third party code.

well, I can make a zip with everything in.

|  > | It's not as simple as providing a conversion operator. The issue is who
| owns
|  > | the memory.
|  > | Converting a string to a subrange implies that one must still
|  > | keep track of the original string separately, and remember to only
| delete it
|  > | *after* the map gets deleted. This is not a trivial issue.
|  >
|  > actually I imagined a conversion operator that created a new string.
|  > std::string can't accept a buffer to alias.
|
| If it creates a new string, then who 'owns' it?

it owns itself.

| (And creating a new string
| to move between the various string types is just another example of why
| C++'s multiple string types is suboptimal.)

well, you asked a specific question (how do I convert a sub_range<string> to a
string?) and I answered it. I don't think it's fair to
turn that into a critique.

|  > It all depends..,if you work with sub_range<string> you know the
sub_range
|  > only references
|  > another string. It is part of the way it is supossed to be used. And in
| the
|  > word counting example, you will know
|  > very easily if you made that error.
|
| I don't understand how the sub_range can very easilly detect if someone else
| deleted the string it refers to.

the program crashes.

br

Thorsten




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/4/2004 6:19:28 PM



"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:Bk58d.410080$8_6.31260@attbi_s04...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:415ff121$0$55825$14726298@news.sunsite.dk...
| > "Walter" <walter@digitalmars.nospamm.com> wrote in message
| > | It is a myth that standard library classes are more flexible than core
| > | language features. And here's why:
| > |
| > | 1) The Standard firmly nails down the behavior of the library classes to
| the
| > | point where they could even be hardwired into the compiler. That isn't
| one
| > | iota more flexible than just putting them in the compiler to begin with.
| >
| > I don't get that statement. Library classes has lots of customizations
| points.
|
| Not that much. For example, you had to go outside of it instead of
| customizing it.

It's not really the issue here.
The new C++ will have hash-maps and more; possible also my range library.
Ordered maps have their use too, word counting not being the best. Move
semantics
will boost performance quite a lot. also for the word-counting example.

| > | 2) D ships with full library source code. There is nothing stopping
| anyone
| > | from optimizing the heck out of the hash table implementation, just as
| > | there's nothing stopping anyone from optimizing the library source for
| > | std::string. For both, you just need to be sure you didn't change the
| API.
| > | No difference.
| > There's a huge difference. All the optimizations I've done to the example
| > were high-level optimization, in fact three of them consists mostly of
| > changing a typedef. And that shows
| > the power of the C++ library approach.
|
| I disagree that you just changed a typedef.
| You used a different string
| class that is not part of the standard library.

Eventually this will hopefully be all one does in C++0x.

| Essentially, you had to
| write a whole new class to replace the library one (even if you just
| downloaded the class from someplace else).

Nobody is saying the current C++ library is perfect. Is just that what we're
trying to
compare here doesn't make much sense if we're not allowed to use the optimal
approach
for each language. Much less if we're not using a hash map in both programs.

| You could write a new class with
| D to replace the core strings. Where is the advantage of C++'s library class
| being in a library?

I think we've been over that before.

| > |Of course, the real difference is you rarely need to make such types,
| > | because D core strings are a superset of C strings, std::string,
| > | std::vector<char>, boost::sub_range<string>, etc. It would be as
| pointless
| > | as writing an Integer<> template in C++. All the need for those classes
| just
| > | goes away in D, as well as all the problems with trying to make them
| > | interoperate.
| >
| > I'm not claiming the D string is particular bad (they seem ok, in fact),
| but
| > even for strings no size fits all.
|
| D strings more than cover the sizes of C++ char[], std::string,
| std::valarray, and boost::sub_range.

this is a bit absurd. sub_range for instance is templated on some container
type, std::string
just being one possibility.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/5/2004 6:22:24 AM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:4161990e$0$59394$14726298@news.sunsite.dk...
> | D strings more than cover the sizes of C++ char[], std::string,
> | std::valarray, and boost::sub_range.
>
> this is a bit absurd. sub_range for instance is templated on some
container
> type, std::string
> just being one possibility.

To get a subrange in D of any array foo:

    foo[a .. b]

That applies to all strings, all arrays, and even user defined types (since
[..] is overloadable). It isn't a trick confined to strings. Creating a
separate type to do a subrange is not necessary.

What can C++'s multiple string types do that D strings do not cover? C++
strings don't even cover unicode. You have to write yet another string
class.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 10/5/2004 10:40:35 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:0wz8d.184934$MQ5.33591@attbi_s52...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:4161990e$0$59394$14726298@news.sunsite.dk...
| > | D strings more than cover the sizes of C++ char[], std::string,
| > | std::valarray, and boost::sub_range.
| >
| > this is a bit absurd. sub_range for instance is templated on some
| container
| > type, std::string
| > just being one possibility.
|
| To get a subrange in D of any array foo:
|
|     foo[a .. b]
|
| That applies to all strings, all arrays, and even user defined types (since
| [..] is overloadable). It isn't a trick confined to strings. Creating a
| separate type to do a subrange is not necessary.
|
| What can C++'s multiple string types do that D strings do not cover? C++
| strings don't even cover unicode. You have to write yet another string
| class.

hopefully basic_string<some_char_type> can cover unicode in some way.

br

Thorsten



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 10/6/2004 1:37:38 PM

"Walter" <walter@digitalmars.nospamm.com> writes:

 >> > I understand what you mean. Where we disagree is the value in the
 >> > very weak notion of const in C++.
 >>
 >> I disagree that it's weak.  It's highly expressive.
 >>
 >> How do you write a function in D that doesn't modify its argument, to
 >> which you can pass a const or non-const object?
 >
 > Call by value makes a copy anyway, so that's not an issue. Call by reference
 > from a const object - this happens rarely since nearly all reference objects
 > will be on the heap, not in read-only memory. And to reiterate, I've never
 > had a programming bug where a function modified a readonly object it
 > shouldn't have. It just doesn't come up.

I don't think the experience report in
http://www.pag.csail.mit.edu/pubs/ref-immutability-oopsla2004-abstract.html
was just due to the slightly stronger notion of const used in
Javari.  With the exercise of some reasonable programming discipline
(i.e., not using const_cast) you can easily achieve the same kinds of
results in C++.


-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 11/1/2004 12:18:31 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:

 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > news:NE22d.183665$9d6.155903@attbi_s54...
 > |
 > | "David Abrahams" <dave@boost-consulting.com> wrote in message
 >
 > | What does come up often is the nasty uninitialized data problem C++ has in
 > | multiple manifestations. Plugging *those* holes would have saved me hundreds
 > | of frustrating hours over the years.

I didn't write that.

And BTW, I think
http://www.pag.csail.mit.edu/pubs/ref-immutability-oopsla2004-abstract.html
pretty much clears up the question of whether const correctness can
help find bugs.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply David 11/1/2004 12:20:24 PM

"David Abrahams" <dave@boost-consulting.com> wrote in message
news:u4qkbm32k.fsf@boost-consulting.com...
 > "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
 >
 >  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 >  > news:NE22d.183665$9d6.155903@attbi_s54...
 >  > |
 >  > | "David Abrahams" <dave@boost-consulting.com> wrote in message
 >  >
 >  > | What does come up often is the nasty uninitialized data problem C++
has in
 >  > | multiple manifestations. Plugging *those* holes would have saved me
hundreds
 >  > | of frustrating hours over the years.
 >
 > I didn't write that.

Correct, I wrote it.


 > And BTW, I think
 >
http://www.pag.csail.mit.edu/pubs/ref-immutability-oopsla2004-abstract.html
 > pretty much clears up the question of whether const correctness can
 > help find bugs.

Thanks for the reference to the interesting paper. I guess I don't find the
one coding error in Gizmoball and 19 in Daikon as convincing proof of a
general problem rather than perhaps an aberration. Furthermore, the paper
specifies a system that goes much further than C++ const, and goes on to
criticize the weak notion of C++ const. There's no evidence presented that
the C++ const would have found any of the problems that Javari did.

I'm not saying that C++ const correctness finds no bugs at any time. I'm
just pointing out that it doesn't appear to be nearly as efficaceous as it
is assumed to be. After a decade and a half of experience, shouldn't such
evidence be more apparent? And why the attention on that rather than the
obvious and gaping problems of uninitialized data in C++?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Walter 11/2/2004 1:32:18 PM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:gXxhd.556181$8_6.149858@attbi_s04...
|
| "David Abrahams" <dave@boost-consulting.com> wrote in message

|  > And BTW, I think
|  >
| http://www.pag.csail.mit.edu/pubs/ref-immutability-oopsla2004-abstract.html
|  > pretty much clears up the question of whether const correctness can
|  > help find bugs.
|

| I'm not saying that C++ const correctness finds no bugs at any time. I'm
| just pointing out that it doesn't appear to be nearly as efficaceous as it
| is assumed to be. After a decade and a half of experience, shouldn't such
| evidence be more apparent?

How much evidense do you need? each time I get a compile-error related to
constness, it has caught a design flaw in my code; I did something I had
earlier
expected not to be allowed. I would expect this to happen daily for C++
programmers.

| And why the attention on that rather than the
| obvious and gaping problems of uninitialized data in C++?

We're already waiting for a post-redmond paper from you on how to solve this
:-)
Discussing it again and again in newsgroups is definitely not going to solve
it; a paper could.

br

Thorsten




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Thorsten 11/2/2004 8:52:21 PM

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
news:4187965d$0$33742$14726298@news.sunsite.dk...
 > "Walter" <walter@digitalmars.nospamm.com> wrote in message
 > | I'm not saying that C++ const correctness finds no bugs at any time. I'm
 > | just pointing out that it doesn't appear to be nearly as efficaceous as
it
 > | is assumed to be. After a decade and a half of experience, shouldn't
such
 > | evidence be more apparent?
 > How much evidense do you need?

Significant, because such errors have not happened in my own experience
(though I make many, many other kinds of errors), and there's a significant
cost, aesthetic and time-wise, to writing const-correct code.

 > each time I get a compile-error related to
 > constness, it has caught a design flaw in my code; I did something I had
 > earlier
 > expected not to be allowed. I would expect this to happen daily for C++
 > programmers.

I agree there's a significant investment in time in writing const-correct
code that will pass the compiler's type checking. I'm more interested in if
such effort results in actual fixing of programming bugs. The paper David
pointed to did try to classify the various sorts of errors.

Are there other major languages that support the notion of
const-correctness? And is there evidence that C++ programs are less buggy as
a result of supporting that notion while other languages do not?

 > | And why the attention on that rather than the
 > | obvious and gaping problems of uninitialized data in C++?
 >
 > We're already waiting for a post-redmond paper from you on how to solve
this
 > :-)
 > Discussing it again and again in newsgroups is definitely not going to
solve
 > it; a paper could.

The C++ community seems largely indifferent to this issue, which puzzles me,
as I find it to be a particularly wretched source of bugs both in my own
code and in code of people I'm trying to help. A lot of the design decisions
for D come from my experience with C and C++ and the desire to eliminate
common sources of bugs.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Walter 11/3/2004 9:51:43 AM

"Walter" <walter@digitalmars.nospamm.com> wrote in message
news:JNYhd.48445$R05.10323@attbi_s53...
|
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message
| news:4187965d$0$33742$14726298@news.sunsite.dk...
|  > "Walter" <walter@digitalmars.nospamm.com> wrote in message
|  > | I'm not saying that C++ const correctness finds no bugs at any time.
I'm
|  > | just pointing out that it doesn't appear to be nearly as efficaceous as
| it
|  > | is assumed to be. After a decade and a half of experience, shouldn't
| such
|  > | evidence be more apparent?
|  > How much evidense do you need?
|
| Significant, because such errors have not happened in my own experience
| (though I make many, many other kinds of errors), and there's a significant
| cost, aesthetic and time-wise, to writing const-correct code.

It's not just about removing specific errors. Its also about overall code
quality and about
making code selfdocumenting.

|  > each time I get a compile-error related to
|  > constness, it has caught a design flaw in my code; I did something I had
|  > earlier
|  > expected not to be allowed. I would expect this to happen daily for C++
|  > programmers.
|
| I agree there's a significant investment in time in writing const-correct
| code that will pass the compiler's type checking. I'm more interested in if
| such effort results in actual fixing of programming bugs. The paper David
| pointed to did try to classify the various sorts of errors.
|
| Are there other major languages that support the notion of
| const-correctness?

don't no.

| And is there evidence that C++ programs are less buggy as
| a result of supporting that notion while other languages do not?

That is a hard question to answer. The thing is that having const changes the
way you
think about programming and it affects your code-base significantly.

There is in my opinion a lot of *common sense* about the use of const that
implies significant benefits:

1. one can document and enforce read-only (and mtuable) behavior
2. one can enforce procedure/function seperation (ie, side-effect, no
side-effect)
3. greater interface flexibility

What precise bug it eliminates is harder to say than in the initialization
example, since it relates some to what I would call
code hygiene. But it also allows for more expressiveness, for example

class Photon
{
     Vector3 direction_;
     ...
  public:
    const Vector3& direction() const;
};

this type of read-only behavior I cannot express in languages without const
(or value-semantics) and as a result the way encapsulation works is poorer.
If I had a cent each time a Java programmer returned a reference to some
internal data
*under the assumption it should only be read* I would be rich. (I bet the same
problem happens often in D).

-Thorsten




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Thorsten 11/4/2004 11:59:03 AM

191 Replies
204 Views

(page loaded in 1.414 seconds)


Reply: