Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

We do not use C++ exceptions

2,807 views
Skip to first unread message

Martin T.

unread,
Jan 9, 2009, 6:21:33 PM1/9/09
to
I found the arguments in the Google Style Guide quite interesting:

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Exceptions#Exceptions

quote Con(3): "Exception safety requires both RAII and different coding
practices. Lots of supporting machinery is needed to make writing
correct exception-safe code easy. Further, to avoid requiring readers to
understand the entire call graph, exception-safe code must isolate logic
that writes to persistent state into a "commit" phase. This will have
both benefits and costs (...)"

- Of course. When people are not using exceptions, they are usually just
ignoring the error return codes, so no need for a no-fail commit phase.
::-)

quote Decisinon: "Given that Google's existing code is not
exception-tolerant, the costs of using exceptions are somewhat greater
(...) We don't believe that the available alternatives to exceptions,
such as error codes and assertions, introduce a significant burden."
"(...) Because we'd like to use our open-source projects at Google and
it's difficult to do so if those projects use exceptions, we need to
advise against exceptions in Google open-source projects as well."

- I really don't know. I'm all for not introducing exceptions into
existing code bases that are not exception safe. But recommending
against the use of exceptions for new (sub)projects and modules because
the existing code base does not use them seems a bit overreacting.

I just hope this doesn't spread.

br,
Martin

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

Metron

unread,
Jan 10, 2009, 7:22:23 PM1/10/09
to
I've been thinking several times of using exceptions in new projects
(done in C++). But ultimately I decided against because when you see a
function declaration, you cannot see which exceptions might be thrown
by that function. You have to read and re-read almost every time you
want to use that function to be sure to catch the right exceptions.

Also, IIRC but please correct me, if I'm wrong, under Windows throwing
exceptions is problematic once it has to cross DLL borders (ie. when
you've implemented an interface within the DLL and a function within
the implementation has to throw an exception).

That's the point I like in Java: You declare the exceptions the
function might throw in the function declaration.

Have fun,
Metron

Balog Pal

unread,
Jan 10, 2009, 7:25:13 PM1/10/09
to

"Martin T." <0xCDC...@gmx.at> :

>I found the arguments in the Google Style Guide quite interesting:
>
> http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Exceptions#Exceptions

I read through the guide, it has a couple items that is completely nuts:

- "Reference Arguments: All parameters passed by reference must be labeled
const. ...
In fact it is a very strong convention that input arguments are values or
const references while output arguments are pointers. ..."

I'm using C++ since about CFront 2.0, first time to hear this idea, and it
is IMNSHO utter nonsense.

- Default Arguments: We do not allow default function parameters.

DOH.

- Doing Work in Constructors: Do only trivial initialization in a
constructor. If at all possible, use an Init() method for non-trivial
initialization. ... If your object requires non-trivial initialization,
consider having an explicit Init() method and/or adding a member flag that
indicates whether the object was successfully initialized.

I recon most mentors say to do 2 phase init only when absolutely necessary
for data flow or use cases (i.e. you need instances before knowing params).
Make it based on "amount of work"?

- (here comes your observed) Exceptions: We do not use C++ exceptions.

> quote Con(3): "Exception safety requires both RAII and different coding

> practices. ...


> - Of course. When people are not using exceptions, they are usually just
> ignoring the error return codes, so no need for a no-fail commit phase.
> ::-)

Yea++. RAII and SEME-tolerant layout serves better readability and
correctness. Wrt anything, including exceptions.

> quote Decisinon: "Given that Google's existing code is not
> exception-tolerant, the costs of using exceptions are somewhat greater

Anyone has familiarity with Google code? Is it correct and readable? This
statement rings me a "stay away" warning.

> (...) We don't believe that the available alternatives to exceptions,
> such as error codes and assertions, introduce a significant burden."

Assertion is NOT an alternative to exception (or error code).
Having no exception takes us back by 2 decades (almost) to fully-local error
handling and inability to separate concepts. I recall rewriting some old
code to use exception-triggering classes (mostly handling file i/o and
database ops), it shrinked to 20% and became totally readable and obvious at
glance.

> "(...) Because we'd like to use our open-source projects at Google and
> it's difficult to do so if those projects use exceptions, we need to
> advise against exceptions in Google open-source projects as well."
>
> - I really don't know. I'm all for not introducing exceptions into
> existing code bases that are not exception safe. But recommending
> against the use of exceptions for new (sub)projects and modules because
> the existing code base does not use them seems a bit overreacting.

Taking the earlier statement it makes sense -- for Googlers -- if they can't
see so better make darkness the standard.
:-((

- Run-Time Type Information (RTTI): We do not use Run Time Type Information
(RTTI) ... "A query of type during run-time typically means a design
problem."

Hard to decide whether we should laugh or cry. Dealing with polymorphism
has a toolset, dynamic_cast is one good element of it. How about teaching
the ways to pick tools and use REVIEWs to address allegedly "typical
misuse".

- Preincrement and Predecrement: Use prefix form (++i) of the increment and
decrement operators with iterators and other template objects.

The typical guideline states like "use postincrement ONLY when you need the
previous value". Not depending on types involved, or the phase of the moon.
(corollary: "say what you mean, mean what you say".)

- Use of const: ...
the guideline is correct, but the explanation text has fishy elements.

- Variable and Array Initialization: Your choice of = or (). ... the
following are all correct: ... string name("Some Name"); string name = "Some
Name";

Who cares difference of direct init and copy-init after all...

nbutterw...@googlemail.com

unread,
Jan 10, 2009, 7:21:51 PM1/10/09
to
On 9 Jan, 23:21, "Martin T." <0xCDCDC...@gmx.at> wrote:

> I found the arguments in the Google Style Guide quite interesting:
>

> http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showon...

[snip]

> - I really don't know. I'm all for not introducing exceptions into
> existing code bases that are not exception safe. But recommending
> against the use of exceptions for new (sub)projects and modules because
> the existing code base does not use them seems a bit overreacting.

I agree, but consider that users of such a sub-project, for example a
library, will have to deal with exceptions. They means that the code
that uses the sub-project will need to be exception-aware/safe, use
RAII etc. Thus exception related code will spread through the
project.

This reminds me of the fuss people used to make about the use of
const, which has a similar viral effect on code and coding practices.
But use of const won out in the end - I expect the use of exceptions
will do the same.

I thought the remainder of the Google coding standards were clear,
concise and in general pretty good.

Neil Butterworth

Mathias Gaunard

unread,
Jan 10, 2009, 7:29:56 PM1/10/09
to
On 10 jan, 00:21, "Martin T." <0xCDCDC...@gmx.at> wrote:

> - I really don't know. I'm all for not introducing exceptions into
> existing code bases that are not exception safe. But recommending
> against the use of exceptions for new (sub)projects and modules because
> the existing code base does not use them seems a bit overreacting.

This is probably just justification for the fact that they do not want
to learn how to design exception-safe code with RAII.

Hak...@gmail.com

unread,
Jan 10, 2009, 7:28:20 PM1/10/09
to
On Jan 9, 6:21 pm, "Martin T." <0xCDCDC...@gmx.at> wrote:
> I found the arguments in the Google Style Guide quite interesting:
>
> http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showon...

I agree to a point. For example: (pseudo code)

f(x):
if x return 1/x
else throw

Throwing when x is zero is really good here because the user would
have no good way of knowing the return value is bad (if this code was
allowed to continue). However, generally, exceptions are not good at
all. Having an unhandled exception in one module affects every other
module. If I have functions that throw in my trivial module part of a
greater system, and the exception is not caught, even the important
modules kindly stop as the program recognizes the exception.

On the other hand, if the istream threw a little more often, than I
wouldn't see, about once a week, a newbie having it explained to him/
her about how you have to clear the flag, ignore the buffer, and start
again. But, this is actually a problem in interface. If it were easier
to recognize such problems and start again, we might not see beginners
get this wrong so much and the textbooks might be able to explain it
better.

So: I don't think throwing is generally good, and its necessity is due
to bad interface, but when there is no possible other way to inform
the user of a problem, it's good.

On a note about that, though: Some libraries, like OpenGL, instead of
throwing create lists of problems stored in strings. It doesn't crash
my program and it tells me what the problem is so I can just rewrite
the bad code. Not a bad work-a-round!

SG

unread,
Jan 10, 2009, 7:30:32 PM1/10/09
to
On 10 Jan., 00:21, "Martin T." <0xCDCDC...@gmx.at> wrote:
> I found the arguments in the Google Style Guide quite interesting:
> http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showon...

They're not saying exceptions are inherently bad. They are saying that
they think it is a bad idea to add exceptions to an existing project
that has been designed without exceptions in mind. But as far as I can
tell they do acknowledge the benefits of exceptions in case you can
start a new project from scratch.

Cheers!
SG

litb

unread,
Jan 10, 2009, 7:33:20 PM1/10/09
to
{ It's about the Google Style Guide at
<http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Exceptions#Exceptions>.
Please include some quoting yourself to establish context. -mod }

I think the situations is a bit like the one when you have const
methods, but objects that do not adhere to const correctness. You will
not be able to call the non-const methods of those, and need const-
cast. That's additional code, but it's not near as painful as adding
RAII object everywhere to ensure cleanup functions of objects are
called that do not adhere to the RAII/SBRM principle (deallocate in
destructor). And remember you need to do that even tho you only call
throwing code indirectly, possibly not noticing you call possibly
throwing code in the end. With C++1x i think the whole situation will
be somewhat better, because you can do something like

{
ScopeGuard s = [&] { queue.CleanUp(); }
...
}

But now, i think i can understand google's decision. They don't want
to create SBRM objects for each and every situation that need such a
special handling.

nbutterw...@googlemail.com

unread,
Jan 11, 2009, 12:52:02 PM1/11/09
to
On 11 Jan, 00:25, "Balog Pal" <p...@lib.hu> wrote:

Some comments on yours:

> - "Reference Arguments: All parameters passed by reference must be
> labeled
> const. ...
> In fact it is a very strong convention that input arguments are values
> or
> const references while output arguments are pointers. ..."
>
> I'm using C++ since about CFront 2.0, first time to hear this idea,
> and it

I can assure you that there were a lot of guidelines sloshing around
back in CFront days that said you should use references to pass
information and pointers to pass ownership.

> is IMNSHO utter nonsense.

Nonsense maybe, but not _utter_ nonsense :-)

> - Default Arguments: We do not allow default function parameters.
>
> DOH.

Actually, I find myself using defaults less and less as time goes on.
It seems to be often the case that there is subtle semantic difference
between a method that takes one or two (one defaulted) parameters. I
agree a blanket ban is silly.

> - Doing Work in Constructors: Do only trivial initialization in a
> constructor. If at all possible, use an Init() method for non-trivial
> initialization. ... If your object requires non-trivial
> initialization,

I thought they meant factoring out common constructor code into a
private Init() method...

> consider having an explicit Init() method and/or adding a member flag
> that
> indicates whether the object was successfully initialized.

.. but I guess I was wrong. I didn't read this, which is obviously
completely wrong-headed.

> - Run-Time Type Information (RTTI): We do not use Run Time Type
> Information
> (RTTI) ... "A query of type during run-time typically means a design
> problem."
>
> Hard to decide whether we should laugh or cry. Dealing with
> polymorphism
> has a toolset, dynamic_cast is one good element of it. How about
> teaching
> the ways to pick tools and use REVIEWs to address allegedly "typical
> misuse".

This was the main thing in GC guidelines I disagreed with, but to be
fair, I've only started using RTTI in the past 5 years or so myself. I
guess it's too much to expect a company full of Python programmers to
embrace it any quicker!

Neil Butterworth

Balog Pal

unread,
Jan 11, 2009, 12:57:17 PM1/11/09
to

"Metron" <Metr...@gmail.com> az alábbiakat írta a következo
hírüzenetben:
bd98f839-dde7-44a3...@r15g2000prh.googlegroups.com...

> I've been thinking several times of using exceptions in new projects
> (done in C++). But ultimately I decided against because when you see a
> function declaration, you cannot see which exceptions might be thrown
> by that function. You have to read and re-read almost every time you
> want to use that function to be sure to catch the right exceptions.

This sounds like missing the point. I rarely check exceptions
per-function:
normally the *component* defines an exception policy and a set of its
own
exceptions.

And most of the client code shall not bother -- the baseline is to make
functions exception-transparent. You catch only at special places,
where
you need to handle or convert.

Integrating an exceotion-using component also just needs a wrapper, that
converts the exceptions to retcodes if really that is what yo want.

Refusing exceptions in C++ means you refrain using std::, boost::, many
other libs following or using them, and use only nothrow new? Sounds
like a
huge cut.

> Also, IIRC but please correct me, if I'm wrong, under Windows throwing
> exceptions is problematic once it has to cross DLL borders (ie. when
> you've implemented an interface within the DLL and a function within
> the implementation has to throw an exception).

Can't recall any problem there. MFC sits in a DLL, throws a dozen
different
exceptions my code gladly caught...

> That's the point I like in Java: You declare the exceptions the
> function might throw in the function declaration.

Opinion is mixed on that, I recall articles from mentors (including
Bruce
Eckel) to switch to unchecked exceptions in some cases. Observing ill
practices, like catch {} to make code compile.

Ulrich Eckhardt

unread,
Jan 11, 2009, 1:02:18 PM1/11/09
to
Metron wrote:
> I've been thinking several times of using exceptions in new projects
> (done in C++). But ultimately I decided against because when you see a
> function declaration, you cannot see which exceptions might be thrown
> by that function.

So you neither use containers from the standard library or 'new'
without 'nothrow'? Further, you don't use an overloaded 'operator+' to
concatenate strings? Or, FWIW, even constructors for objects that
require
any operations there that might fail?

Any container requires allocation and will throw when that fails, just
as 'new' does. 'operator+' requires allocation, too, but its returnvalue
is
that object already so there is no way to signal that this allocation
failed, safe using a string that can not only contain various amounts of
textual data but also have a 'broken' state. Similarly, passing a string
literal to a function that takes a string type requires an allocation.
Reading a file into memory requires memory and that opening the file
works.

> You have to read and re-read almost every time you
> want to use that function to be sure to catch the right exceptions.

Sorry, but that is the wrong way to think about using exceptions. The
point
is that you usually don't catch them at the point where they escape from
a
function, because usually you can't handle them there anyway. Rather,
you
bundle several operations in a single try-catch clause. Of course, if
you
need cleanup in the middle, you might also catch, cleanup and rethrow
or,
better, use a scope guard.

> Also, IIRC but please correct me, if I'm wrong, under Windows throwing
> exceptions is problematic once it has to cross DLL borders (ie. when
> you've implemented an interface within the DLL and a function within
> the implementation has to throw an exception).

Not that I was aware of.

Uli

Martin T

unread,
Jan 11, 2009, 1:07:39 PM1/11/09
to
Mathias Gaunard wrote:
> On 10 jan, 00:21, "Martin T." <0xCDCDC...@gmx.at> wrote:
>
>> - I really don't know. I'm all for not introducing exceptions into
>> existing code bases that are not exception safe. But recommending
>> against the use of exceptions for new (sub)projects and modules
>> because
>> the existing code base does not use them seems a bit overreacting.
>
> This is probably just justification for the fact that they do not want
> to learn how to design exception-safe code with RAII.

I think it's unfair to blame them "that they do not want to learn".
You see, I do not agree with their arguments against exceptions, but
teaching new staff the right use of exception is a non-trivial affair.
At our shop my sup. is against the use of exceptions for the very reason
that he thinks our devs can't use them properly (esp. with regd to
RAII). As things are I think he's mostly right. Many devs come out of
uni/college having done some basic C/C++ (read: "C with classes") and
some Java programming and then continue to program "C with classes".
The fear is also, that even if a dev has understood the concepts to
write exception-correct code in C++ he still can break existing code
because that legacy code was not exception save.

I would have thought though that Google at least should have the will,
time and resources to bring their developers to a decent language level
... either that guide is not for their employees or they just stumble
along as everyone else :-)

br,
Martin

Martin T

unread,
Jan 11, 2009, 2:34:21 PM1/11/09
to
Hak...@gmail.com wrote:
> On Jan 9, 6:21 pm, "Martin T." <0xCDCDC...@gmx.at> wrote:
>> http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showon...
>
> I agree to a point. For example: (pseudo code)
>
> f(x):
> if x return 1/x
> else throw
>
> Throwing when x is zero is really good here because the user would
> have no good way of knowing the return value is bad (if this code was
> allowed to continue).

Kind of bad example, because:
int main(int, char*[]) {
using namespace std;
double x = 0.0;
double inf = 1/x;
cout << inf << endl; // prints: 1.#INF
return 0;
}
( but maybe this is impl. specific, I'm never too sure with that FP stuff)

> However, generally, exceptions are not good at
> all. Having an unhandled exception in one module affects every other
> module. If I have functions that throw in my trivial module part of a
> greater system, and the exception is not caught, even the important
> modules kindly stop as the program recognizes the exception.
>

Well, this is certainly *not* an argument against exceptions. Because
C++ has quite a few other situations that will result in std::terminate
(or whatever). It's just a buggy module if it generates an unhandled
exceptions, just as it's a bugy module if it does a nullptr access ...

br,
Martin

Bo Persson

unread,
Jan 11, 2009, 2:35:41 PM1/11/09
to
Hak...@gmail.com wrote:
> On Jan 9, 6:21 pm, "Martin T." <0xCDCDC...@gmx.at> wrote:
>> I found the arguments in the Google Style Guide quite interesting:
>>
>> http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showon...
>
> I agree to a point. For example: (pseudo code)
>
> f(x):
> if x return 1/x
> else throw
>
> Throwing when x is zero is really good here because the user would
> have no good way of knowing the return value is bad (if this code
> was allowed to continue). However, generally, exceptions are not
> good at all. Having an unhandled exception in one module affects
> every other module. If I have functions that throw in my trivial
> module part of a greater system, and the exception is not caught,
> even the important modules kindly stop as the program recognizes
> the exception.

Perhaps they should, rather than trying to increase the power output
to 1/x, for a bad x.

Otherwise, catching the exeption at the module boundary might be a
good idea. Maybe it can be handled there, or translated into something
that the higher lever modules can handle.

>
> On the other hand, if the istream threw a little more often, than I
> wouldn't see, about once a week, a newbie having it explained to
> him/ her about how you have to clear the flag, ignore the buffer,
> and start again. But, this is actually a problem in interface. If
> it were easier to recognize such problems and start again, we might
> not see beginners get this wrong so much and the textbooks might be
> able to explain it better.
>
> So: I don't think throwing is generally good, and its necessity is
> due to bad interface, but when there is no possible other way to
> inform the user of a problem, it's good.

The user isn't supposed to be the recipient of the exception, but
higher level code. The code might actually know what to do about the
problem. Informing the user is one option, in case there is a user
present.

>
> On a note about that, though: Some libraries, like OpenGL, instead
> of throwing create lists of problems stored in strings. It doesn't
> crash my program and it tells me what the problem is so I can just
> rewrite the bad code. Not a bad work-a-round!

OpenGL is a special case, where bad output is sometimes acceptable.
The image looks a bit strange, but the application can often continue
anyway. The next couple of frame updates might hide the problem.

This is not generally the case.


Bo Persson

Mathias Gaunard

unread,
Jan 11, 2009, 2:37:13 PM1/11/09
to
On 11 jan, 01:25, "Balog Pal" <p...@lib.hu> wrote:

> - "Reference Arguments: All parameters passed by reference must be labeled
> const. ...
> In fact it is a very strong convention that input arguments are values or
> const references while output arguments are pointers. ..."
>
> I'm using C++ since about CFront 2.0, first time to hear this idea, and it
> is IMNSHO utter nonsense.

It's discutable, but not utter nonsense.
Personally I avoid usually arguments as output altogether, except for
output iterators.


> - Default Arguments: We do not allow default function parameters.
>
> DOH.

Such functionality can be attained with overloading and usually leads
to more efficient code.
Also, if your default constructors can't work properly, you don't have
much of a choice.


> - Doing Work in Constructors: Do only trivial initialization in a
> constructor. If at all possible, use an Init() method for non-trivial
> initialization. ... If your object requires non-trivial initialization,
> consider having an explicit Init() method and/or adding a member flag that
> indicates whether the object was successfully initialized.
>
> I recon most mentors say to do 2 phase init only when absolutely necessary
> for data flow or use cases (i.e. you need instances before knowing params).
> Make it based on "amount of work"?

No exception implies two-phase initialization, which implies previous
point.


> Anyone has familiarity with Google code? Is it correct and readable? This
> statement rings me a "stay away" warning.

Yes, me too.

>
> > (...) We don't believe that the available alternatives to exceptions,
> > such as error codes and assertions, introduce a significant burden."
>
> Assertion is NOT an alternative to exception (or error code).

Unfortunately, few people use assertions and exceptions in the right
places.
Assertions are programming errors which should *never* happen and are
bugs. That is why it is good to disable them once the software has
been sufficiently tested.
Exceptions are recoverable errors which shouldn't happen in the normal
execution flow, but may well happen.

The common mistake, however, is usually the opposite: using exceptions
for things that really ought to be assertions.


> - Run-Time Type Information (RTTI): We do not use Run Time Type Information
> (RTTI) ... "A query of type during run-time typically means a design
> problem."
>
> Hard to decide whether we should laugh or cry. Dealing with polymorphism
> has a toolset, dynamic_cast is one good element of it. How about teaching
> the ways to pick tools and use REVIEWs to address allegedly "typical
> misuse".

Code that downcasts is badly designed, yes.
A chain of dynamic_cast doesn't scale, you should use some kind of
visitor pattern.


> Who cares difference of direct init and copy-init after all...

Some types are not copiable (some are even non movable).
Some types have explicit constructors.

Bart van Ingen Schenau

unread,
Jan 11, 2009, 2:37:16 PM1/11/09
to
Metron wrote:

> I've been thinking several times of using exceptions in new projects
> (done in C++). But ultimately I decided against because when you see a
> function declaration, you cannot see which exceptions might be thrown
> by that function. You have to read and re-read almost every time you
> want to use that function to be sure to catch the right exceptions.

And how are you sure that you are handling the right error-codes?
Those are also not part of the function declaration.

And if you say you just read the function documentation, remember that
you should also be able to find in the exact same place which
exceptions a function might throw.

> Have fun,
> Metron
>
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

Mathias Gaunard

unread,
Jan 11, 2009, 2:37:18 PM1/11/09
to
On 11 jan, 01:22, Metron <Metro...@gmail.com> wrote:
> I've been thinking several times of using exceptions in new projects
> (done in C++). But ultimately I decided against because when you see a
> function declaration, you cannot see which exceptions might be thrown
> by that function.

Why do you need to know?
You shouldn't need to know what a function may throw unless you want
to do something meaningful in those specific cases.

The only thing you need to know is whether a function is nothrow and
not; and still, most code shouldn't need to know that some specific
action is nothrow.


> You have to read and re-read almost every time you
> want to use that function to be sure to catch the right exceptions.

If you want to catch the exceptions, that's probably because you want
to handle specific errors that may happen during their execution. If
you know what specific errors may happen and want to handle them, that
probably means that you have read the documentation; said documention
which should mention which exception types may be thrown.

What exceptions may be thrown may be documented as comments too, which
gives you the same information as what you'd like.


> Also, IIRC but please correct me, if I'm wrong, under Windows throwing
> exceptions is problematic once it has to cross DLL borders

DLLs and similar mechanisms were usually designed for C.
Of course, you cannot use C++ mechanisms across mechanisms designed
for another language without those mechanisms...

This isn't really a problem though, just catch any exception and
encode it into another format to pass it.


> That's the point I like in Java: You declare the exceptions the
> function might throw in the function declaration.

That's why most people will use RuntimeException to get around that
annoying mechanism.
Checked exceptions are a highly criticized thing.

Bo Persson

unread,
Jan 11, 2009, 2:35:39 PM1/11/09
to
Metron wrote:
> I've been thinking several times of using exceptions in new projects
> (done in C++). But ultimately I decided against because when you
> see a function declaration, you cannot see which exceptions might
> be thrown by that function. You have to read and re-read almost
> every time you want to use that function to be sure to catch the
> right exceptions.

Unlike when it returns error codes? :-)

How do you know what codes to check for?


Bo Persson

Bart van Ingen Schenau

unread,
Jan 11, 2009, 2:34:24 PM1/11/09
to
Balog Pal wrote:

>
> "Martin T." <0xCDC...@gmx.at> :
>>I found the arguments in the Google Style Guide quite interesting:
>>
>>
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Exceptions#Exceptions
>
> I read through the guide, it has a couple items that is completely
> nuts:
>
> - "Reference Arguments: All parameters passed by reference must be
> labeled const. ...
> In fact it is a very strong convention that input arguments are values
> or const references while output arguments are pointers. ..."
>
> I'm using C++ since about CFront 2.0, first time to hear this idea,
> and it is IMNSHO utter nonsense.

For me, it is not the first time I hear it (though I don't adhere to
it).
The idea is that, at the call-site you can more easily identify the
arguments that will/should be changed by the function without having to
lookup the prototype. This is because, like in C, the out-parameters
are identified by the use of the operator& at the call site.

For me, the argument is not strong enough to include the rule in any
style guide I am responsible for, but I will follow it if someone else
insist on it.

<snip>

> - Doing Work in Constructors: Do only trivial initialization in a
> constructor. If at all possible, use an Init() method for non-trivial
> initialization. ... If your object requires non-trivial
> initialization, consider having an explicit Init() method and/or
> adding a member flag that indicates whether the object was
> successfully initialized.
>
> I recon most mentors say to do 2 phase init only when absolutely
> necessary for data flow or use cases (i.e. you need instances before
> knowing params). Make it based on "amount of work"?

I see this as a consequence of not allowing exceptions.
If you can't use exceptions to report errors from constructors, you
simply can't avoid two-phase construction if the object requires a
fallible operation on construction. And any non-trivial initialisation
can possibly fail.

<snip>

> - Run-Time Type Information (RTTI): We do not use Run Time Type
> Information (RTTI) ... "A query of type during run-time typically
> means a design problem."
>
> Hard to decide whether we should laugh or cry. Dealing with
> polymorphism
> has a toolset, dynamic_cast is one good element of it. How about
> teaching the ways to pick tools and use REVIEWs to address allegedly
> "typical misuse".

I am not sure what you mean with this comment, but that might be due to
a different interpretation of the phrase "use RTTI".
To me, that phrase only means the explicit use of type_info objects (for
other purposes than printing a typename as a diagnostic tool). But I am
aware that some people also include any use of dynamic_cast in their
meaning of the phrase "use RTTI".

Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Balog Pal

unread,
Jan 12, 2009, 2:27:54 PM1/12/09
to

<nbutterw...@googlemail.com> :
>> "Balog Pal" <p...@lib.hu>

>> - "Reference Arguments: All parameters passed by reference must be
>> labeled const. ...
>> In fact it is a very strong convention that input arguments are values
>> or const references while output arguments are pointers. ..."
>>
>> I'm using C++ since about CFront 2.0, first time to hear this idea,
>> and it
>
> I can assure you that there were a lot of guidelines sloshing around
> back in CFront days that said you should use references to pass
> information and pointers to pass ownership.

Pass ownership: sure, that is done by passing pointers. Also pointers are
used to have NULL available as special/missing/... value.

This point is not about that, but about [out] or [inout] params. For that I
(and I thought everyone) use nonconst reference. Why on earth would I pass
a pointer to a mandatory output param, allowing someone to pass NULL?

It is also self-documenting: param is nonconst-ref: the function will
modify that object. nonconst pointer: either an opotional output param (to
capture auxillary info) or a passing the pointer for ownership
transfer/registering for later use/...

>> is IMNSHO utter nonsense.
> Nonsense maybe, but not _utter_ nonsense :-)

Not convinced yet. :)

Seungbeom Kim

unread,
Jan 12, 2009, 2:34:12 PM1/12/09
to
Bart van Bingen Schenau wrote:

> Balog Pal wrote:
>>
>> - "Reference Arguments: All parameters passed by reference must be
>> labeled const. ...
>> In fact it is a very strong convention that input arguments are values
>> or const references while output arguments are pointers. ..."
>>
>> I'm using C++ since about CFront 2.0, first time to hear this idea,
>> and it is IMNSHO utter nonsense.
>
> For me, it is not the first time I hear it (though I don't adhere to
> it).
> The idea is that, at the call-site you can more easily identify the
> arguments that will/should be changed by the function without having to
> lookup the prototype. This is because, like in C, the out-parameters
> are identified by the use of the operator& at the call site.

A big flaw with this logic is that, since the C days, many in-parameters
have been also identified by & at the call site, to avoid call-by-value
(ex. localtime), though new functions written in C++ will more likely
use (const) references instead.

On the other hand, absence of & doesn't tell you that the parameter
won't be changed; strcpy(a, b) says nothing about which one will be
changed and which one won't. Upon meeting swap(a, b) or increment(x),
you will instantly recognise that they will all be changed.

In other cases, what you have received from outside may be a pointer p
from the beginning, and if you want to pass it to a function f that
also takes a pointer form, you'll have to use f(p), not f(&p), no matter
whether f changes the object pointed to by the pointer.

The conclusion that I came to is that using & at the call site to
distinguish in-parameters and out-parameters doesn't work well, and
you really cannot dispense with understanding what the function does
with the arguments.

This has been discussed many times in this group, including one in
March 2005.

--
Seungbeom Kim

David Abrahams

unread,
Jan 12, 2009, 2:32:07 PM1/12/09
to

on Sun Jan 11 2009, Martin T <0xCDCDCDCD-AT-gmx.at> wrote:

> Mathias Gaunard wrote:
>> On 10 jan, 00:21, "Martin T." <0xCDCDC...@gmx.at> wrote:
>>
>>> - I really don't know. I'm all for not introducing exceptions into
>>> existing code bases that are not exception safe. But recommending
>>> against the use of exceptions for new (sub)projects and modules because
>>> the existing code base does not use them seems a bit overreacting.
>>
>> This is probably just justification for the fact that they do not want
>> to learn how to design exception-safe code with RAII.
>
> I think it's unfair to blame them "that they do not want to learn".
> You see, I do not agree with their arguments against exceptions, but
> teaching new staff the right use of exception is a non-trivial affair.
> At our shop my sup. is against the use of exceptions for the very reason
> that he thinks our devs can't use them properly (esp. with regd to
> RAII). As things are I think he's mostly right.

Most devs don't handle errors properly no matter what tools you give
them. At least exceptions make it easier to do the job right.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Goran

unread,
Jan 12, 2009, 2:37:00 PM1/12/09
to
> You shouldn't need to know what a function may throw unless you want
> to do something meaningful in those specific cases.
>
> The only thing you need to know is whether a function is nothrow and
> not; and still, most code shouldn't need to know that some specific
> action is nothrow.

+1. And even knowing whether some specific function is nothrow is bad
for maintenance (that is, as code evolves, it __will__ go from nothrow
to throw ;-) ).

IMO, the baseline rule is: everything throws, except primitive type
assignments and a small set of operations, most restrictive set being
simply one thing: a non-throwing swap operation on a class. That's a
simple-enough base to reason about exception safety.

David Abrahams

unread,
Jan 12, 2009, 2:34:08 PM1/12/09
to

on Sun Jan 11 2009, nbutterworth1953-AT-googlemail.com wrote:

>> Hard to decide whether we should laugh or cry. Dealing with
>> polymorphism has a toolset, dynamic_cast is one good element of
>> it. How about teaching the ways to pick tools and use REVIEWs to
>> address allegedly "typical misuse".
>
> This was the main thing in GC guidelines I disagreed with, but to be
> fair, I've only started using RTTI in the past 5 years or so myself. I
> guess it's too much to expect a company full of Python programmers to
> embrace it any quicker!

The cultures of Python and C++ are so different that I'm sometimes
amazed that I manage to be both a C++ and a Python programmer at once.
Just take a look at this thread:
http://markmail.org/message/vvhahc6ccgda3nkt

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Le Chaud Lapin

unread,
Jan 12, 2009, 2:30:50 PM1/12/09
to
On Jan 11, 12:02 pm, Ulrich Eckhardt <dooms...@knuut.de> wrote:

First of all, I was appalled that Google prescribes no exceptions and
no non-const references.

> Any container requires allocation and will throw when that fails, just
> as 'new' does. 'operator+' requires allocation, too, but its returnvalue
> is
> that object already so there is no way to signal that this allocation
> failed, safe using a string that can not only contain various amounts of
> textual data but also have a 'broken' state. Similarly, passing a string
> literal to a function that takes a string type requires an allocation.
> Reading a file into memory requires memory and that opening the file
> works.

Yep.

Ironically, from an aesthetic point of view, exceptions and non-const-
references are two of the most liberating features of C++. They allow
a mode of thinking that is simply impossible in their absence.

It should be a goal, in any design, new code or not, to find good
form, and both these features, exceptions in particular, facilitate
finding good form, IMO. I would be curious to see how Google's object-
cannot-construct-itself looks. There is one possible answer if
exceptions are disallowed: ugly, tedious, and one _still_ does not
know what to do in awkward situations.

And the argument they give for avoiding non-const references...

"References can be confusing, as they have value syntax but pointer
semantics. "

...is highly subjective.

To whom is it confusing? I doubt that there are many people in this
newsgroup that would agree with this statement.

It's like saying that foreigners [w.r.t. USA] should not use
subjunctives because most people in USA have trouble understanding
subjuncitves. It is a riduculous proposition that reeks of
intellectual communism.

I would prefer that Google reconsidered. ;)

-Le Chaud Lapin-

Mathias Gaunard

unread,
Jan 13, 2009, 12:16:35 AM1/13/09
to
On 12 jan, 20:34, David Abrahams <d...@boostpro.com> wrote:

> The cultures of Python and C++ are so different that I'm sometimes
> amazed that I manage to be both a C++ and a Python programmer at once.
> Just take a look at this thread:http://markmail.org/message/vvhahc6ccgda3nkt

I'll be straying a bit off-topic here, but I hope this can still be
interesting, and why not bring the Python or other dynamic-languages-
loving masses to C++.

You can code Python-style in C++ (and quite more efficiently) without
much hassle. It's just dynamic duck typing, nothing hard to do,
depending on how well you want it to play with the C++ type system.
I will here talk about how to make it integrate with the existing type
system well.

If you only want to use it on a finite set of types, you can just use
a variant; everytime you do something (like call a method) you visit
your variant, then check whether the call is valid at compile-time
(with SFINAE for expressions, for example): if it is, perform the
action, otherwise raise a runtime error.
It would be possible to do such a thing without a constrained list of
types, but that would require a feature virtually equivalent to
template virtual functions (and certainly, that would make C++ so
versatile it would be the dawn of a new age for the language).

Note, however, that it working on a finite set of types is enough to
express anything you would express in Python: it just won't integrate
too easily with different, non-duck using, C++ code.
Here is, for example, a possible variant (the "variable" type) in ML
syntax, quite representative of dynamic languages and how they are
implemented (it's actually more like PHP, since our arrays are maps):
type variable = Object of object | Function of (variable list ->
variable) | Procedure of (variable list -> unit)
| Int of int | Float of float | Array of (variable,
variable) map | Resource of int ... other native types
and
type object = (string, variable) map

Now here is what a generic duck typing system can look like in C+
+(0x).
Unfortunately, without proper operator. (dot) overloading (and by
proper I mean real name overloading, not just forwarding), or at least
polymorphic lambdas, it's just not practical to use.
We can have something that works like this:

duck<T1, T2, ..., Tn> variable = T1();
variable.call(lambda_method(arg1, arg2, ..., argn));

with

struct lambda_method
{
Arg1 arg1; // Argi being the type of argi
Arg2 arg2;
...
Argn argn;

// the constructor you expect
// ...

template<typename T>
auto operator()(T&& o) -> decltype(o.method(arg1, arg2, ...,
argn))
{
return o.method(arg1, arg2, ..., argn);
}
};

we'd really like to just write
variable.call([&](o) { return o.method(arg1, arg2, ..., argn); });

or ideally the shorter
variable.method(arg1, arg2, ..., argn);

Unfortunately, everytime I offered a mechanism to provide the
necessary operator. overloading on comp.std.c++ (which is simply
making the last line equivalent to the previous one), there was lack
of interest.

--

Kenneth Porter

unread,
Jan 13, 2009, 12:17:53 AM1/13/09
to
"Balog Pal" <pa...@lib.hu> wrote in news:gkcfs1$19kh$1...@news.ett.com.ua:

> Integrating an exceotion-using component also just needs a wrapper, that
> converts the exceptions to retcodes if really that is what yo want.

> Can't recall any problem there. MFC sits in a DLL, throws a dozen
> different exceptions my code gladly caught...

I haven't run into issues with DLLs, provided that all modules use the same
runtime DLL and compiler options.

However, I have run into a bug in VC6 (now retired) when writing an
exception-to-error-code dispatcher using "try { throw; } catch (exception1
&) {} catch (exception2&) {} ...". VC6 will call the exception's destructor
twice, which means you can't use an exception based on std::exception
(which contains a string). The bug trips when attempting to rethrow from a
catch handler.

Instead, I found it necessary to use an RTTI-based dispatcher, assigning
the exception passed by base class reference (ie. std::exception) and
dynamic_cast to each candidate type to find the one that matches a given
error code.

I think most MS users didn't encounter this because MFC encouraged
programmers to throw new'd exception objects and to delete the exception at
the point of catch. As with all scalars, calling a pointer's destructor
twice has no effect.

Le Chaud Lapin

unread,
Jan 13, 2009, 12:24:01 AM1/13/09
to
On Jan 11, 11:57 am, "Balog Pal" <p...@lib.hu> wrote:
> "Metron" <Metro...@gmail.com> az alábbiakat írta a következo
> hírüzenetben:
> bd98f839-dde7-44a3-89d3-134e3f325...@r15g2000prh.googlegroups.com...

> > Also, IIRC but please correct me, if I'm wrong, under Windows throwing
> > exceptions is problematic once it has to cross DLL borders (ie. when
> > you've implemented an interface within the DLL and a function within
> > the implementation has to throw an exception).
>
> Can't recall any problem there. MFC sits in a DLL, throws a dozen
> different
> exceptions my code gladly caught...

Metron might be refering to the heap mismatch problem, where exception
allocates memory during construction of its object using new, the new
that is inside DLL, then EXE attempts to deallocate that same memory
using delete, the delete that is inside EXE. Boom!

This problem is easily solved by making the destructor virtual. In
fact, it solves the general problem where memory allocated by
constructor in one module is deallocated by destructor in different
module. A virtual destructor forces invocation of the delete code that
is actually inside the module containing the new that allocated the
memory from their respective heap.

I often get into fights with C++ jocks who insist, rather emphatically
I might add, that I am wrong about this without checking for
themselves.

-Le Chaud Lapin-

Andrei Alexandrescu

unread,
Jan 13, 2009, 12:21:52 AM1/13/09
to
Le Chaud Lapin wrote:
> On Jan 11, 12:02 pm, Ulrich Eckhardt <dooms...@knuut.de> wrote:
>
> First of all, I was appalled that Google prescribes no exceptions and
> no non-const references.
>
>> Any container requires allocation and will throw when that fails, just
>> as 'new' does. 'operator+' requires allocation, too, but its returnvalue
>> is
>> that object already so there is no way to signal that this allocation
>> failed, safe using a string that can not only contain various amounts of
>> textual data but also have a 'broken' state. Similarly, passing a string
>> literal to a function that takes a string type requires an allocation.
>> Reading a file into memory requires memory and that opening the file
>> works.
>
> Yep.
>
> Ironically, from an aesthetic point of view, exceptions and non-const-
> references are two of the most liberating features of C++. They allow
> a mode of thinking that is simply impossible in their absence.

(Focusing on exceptions) I agree in spirit, but at the same time I wish
there was a clearer advantage of exception-using code. If there were, I
have no doubt Google would be willing to adopt that technological
advantage. They have a great deal of smart and knowledgeable people, and
at least according to my friends who work there, the culture is rather
open to good ideas. Sadly, we still have to convince ourselves as a
community that C++ code using exceptions is significantly simpler, more
modular, and easier to maintain.

Aside from the commonly mentioned issues, I think (and shared that
thought with this newsgroup) that "try" introducing a new scope is a
rather unfortunate choice, and that RAII fosters proliferation of types
- and types are not easy to define in C++. Also, exceptions have a few
ancillary issues: exception specs are statically declared but
dynamically checked; recursive throws are handled rather bluntly by
terminating the program; std exceptions are polymorphic but don't offer
cloning or visitation primitives, two rather essential ingredients. All
in all, not a very compelling package. In the words of Chris Rock, "Does
that mean I agree? No. But I understand!"


Andrei

Le Chaud Lapin

unread,
Jan 13, 2009, 7:19:51 PM1/13/09
to
On Jan 12, 11:21 pm, Andrei Alexandrescu
<SeeWebsiteForEm...@erdani.org> wrote:

> Le Chaud Lapin wrote:
> (Focusing on exceptions) I agree in spirit, but at the same time I wish
> there was a clearer advantage of exception-using code. If there were, I
> have no doubt Google would be willing to adopt that technological
> advantage. They have a great deal of smart and knowledgeable people, and
> at least according to my friends who work there, the culture is rather
> open to good ideas. Sadly, we still have to convince ourselves as a
> community that C++ code using exceptions is significantly simpler, more
> modular, and easier to maintain.

What do you do in your personal coding regarding exceptions?

For me, the need is summed up in one word: form. When I design a
system, form supercedes everything.

I shamelessly admit that I will write an entire program with hardly
any (real) error checking to get the form right, then backtrack and
add code for error checking. I do this because premature error-
checking tends to impede the thought process. I prefer to begin with
idealistic expectations since, in a perfect world, perfect form will
be achieved when a system is unencumbered by inherently imperfect
error checking. However, as I go along, I make small notes places
where things can go wrong. I write "// DEFECT..." where error codes
should be returned, and

throw 0;

...where exceptions should be thrown.

Then I backtrack in my perfect-system-but-no-error-checking, grepping
for these notes. As expected, I discover special places where no
amount of error-checking whatsoever will preserve the beauty of form
that has been created by ignoring it in favor of exceptions.

Here is example of big-integer division code that could be buried deep
within a cryptographic operation:

Integer operator / (const Integer &dividend, const Integer &divisor)
{
Integer quotient, remainder;
if (!Integer::divide (dividend, divisor, quotient, remainder))
throw Integer::DIVISION_BY_ZERO;
return quotient;
}

Integer::divide() returns true for success, false for failure, but
operator / would lose all its beauty if it had to return an error:

My entire system is littered with examples like this, places where the
form would be ruined without exceptions. And in places where I
exceptions could be replaced with error codes, the result would be a
mess, because I would still have to check for errors, only I would be
forced to check right then, right there, deep within the abyss, even
though I would not have a clue of how to proceed. This would force
every piece of code in the the call chain to be prepared to deal with
errors beneath it, creating irregularity everywhere. With exceptions,
I get to pretend everything is mostly perfect, and simply worry about
resource reclamation, which is very easy in my world, as I avoid
unwarranted proliferation of polymorphic objects.

> Aside from the commonly mentioned issues, I think (and shared that
> thought with this newsgroup) that "try" introducing a new scope is a
> rather unfortunate choice, and that RAII fosters proliferation of types
> - and types are not easy to define in C++. Also, exceptions have a few
> ancillary issues: exception specs are statically declared but
> dynamically checked; recursive throws are handled rather bluntly by
> terminating the program; std exceptions are polymorphic but don't offer
> cloning or visitation primitives, two rather essential ingredients. All
> in all, not a very compelling package. In the words of Chris Rock, "Does
> that mean I agree? No. But I understand!"

Hmm..didn't know that std exceptions are polymorphic. Again, overuse
of polymorphism rears its ugly head, IMO.

As far as type proliferation goes, I have always said that defining
new classes is the narcotic of C++.

I believe the relation:

y = number-of-C++-classes-defined(number-of-systems-built)

should be a logarithmic:

y = C * ln(number-of-systems built)

If it is not logarithmic, if means that we have not been finding
classes whose persistence attest to their virtue. For example,
millions, if not billions, of lines of C++ have been written, and we
still do not have obviously fundamental classes like hierarchies and
associative hierarchies.

A more linear relationship might also mean that the concept-space
might be malformed in minds of those who feel need to create types.
All of us is familiar with the engineer down the hall who thinks that
affixing "C" to the beginning of any word justifies its definition as
a class:

class CCheckerForNullPointer {} ;

One day it might help us to stop creating C++ libraries and instead
create a taxonomy of what classes should be in a feature-complete C++
library to bring about this logarithmic relationship.

-Le Chaud Lapin-

Maxim Yegorushkin

unread,
Jan 13, 2009, 7:25:19 PM1/13/09
to
On Jan 11, 12:25 am, "Balog Pal" <p...@lib.hu> wrote:
> "Martin T." <0xCDCDC...@gmx.at> :

>
> >I found the arguments in the Google Style Guide quite interesting:
>
> >http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showon...

>
> I read through the guide, it has a couple items that is completely nuts:
>
> - "Reference Arguments: All parameters passed by reference must be labeled
> const. ...
> In fact it is a very strong convention that input arguments are values or
> const references while output arguments are pointers. ..."
>
> I'm using C++ since about CFront 2.0, first time to hear this idea, and it
> is IMNSHO utter nonsense.

This is, in fact, the convention I like to use.

The reason for passing output parameters by pointer is that the
ampersand at the call site gives you a hint that something is done to
that argument.

Just to make clear, I don't find useful the idea of references-are-
better-pointers-and-cant-be-null.

[]

> - Doing Work in Constructors: Do only trivial initialization in a
> constructor. If at all possible, use an Init() method for non-trivial
> initialization. ... If your object requires non-trivial initialization,
> consider having an explicit Init() method and/or adding a member flag that
> indicates whether the object was successfully initialized.

> I recon most mentors say to do 2 phase init only when absolutely necessary
> for data flow or use cases (i.e. you need instances before knowing params).
> Make it based on "amount of work"?

Agree on that.

Now I have to check whether a class has Init() member function and
call it. Boring and error-prone.

> - (here comes your observed) Exceptions: We do not use C++ exceptions.
>
> > quote Con(3): "Exception safety requires both RAII and different coding
> > practices. ...
> > - Of course. When people are not using exceptions, they are usually just
> > ignoring the error return codes, so no need for a no-fail commit phase.
> > ::-)
>
> Yea++. RAII and SEME-tolerant layout serves better readability and
> correctness. Wrt anything, including exceptions.

Yep, scoped resource management (aka RAII) makes your code more robust
in the face of errors, and, in general, is way more superior to
garbage collection and try/finally constructs. It does require some
discipline and consistency, however, the results are well worth it.

> > quote Decisinon: "Given that Google's existing code is not
> > exception-tolerant, the costs of using exceptions are somewhat greater


>
> Anyone has familiarity with Google code? Is it correct and readable? This
> statement rings me a "stay away" warning.

One thing for sure: reading google's C++ code won't make you a better
programmer. The same holds true for reading their coding standard.

http://code.google.com/p/google-glog/source/browse/trunk/src/glog/logging.h.in

> > "(...) Because we'd like to use our open-source projects at Google and
> > it's difficult to do so if those projects use exceptions, we need to
> > advise against exceptions in Google open-source projects as well."


>
> > - I really don't know. I'm all for not introducing exceptions into
> > existing code bases that are not exception safe. But recommending
> > against the use of exceptions for new (sub)projects and modules because
> > the existing code base does not use them seems a bit overreacting.
>

> Taking the earlier statement it makes sense -- for Googlers -- if they can't
> see so better make darkness the standard.
> :-((

In my experience, corporations have difficulty hiring quality
developers in numbers. Thus, they lower quality standards and use
"easy" languages like C# and Java.

--
Max

Le Chaud Lapin

unread,
Jan 13, 2009, 7:23:16 PM1/13/09
to
On Jan 10, 6:28 pm, "Hak...@gmail.com" <Hak...@gmail.com> wrote:
> On Jan 9, 6:21 pm, "Martin T." <0xCDCDC...@gmx.at> wrote:
> On a note about that, though: Some libraries, like OpenGL, instead of
> throwing create lists of problems stored in strings. It doesn't crash
> my program and it tells me what the problem is so I can just rewrite
> the bad code. Not a bad work-a-round!

I have seen code where roughly 10% of all lines were perror's() and
printf's(), potentially complaining about things that could not
possibly occur if programmer would simply structure the code so that
they could not, at which point, the programmer would become an
engineer rather than a genial confidant to whom the program provides a
detailed report of just how bad it is written...by said programmer,
who, having written the code, should already know.

if (arg < 0)
perror ("Negative value passed to function when positive value
expected!!!"); // silly, IMO :)

-Le Chaud Lapin-


--

Marsh Ray

unread,
Jan 13, 2009, 8:32:23 PM1/13/09
to
On Jan 12, 1:32 pm, David Abrahams <d...@boostpro.com> wrote:
> ...

>
> Most devs don't handle errors properly no matter what tools you give
> them. At least exceptions make it easier to do the job right.

They also tend to convert silent failures into "in your face" crashes
during development and testing. I think most of us would feel that
this is a good thing.

Most development teams seem to at heart prefer the silent kind of
failures though, but will never admit it. These are the ones that end
up with a mix of error codes, return bools variously indicating
'success' or 'failure', and exceptions (some of which come straight
from vanilla C++).

Invariably, the return codes are checked imperfectly, while the
exceptions do their job as designed. People like to blame the
messenger I guess, and exceptions take a bad rap rather than the
underlying causes.

Recent example: http://www.openssl.org/news/secadv_20090107.txt

- Marsh


--

Marsh Ray

unread,
Jan 13, 2009, 8:33:08 PM1/13/09
to
On Jan 12, 11:21 pm, Andrei Alexandrescu
<SeeWebsiteForEm...@erdani.org> wrote:
> ...

> (Focusing on exceptions) I agree in spirit, but at the same time I wish
> there was a clearer advantage of exception-using code.
> ...

How about:

In short, consistent use of exceptions can cut out 50% of dead, buggy
from your codebase:

Error handling code tends to be itself error prone, poorly maintained,
and under-tested. It tends to get mixed in throughout the program on a
line-by-line basis.

C-language APIs typically report failures via return values and status
variables, necessitating conditional checks and error handler blocks
after every significant function call. Use such APIs correctly can
lead to more than half of a program's code being devoted to error
detection and handling.

The vast majority of the time no error is encountered. Therefore, the
vast majority of error handling code is completely dead. Most dead
code is of little value. Instead it acts to obscure that code which
does have value in expressing the programs essential operations.

In addition to degrading the S/N of the program's source, it can
consume valuable computational resources.

Typically the caller has two options for handling a failed call: (1)
it can fail itself, or (2) it can make some modifications and retry
the operation (after waiting some time, freeing some resources, etc.).
The vast majority of places in code where errors can be detected don't
have the ability or motivation to attempt (2).

Exceptions, properly used, make the (1) case automatic and
transparent.

- Marsh

Kenneth Porter

unread,
Jan 13, 2009, 8:31:12 PM1/13/09
to
Le Chaud Lapin <jaibu...@gmail.com> wrote in news:1f656241-ba00-44c7-
9dba-932...@f33g2000vbf.googlegroups.com:

> This problem is easily solved by making the destructor virtual. In
> fact, it solves the general problem where memory allocated by
> constructor in one module is deallocated by destructor in different
> module. A virtual destructor forces invocation of the delete code that
> is actually inside the module containing the new that allocated the
> memory from their respective heap.
>
> I often get into fights with C++ jocks who insist, rather emphatically
> I might add, that I am wrong about this without checking for
> themselves.

That surprises me. I would have thought that the new/delete would be those
of the call sites, not the object definition sites. (Unless the class
overloads new and delete.)

Mathias Gaunard

unread,
Jan 14, 2009, 1:37:42 PM1/14/09
to
On 14 jan, 01:19, Le Chaud Lapin <jaibudu...@gmail.com> wrote:

> Here is example of big-integer division code that could be buried deep
> within a cryptographic operation:
>
> Integer operator / (const Integer &dividend, const Integer &divisor)
> {
> Integer quotient, remainder;
> if (!Integer::divide (dividend, divisor, quotient, remainder))
> throw Integer::DIVISION_BY_ZERO;
> return quotient;
>
> }
>
> Integer::divide() returns true for success, false for failure, but
> operator / would lose all its beauty if it had to return an error:

Why not throw directly from Integer::divide?
You could also return a tuple (here, a pair) rather than taking
objects to modify by reference.

This would likely result in more efficient code, for a number of
reasons.


> For example,
> millions, if not billions, of lines of C++ have been written, and we
> still do not have obviously fundamental classes like hierarchies and
> associative hierarchies.

A recursive variant might provide the kind of thing you're looking
for. See Boost.Variant.
This is usually how trees are handled in high-level languages, be it
functional ones (where the variant is explicit) or dynamic ones (where
the variant is implicit, it's the dynamic typing).

Property trees are also a nice way to deal with trees. (You associate
a path in a tree with a value, quite useful for hierarchical
configuration files)
You might want to look at Boost.PropertyTree.

These, however, don't provide a more fine-grained and optimized way to
deal with trees. There is work in the standard (emitted from a
Boost.Tree GSoC) for that.

Mathias Gaunard

unread,
Jan 14, 2009, 1:36:37 PM1/14/09
to
On 14 jan, 01:23, Le Chaud Lapin <jaibudu...@gmail.com> wrote:

> if (arg < 0)
> perror ("Negative value passed to function when positive value
> expected!!!"); // silly, IMO :)

They simply don't know about assert(arg >= 0).
Making arg unsigned could also have been a solution (or not).

Mathias Gaunard

unread,
Jan 14, 2009, 1:36:35 PM1/14/09
to
On 14 jan, 02:31, Kenneth Porter <shiva.blackl...@sewingwitch.com>
wrote:

> That surprises me. I would have thought that the new/delete would be those
> of the call sites, not the object definition sites. (Unless the class
> overloads new and delete.)

Well, obviously, the delete which is invoked is the one that is in the
virtual function table (if the destructor is virtual).
The table the object uses is the one it was provided with during
construction.

So, yes, having a virtual destructor means both new and delete will be
from the same module.

Gerhard Menzl

unread,
Jan 14, 2009, 1:43:18 PM1/14/09
to
Maxim Yegorushkin wrote:

> The reason for passing output parameters by pointer is that the
> ampersand at the call site gives you a hint that something is done to
> that argument.

I have always found this to be a bogus argument, and it is really
surprising me that it still keeps getting repeated after years of
debate. Consider:

// foomangle.h

void mangle(foo* f);

// bar.h

class bar
{
// ...
foo* myfoo;
};

// bar.cpp

void bar::rebuild()
{
// ...
mangle(myfoo); // whoa! no ampersand!
}

Relying on ampersands to signal argument modification (and their absence
to signal the opposite) gives a false sense of security because you get
the wrong signal when the argument is already of pointer type. The
convention, even if rigidly being adhered to, does not relieve you from
checking the documentation of the called function.

> Just to make clear, I don't find useful the idea of references-are-
> better-pointers-and-cant-be-null.

Reducing the number of states of a function is not useful?

--
Gerhard Menzl

Non-spammers may respond to my email address, which is composed of my
full name, separated by a dot, followed by at, followed by "fwz",
followed by a dot, followed by "aero".

David Abrahams

unread,
Jan 14, 2009, 1:42:30 PM1/14/09
to

on Tue Jan 13 2009, Kenneth Porter <shiva.blacklist-AT-sewingwitch.com> wrote:

> Le Chaud Lapin <jaibu...@gmail.com> wrote in news:1f656241-ba00-44c7-
> 9dba-932...@f33g2000vbf.googlegroups.com:
>
>> This problem is easily solved by making the destructor virtual. In
>> fact, it solves the general problem where memory allocated by
>> constructor in one module is deallocated by destructor in different
>> module. A virtual destructor forces invocation of the delete code that
>> is actually inside the module containing the new that allocated the
>> memory from their respective heap.
>>
>> I often get into fights with C++ jocks who insist, rather emphatically
>> I might add, that I am wrong about this without checking for
>> themselves.
>
> That surprises me. I would have thought that the new/delete would be those
> of the call sites, not the object definition sites. (Unless the class
> overloads new and delete.)

Don't worry, the validity of the hot bunny's statement is completely
dependent on implementation details of the particular compiler in
question.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Metron

unread,
Jan 14, 2009, 1:43:21 PM1/14/09
to
On Jan 14, 2:31 am, Kenneth Porter <shiva.blackl...@sewingwitch.com>
wrote:
> Le Chaud Lapin <jaibudu...@gmail.com> wrote in news:1f656241-ba00-44c7-
> 9dba-9324098f0...@f33g2000vbf.googlegroups.com:

>
> > This problem is easily solved by making the destructor virtual. In
> > fact, it solves the general problem where memory allocated by
> > constructor in one module is deallocated by destructor in different
> > module. A virtual destructor forces invocation of the delete code that
> > is actually inside the module containing the new that allocated the
> > memory from their respective heap.
>
> > I often get into fights with C++ jocks who insist, rather emphatically
> > I might add, that I am wrong about this without checking for
> > themselves.
>
> That surprises me. I would have thought that the new/delete would be those
> of the call sites, not the object definition sites. (Unless the class
> overloads new and delete.)

Well... it boils down to: "It has to be destructed where it has been
created."

That's why I tend to make extensive use of the factory pattern mixed
with the facade pattern. To be more precise the parameterized factory
pattern. My extension to this pattern is that the factory not only has
the create function but also the destroy function.

Using the facade pattern (kind of management of several factories) the
facade functions select the appropriate factory for object creation.
Since the identification of the object type to create is part of the
object itself, it is possible to have facade functions that select the
right factory to destroy the object.

NOTE: I'm speaking more of a Windows platform developer. My Linux
development experience tends to zero but still has a little
existens :)

This enables me to have implementation of factories spread in
different DLLs (IE. in a plug-in/add-in environment). And this is
where it's obvious that it can be problematic to destroy objects and
thus that it's difficult to maintain a proper exception handling based
programming style. Either you pass exception objects (but those will
not necessarily pass the DLL boundary depdendent on the compiler
settings) or you pass pointers (in which case you might have to delete
them or not... dependent on the implementation of the exceptions).

This link is an interesting resource: http://www.parashift.com/c++-faq-lite/exceptions.html

Have fun,
Metron

Thant Tessman

unread,
Jan 15, 2009, 9:57:09 AM1/15/09
to
Bo Persson wrote:
> Hak...@gmail.com wrote:

[...]

>> On a note about that, though: Some libraries, like OpenGL, instead
>> of throwing create lists of problems stored in strings. It doesn't
>> crash my program and it tells me what the problem is so I can just
>> rewrite the bad code. Not a bad work-a-round!
>

> OpenGL is a special case, where bad output is sometimes acceptable.
> The image looks a bit strange, but the application can often continue
> anyway. The next couple of frame updates might hide the problem.
>
> This is not generally the case.

Some clarification is in order:

OpenGL creates a list of problems stored in strings only when you try to
compile or link shaders at runtime. The errors that it needs to report
are bugs in your shader source code.

The programmer is still burdened with fetching that information and
reporting it anyway.

Conforming OpenGL implementations were never allowed to crash your
program no matter what you pass them. Note that crashing your program
and throwing an exception are two different things. OpenGL doesn't crash
your program as a matter of policy. It doesn't throw exceptions as a
matter of being a C API. (Technically it's also a FORTAN API, etc.,
which is why the API also happens to avoid structures.)

(Compiling shaders at runtime always seemed like a stupid idea to me
anyway. (Yes, I know what the arguments for it are.))

As for C++ exceptions in general, they're certainly to be preferred over
'undefined behavior' even when programmers don't bother to catch them.

-thant

Erik Wikström

unread,
Jan 15, 2009, 9:54:34 AM1/15/09
to
On 2009-01-14 01:25, Maxim Yegorushkin wrote:
> On Jan 11, 12:25 am, "Balog Pal" <p...@lib.hu> wrote:
>> "Martin T." <0xCDCDC...@gmx.at> :
>>
>> >I found the arguments in the Google Style Guide quite interesting:
>>
>> >http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showon...
>>
>> I read through the guide, it has a couple items that is completely nuts:
>>
>> - "Reference Arguments: All parameters passed by reference must be labeled
>> const. ...
>> In fact it is a very strong convention that input arguments are values or
>> const references while output arguments are pointers. ..."
>>
>> I'm using C++ since about CFront 2.0, first time to hear this idea, and it
>> is IMNSHO utter nonsense.
>
> This is, in fact, the convention I like to use.
>
> The reason for passing output parameters by pointer is that the
> ampersand at the call site gives you a hint that something is done to
> that argument.

As others have pointed out this argument is invalid, I work with a
moderately large system written in C and it is not uncommon that the
variable passed to a function is a pointer the current function received
as an argument, or the argument is a pointer for which memory was
allocated before passing on. In fact I'd say that it's very rare to pass
the address of a local variable. The only good indicator of whether the
argument passed can be changed or not is const, and it's not used nearly
as often as it could have.

--
Erik Wikström

Martin T.

unread,
Jan 15, 2009, 10:54:30 AM1/15/09
to
Le Chaud Lapin wrote:
> ... might be refering to the heap mismatch problem, where exception

> allocates memory during construction of its object using new, the new
> that is inside DLL, then EXE attempts to deallocate that same memory
> using delete, the delete that is inside EXE. Boom!
>
> This problem is easily solved by making the destructor virtual. In
> fact, it solves the general problem where memory allocated by
> constructor in one module is deallocated by destructor in different
> module. A virtual destructor forces invocation of the delete code that
> is actually inside the module containing the new that allocated the
> memory from their respective heap.
>

Not believing it myself I have tried it under Visual Studio 2005 and
it's indeed correct :-)

The question is, on which platforms can we rely on this behaviour?

br,
Martin

Le Chaud Lapin

unread,
Jan 15, 2009, 10:53:41 AM1/15/09
to
On Jan 14, 12:43 pm, Gerhard Menzl <clcppm-pos...@this.is.invalid>
wrote:

> Maxim Yegorushkin wrote:
> > The reason for passing output parameters by pointer is that the
> > ampersand at the call site gives you a hint that something is done to
> > that argument.
>
> I have always found this to be a bogus argument, and it is really
> surprising me that it still keeps getting repeated after years of
> debate. Consider:
>
> // foomangle.h
>
> void mangle(foo* f);
>
> // bar.h
>
> class bar
> {
> // ...
> foo* myfoo;
> };
>
> // bar.cpp
>
> void bar::rebuild()
> {
> // ...
> mangle(myfoo); // whoa! no ampersand!
> }
>
> Relying on ampersands to signal argument modification (and their absence
> to signal the opposite) gives a false sense of security because you get
> the wrong signal when the argument is already of pointer type. The
> convention, even if rigidly being adhered to, does not relieve you from
> checking the documentation of the called function.

I smell a serious breach of semantic regularity here. :)

I have written extensively about the fact that pointers are not the
things that they point to, but 1st-class bona-fide objects, scalar
objects in their own right. So technically speaking, "technically" in
the sense that the Earth is round and not flat, Maxim's point of view
is not only correct, but this other point of view is dangerous, IMO.

If someone were to put a gun to my head and ask me, "Quickly, what is
the type of myfoo?" with penalty of death for incorrect answer, I
would examine the code and respond..."Pointer to foo." I would _not_
say, "Foo.", because that would be incorrect.

>From this point of view, myfoo is a pointer, and the label "myfoo"
refers to an object that is on the stack. That object, and not the
object at the call site (another pointer!), will be modified, if
necessary.

You could pass by value an unsigned long int, and the label 'v' would
only allow you to modify anobject that of the same type as v, but
local to mangle, not the object that was used to generate the value of
v upon call:

void mangle(unsigned long int v);

You could pass by reference an unsigned long int, and the label 'v'
would allow you to modify an object that of the same type as v,
external to mangle, the object that is v upon call:

void mangle(unsigned long int &v);

You could pass by reference to const unsigned long int, and the label
'v' would not allow you to modify an object that of the same type as
v, external to mangle, the object that is v upon call:

void mangle(const unsigned long int &v);

You could pass by refrence to const foo, and the label 'f' would not
allow you to modify an object that of the same type as f, namely, a
pointer!, external to mangle, the object that is v upon call:

void mangle(foo* &f);

I know you know all of these things, but B.L. Whorf was right. Words
are critical.

I had a professor of stochastics in college who understood this very
well, and was extremely careful...to the point of appearing to have a
speech impediment at critical utterances, in making sure erroneous
concepts did not enter the minds of the newly initiated. I did the
same with my own students, carefully avoiding ever saying...

"It passes a const reference..." because I know that most students in
class already have erroneous notion that references are pointers and
can be passed around.

Nor do I do what you did above, puting the asterisk (*) right up
against the foo:

> void mangle(foo* f);

Instead I write

void mangle(foo *f);

to emphasize the semantics, and how the compiler thinks, because this
subtle change actually alters the way students think, and it prempts
bad habits in thought process.

Some students see the above line and insist on still calling f a
"foo", after having been admonished that it is not, so I write this on
board:

void mangle (const Foo ****** &f);

and ask, "What is f now?"

They go silent.

-Le Chaud Lapin-


--

Maxim Yegorushkin

unread,
Jan 15, 2009, 11:30:18 AM1/15/09
to
On Jan 14, 6:43 pm, Gerhard Menzl <clcppm-pos...@this.is.invalid>
wrote:

> Maxim Yegorushkin wrote:
> > The reason for passing output parameters by pointer is that the
> > ampersand at the call site gives you a hint that something is done to
> > that argument.
>
> I have always found this to be a bogus argument, and it is really
> surprising me that it still keeps getting repeated after years of
> debate. Consider:
>
> // foomangle.h
>
> void mangle(foo* f);
>
> // bar.h
>
> class bar
> {
> // ...
> foo* myfoo;
> };
>
> // bar.cpp
>
> void bar::rebuild()
> {
> // ...
> mangle(myfoo); // whoa! no ampersand!
> }
>
> Relying on ampersands to signal argument modification (and their absence
> to signal the opposite) gives a false sense of security because you get
> the wrong signal when the argument is already of pointer type.

myfoo argument does not change here, thus the desired effect achieved.

> The
> convention, even if rigidly being adhered to, does not relieve you from
> checking the documentation of the called function.

It does not indeed.

> > Just to make clear, I don't find useful the idea of references-are-
> > better-pointers-and-cant-be-null.
>
> Reducing the number of states of a function is not useful?

It sounds useful theoretically. Practically, I haven't seen a case
where cant-be-null property of references would make a difference for
function arguments.

--
Max


--

tonytech08

unread,
Jan 15, 2009, 1:08:17 PM1/15/09
to
On Jan 15, 10:30 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
wrote:

> It sounds useful theoretically. Practically, I haven't seen a case
> where cant-be-null property of references would make a difference for
> function arguments.


It's very practical. Example:

void Func(SomeObject* obj) // must check for null args usually
{
assert(null != obj)

// do something
}

void Func(SomeObject& obj) // obj can't be null so no need to assert
anything
{
// do something

Marsh Ray

unread,
Jan 15, 2009, 1:08:46 PM1/15/09
to
On Jan 15, 10:30 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
wrote:
> Practically, I haven't seen a case
> where cant-be-null property of references would make a difference for
> function arguments.

int a = 0, b = 1;

std::swap<int>(a, b); // Probably pretty darn efficient.

swap_c_style(&a, &b);

The implementation of swap_c_style has to either check both args for
null, or produce undefined behavior over its domain of input values.
The compiler probably has a harder job optimizing away the case where
(*a == *b), too.

- Marsh

Balog Pal

unread,
Jan 15, 2009, 8:53:11 PM1/15/09
to
>"Gerhard Menzl" <clcppm...@this.is.invalid>

>> Maxim Yegorushkin wrote:
>
>> The reason for passing output parameters by pointer is that the
>> ampersand at the call site gives you a hint that something is done to
>> that argument.
>
> I have always found this to be a bogus argument, and it is really
> surprising me that it still keeps getting repeated after years of debate.

Yea, seems as bogus to me even considering the stone age.

But for like a decade, in most useful working environments you just mouse
over the function and it will show the signature. Where types and param
names shall give a good hint. No need to guess.
If still in doubt another click (or key) navigates to declaration,
definition, dox, etc.


--

Gerhard Menzl

unread,
Jan 16, 2009, 2:58:33 PM1/16/09
to
Le Chaud Lapin wrote:

> On Jan 14, 12:43 pm, Gerhard Menzl <clcppm-pos...@this.is.invalid>
> wrote:
>> // foomangle.h
>>
>> void mangle(foo* f);
>>
>> // bar.h
>>
>> class bar
>> {
>> // ...
>> foo* myfoo;
>> };
>>
>> // bar.cpp
>>
>> void bar::rebuild()
>> {
>> // ...
>> mangle(myfoo); // whoa! no ampersand!
>> }

> I smell a serious breach of semantic regularity here. :)


>
> I have written extensively about the fact that pointers are not the
> things that they point to, but 1st-class bona-fide objects, scalar
> objects in their own right. So technically speaking, "technically" in
> the sense that the Earth is round and not flat, Maxim's point of view
> is not only correct, but this other point of view is dangerous, IMO.
>
> If someone were to put a gun to my head and ask me, "Quickly, what is
> the type of myfoo?" with penalty of death for incorrect answer, I
> would examine the code and respond..."Pointer to foo." I would _not_
> say, "Foo.", because that would be incorrect.
>
>>From this point of view, myfoo is a pointer, and the label "myfoo"
> refers to an object that is on the stack. That object, and not the
> object at the call site (another pointer!), will be modified, if
> necessary.

I think you are mixing up the physical and the logical aspects of the
code. Physically, myfoo is, of course, a pointer, and its value is not
going to be changed by mangle(). Logically, however, mangle's
responsibility is to mangle foos. That it does so via a pointer is an
implementation detail, and that there is a choice to use a pointer or a
reference is a peculiarity of the C++ language. What happens to the foo
object is the main focus of interest. Hence, relying on the ampersand is
confusing and misleading.

> Nor do I do what you did above, puting the asterisk (*) right up
> against the foo:
>
>> void mangle(foo* f);
>
> Instead I write
>
> void mangle(foo *f);
>
> to emphasize the semantics, and how the compiler thinks, because this
> subtle change actually alters the way students think, and it prempts
> bad habits in thought process.

The counterargument is that the asterisk is part of the type and not
part of the parameter. A considerable percentage of C++ programmers, as
well as the Standard, stick to the convention I have used.

> Some students see the above line and insist on still calling f a
> "foo", after having been admonished that it is not, so I write this on
> board:
>
> void mangle (const Foo ****** &f);
>
> and ask, "What is f now?"
>
> They go silent.

My code is intended to be read by professionals, not by students. Any
serious practitioner of C++ must be able to recognize both forms, and to
distinguish between the levels of indirection involved.


--
Gerhard Menzl

Non-spammers may respond to my email address, which is composed of my
full name, separated by a dot, followed by at, followed by "fwz",
followed by a dot, followed by "aero".

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu

unread,
Jan 16, 2009, 2:51:26 PM1/16/09
to
tonytech08 wrote:
> On Jan 15, 10:30 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
> wrote:
>
>> It sounds useful theoretically. Practically, I haven't seen a case
>> where cant-be-null property of references would make a difference for
>> function arguments.
>
>
> It's very practical. Example:
>
> void Func(SomeObject* obj) // must check for null args usually
> {
> assert(null != obj)
>
> // do something
> }
>
> void Func(SomeObject& obj) // obj can't be null so no need to assert
> anything
> {
> // do something
> }

Yah, we just shouldn't forget that the "can't be null" is only assumed,
not enforced, by most of today's implementations. It does clarify
interfaces nicely though. However, a combination of tradition and
language rules makes interface-based programs in C++ still deal in
pointers (bald or raw). Passing an interface by reference does raise
some eyebrows :o).

Andrei

Maxim Yegorushkin

unread,
Jan 18, 2009, 5:49:33 AM1/18/09
to
On Jan 15, 6:08 pm, tonytech08 <tonytec...@gmail.com> wrote:
> On Jan 15, 10:30 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
> wrote:
>
> > It sounds useful theoretically. Practically, I haven't seen a case
> > where cant-be-null property of references would make a difference
> > for
> > function arguments.
>
> It's very practical. Example:
>
> void Func(SomeObject* obj) // must check for null args usually
> {
> assert(null != obj)
> // do something
> }

It is going to dump core on NULL pointer with or without the
assertion. This assertion is plain useless.

--
Max

Maxim Yegorushkin

unread,
Jan 18, 2009, 5:50:46 AM1/18/09
to
On Jan 15, 6:08 pm, Marsh Ray <marsh...@gmail.com> wrote:
> On Jan 15, 10:30 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
> wrote:
>
> > Practically, I haven't seen a case
> > where cant-be-null property of references would make a difference
> > for
> > function arguments.
>
> int a = 0, b = 1;
>
> std::swap<int>(a, b); // Probably pretty darn efficient.

It as efficient as swap_c_style. See below.

> swap_c_style(&a, &b);
>
> The implementation of swap_c_style has to either check both args for
> null, or produce undefined behavior over its domain of input values.

No checks necessary, it should just dump a core file for your
examination.

> The compiler probably has a harder job optimizing away the case where
> (*a == *b), too.

Why would it? A reference argument to a function is the same as a
pointer argument on the binary level.

Let's compare optimised versions of std::swap<int, int>() and
swap_c_style<int>():

[max@truth test]$ g++ --version
g++ (GCC) 4.3.0 20080428 (Red Hat 4.3.0-8)
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

[max@truth test]$ cat test.cc
#include <algorithm>

template<class T>
void swap_c_style(T* a, T* b)
{
T t(*a);
*a = *b;
*b = t;
}

int main()
{
int a = 1, b = 2;

std::swap(a, b);

swap_c_style(&a, &b);
}

[max@truth test]$ g++ -O3 -fomit-frame-pointer -fno-inline -ggdb -Wall
-Wextra -o test.o test.cc
[max@truth test]$ objdump --syms test.o | grep swap | c++filt
08048460 w F .text 00000013 void swap_c_style<int>
(int*, int*)
08048440 w F .text 00000013 void std::swap<int>(int&,
int&)
[max@truth test]$ objdump -d --start-address=0x08048460 --stop-
address=0x08048473 test.o | c++filt

test.o: file format elf32-i386


Disassembly of section .text:

08048460 <void swap_c_style<int>(int*, int*)>:
8048460: 53 push %ebx
8048461: 8b 54 24 08 mov 0x8(%esp),%edx
8048465: 8b 4c 24 0c mov 0xc(%esp),%ecx
8048469: 8b 1a mov (%edx),%ebx
804846b: 8b 01 mov (%ecx),%eax
804846d: 89 02 mov %eax,(%edx)
804846f: 89 19 mov %ebx,(%ecx)
8048471: 5b pop %ebx
8048472: c3 ret
[max@truth test]$ objdump -d --start-address=0x08048440 --stop-
address=0x08048453 test.o | c++filt

test.o: file format elf32-i386


Disassembly of section .text:

08048440 <void std::swap<int>(int&, int&)>:
8048440: 53 push %ebx
8048441: 8b 54 24 08 mov 0x8(%esp),%edx
8048445: 8b 4c 24 0c mov 0xc(%esp),%ecx
8048449: 8b 1a mov (%edx),%ebx
804844b: 8b 01 mov (%ecx),%eax
804844d: 89 02 mov %eax,(%edx)
804844f: 89 19 mov %ebx,(%ecx)
8048451: 5b pop %ebx
8048452: c3 ret

Can you spot the difference?

--
Max

Le Chaud Lapin

unread,
Jan 18, 2009, 5:55:46 AM1/18/09
to
On Jan 16, 1:58 pm, Gerhard Menzl <clcppm-pos...@this.is.invalid>

wrote:
> Le Chaud Lapin wrote:
> > On Jan 14, 12:43 pm, Gerhard Menzl <clcppm-pos...@this.is.invalid>
> > wrote:
> >> // foomangle.h
>
> >> void mangle(foo* f);
>
> >> // bar.h
>
> >> class bar
> >> {
> >> // ...
> >> foo* myfoo;
> >> };
>
> >> // bar.cpp
>
> >> void bar::rebuild()
> >> {
> >> // ...
> >> mangle(myfoo); // whoa! no ampersand!
> >> }
> > I smell a serious breach of semantic regularity here. :)
>

> >>From this point of view, myfoo is a pointer, and the label "myfoo"


> > refers to an object that is on the stack. That object, and not the
> > object at the call site (another pointer!), will be modified, if
> > necessary.
>
> I think you are mixing up the physical and the logical aspects of the
> code. Physically, myfoo is, of course, a pointer, and its value is not
> going to be changed by mangle(). Logically, however, mangle's
> responsibility is to mangle foos. That it does so via a pointer is an
> implementation detail, and that there is a choice to use a pointer or a
> reference is a peculiarity of the C++ language. What happens to the foo
> object is the main focus of interest. Hence, relying on the ampersand is
> confusing and misleading.

Actually I am not mixing up anything.

Of every principle I have ever learned about science and engineering,
this one would definitely be in the top 15. I firmly believe that it
is nothing short of a cardinal sin to promote this point of view.
Note that I am not saying that we simply have a difference of opinion
here. This is _fundamental_, in the most extreme sense. That's why I
alluded the the Earth flat/round argument. There is something going
on here were you have two groups of people, one large, one smaller,
with diametrically opposed points of views, and they cannot both be
right.

I will certainly revisit this topic [pointers are not the things they
point to] in a separate thread when I have more time. It's that
important.

-Le Chaud Lapin-


--

David Abrahams

unread,
Jan 18, 2009, 8:23:18 AM1/18/09
to

on Thu Jan 15 2009, Le Chaud Lapin <jaibuduvin-AT-gmail.com> wrote:

> I smell a serious breach of semantic regularity here. :)
>
> I have written extensively about the fact that pointers are not the
> things that they point to, but 1st-class bona-fide objects, scalar
> objects in their own right. So technically speaking, "technically" in
> the sense that the Earth is round and not flat, Maxim's point of view
> is not only correct, but this other point of view is dangerous, IMO.

In that case, technically, there are no output parameters in Maxim's
code.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Mathias Gaunard

unread,
Jan 19, 2009, 2:47:38 AM1/19/09
to
On 18 jan, 11:50, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
wrote:

> > int a = 0, b = 1;
>
> > std::swap<int>(a, b); // Probably pretty darn efficient.
>
> It as efficient as swap_c_style. See below.
>
> > swap_c_style(&a, &b);

References are slightly easier to optimize than pointers.
A specific implementation may not find it harder, but some others may.

Thant Tessman

unread,
Jan 19, 2009, 2:50:37 AM1/19/09
to
Maxim Yegorushkin wrote:
> On Jan 15, 6:08 pm, tonytech08 <tonytec...@gmail.com> wrote:
>> On Jan 15, 10:30 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
>> wrote:
>>
>>> It sounds useful theoretically. Practically, I haven't seen a case
>>> where cant-be-null property of references would make a difference
>>> for
>>> function arguments.
>> It's very practical. Example:
>>
>> void Func(SomeObject* obj) // must check for null args usually
>> {
>> assert(null != obj)
>> // do something
>> }
>
> It is going to dump core on NULL pointer with or without the
> assertion. This assertion is plain useless.

Letting a program dump core is not an alternative to an assertion.

More than that, an assertion is not an alternative to an actual check
for invalid values.

-thant

Zeljko Vrba

unread,
Jan 19, 2009, 2:48:26 AM1/19/09
to
On 2009-01-18, Maxim Yegorushkin <maxim.ye...@gmail.com> wrote:
> On Jan 15, 6:08 pm, tonytech08 <tonytec...@gmail.com> wrote:
>>
>> void Func(SomeObject* obj) // must check for null args usually
>> {
>> assert(null != obj)
>> // do something
>> }
>
> It is going to dump core on NULL pointer with or without the
> assertion. This assertion is plain useless.
>

Not on systems without memory protection (e.g., in DOS it would just
access the division by zero exception vector).

Bo Persson

unread,
Jan 19, 2009, 2:46:44 AM1/19/09
to
Maxim Yegorushkin wrote:
> On Jan 15, 6:08 pm, Marsh Ray <marsh...@gmail.com> wrote:
>> On Jan 15, 10:30 am, Maxim Yegorushkin
>> <maxim.yegorush...@gmail.com> wrote:
>>
>>> Practically, I haven't seen a case
>>> where cant-be-null property of references would make a difference
>>> for
>>> function arguments.
>>
>> int a = 0, b = 1;
>>
>> std::swap<int>(a, b); // Probably pretty darn efficient.
>
> It as efficient as swap_c_style. See below.
>
>> swap_c_style(&a, &b);
>>
>> The implementation of swap_c_style has to either check both args
>> for null, or produce undefined behavior over its domain of input
>> values.
>
> No checks necessary, it should just dump a core file for your
> examination.

This assumes that you have a hardware assisted check for null pointer
references. On other systems there will be an additional cost -
presumably the systems where you can afford this the least.


Bo Persson

David Abrahams

unread,
Jan 19, 2009, 2:58:30 AM1/19/09
to

I agree that this is an extremely important idea in general, and for
C/C++ programmers in particular (see Stepanov on Regular Types).
However, most other languages I've encountered either have no value
semantics (Python, Java, Lisp, **) or inconsistent value semantics (D).
Isn't it strange that such a fundamental principle is inaccessible to
programmers in those languages?

(**) ...except for immutable objects, for which value semantics are
indistinguishable from reference semantics.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Balog Pal

unread,
Jan 19, 2009, 2:56:19 AM1/19/09
to
"Maxim Yegorushkin" <maxim.ye...@gmail.com>

>> void Func(SomeObject* obj) // must check for null args usually
>> {
>> assert(null != obj)
>> // do something
>> }
>
> It is going to dump core on NULL pointer with or without the
> assertion. This assertion is plain useless.

Says what?
The standard states dereferencing NULL pointer is undefined behavior. So
anything can happen.

On most modern unix systems, in practice, it is really dumping core or just
terminates the process via SIGSEGV, yet it is only one of a zillion possible
outcomes.
Just an example: on WIN32, using the not most recent MSVC NULL-access
generates a SOH exception. And if you have a catch(...) block upper,
execution gets there.

While failed assert does a deterministic and sure call to abort(), you can
rely on.

Andrei Alexandrescu

unread,
Jan 19, 2009, 3:19:00 PM1/19/09
to
Balog Pal wrote:
> "Maxim Yegorushkin" <maxim.ye...@gmail.com>
>>> void Func(SomeObject* obj) // must check for null args usually
>>> {
>>> assert(null != obj)
>>> // do something
>>> }
>> It is going to dump core on NULL pointer with or without the
>> assertion. This assertion is plain useless.
>
> Says what?
> The standard states dereferencing NULL pointer is undefined behavior. So
> anything can happen.
>
> On most modern unix systems, in practice, it is really dumping core or just
> terminates the process via SIGSEGV, yet it is only one of a zillion possible
> outcomes.
> Just an example: on WIN32, using the not most recent MSVC NULL-access
> generates a SOH exception. And if you have a catch(...) block upper,
> execution gets there.
>
> While failed assert does a deterministic and sure call to abort(), you can
> rely on.

Plus, assert gives you the file and line of the violation.

Andrei

Gerhard Menzl

unread,
Jan 19, 2009, 3:12:09 PM1/19/09
to
Le Chaud Lapin wrote:

> Actually I am not mixing up anything.
>
> Of every principle I have ever learned about science and engineering,
> this one would definitely be in the top 15. I firmly believe that it
> is nothing short of a cardinal sin to promote this point of view.
> Note that I am not saying that we simply have a difference of opinion
> here. This is _fundamental_, in the most extreme sense. That's why I
> alluded the the Earth flat/round argument. There is something going
> on here were you have two groups of people, one large, one smaller,
> with diametrically opposed points of views, and they cannot both be
> right.

Sorry, but I cannot follow you on this tangent. This is about a highly
subjective coding convention issue, i.e. whether passing modifiable
arguments by pointer really makes code more intuitive and easier to
understand, and all I said is that I doubt this because it is easy to be
misled. In short, the issue is human perception. How you can possibly
contrive a Copernican rift from this is beyond me.

--
Gerhard Menzl

Non-spammers may respond to my email address, which is composed of my
full name, separated by a dot, followed by at, followed by "fwz",
followed by a dot, followed by "aero".

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Marsh Ray

unread,
Jan 19, 2009, 3:19:01 PM1/19/09
to
On Jan 18, 4:50 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
wrote:

> On Jan 15, 6:08 pm, Marsh Ray <marsh...@gmail.com> wrote:
>
[...]

> > std::swap<int>(a, b); // Probably pretty darn efficient.
>
> It as efficient as swap_c_style. See below.
>
> > swap_c_style(&a, &b);
>
> > The implementation of swap_c_style has to either check both args for
> > null, or produce undefined behavior over its domain of input values.
>
> No checks necessary, it should just dump a core file for your
> examination.

Maybe there is a time and place to just go ahead and dereference that
pointer, I mean, what would you do anyway, die with "internal error
1623847" message? So maybe your compiler and target guarantee a good
dump file. And there's a good way to get that dump back to the
developers. And your customers are cool with that (no sensitive data
in it, they have connectivity, don't mind your app crashing and
filling up the disk with core files, etc).

So if the stars align right, you get a nice crash dump landing back in
your (boss's) lap (with your code right at the top of the stack).
Still, doesn't seem like a great way to get a reputation as a
developer of quality code.

If you're not so lucky that day, well:
http://www.google.com/search?q=null+pointer+exploit

Or you could just use references.

> > The compiler probably has a harder job optimizing away the case where
> > (*a == *b), too.
>
> Why would it?

Because:
1. The domain of a pointer type includes this value called 'null',
whereas references do not.
2. Pointer types allow arithmetic, whereas references do not.

These possibilities require a higher level of sophistication on the
part of the compiler to make this particular optimization.

Sometimes it won't even be possible. Like if swap_c_style is called
from code that's compiled (and even linked) separately?

> A reference argument to a function is the same as a
> pointer argument on the binary level.

On some, maybe even most, compilers. But not necessarily in general.

> Let's compare [...]


> Can you spot the difference?

Sure, some compilers will generate identical code for some examples.

- Marsh

Martin T.

unread,
Jan 19, 2009, 3:24:43 PM1/19/09
to
Le Chaud Lapin wrote:
> On Jan 14, 12:43 pm, Gerhard Menzl <clcppm-pos...@this.is.invalid>
> wrote:
>> Maxim Yegorushkin wrote:
>>> The reason for passing output parameters by pointer is that the
>>> ampersand at the call site gives you a hint that something is done to
>>> that argument.
>> I have always found this to be a bogus argument, and it is really
>> surprising me that it still keeps getting repeated after years of
>> debate. Consider:
>> ...
>> foo* myfoo;

>> ...
>> mangle(myfoo); // whoa! no ampersand!
>> }
>>
>> Relying on ampersands to signal argument modification (and their absence
>> to signal the opposite) gives a false sense of security because you get
>> the wrong signal when the argument is already of pointer type. The
>> convention, even if rigidly being adhered to, does not relieve you from
>> checking the documentation of the called function.
>
> I smell a serious breach of semantic regularity here. :)
>
> I have written extensively about the fact that pointers are not the
> things that they point to, but 1st-class bona-fide objects, scalar
> objects in their own right. So technically speaking, "technically" in
> the sense that the Earth is round and not flat, Maxim's point of view
> is not only correct, but this other point of view is dangerous, IMO.
>

Yes, pointers are not what they point to. (And I remeber well how long
it took me in college to understand pointers vs. values so you are right
to insist in making a distinction.)

However, I think this is irrelevant to the discussion if using pointers
as out arguments helps in reading the code.
Example:
-- header --
void pf1(const T* o);
void pf2( T* o);
void rf1(const T& o);
void rf2( T& o);

-- code using it --
T* pobj = ...

pf1(pobj); // may not modify pobj, but may modify
// obj pointed to *unless* the signature
// spec. a const pointer => Need to look
// at declaration

pf2(pobj); // same as for pf1 ... I need to know the
// declaration. Otherwise I only know that this function
// does not modify the pointer, which most likely
// I don't care about.

rf1(*pobj); // hm. so i know that this function will certainly
// not modify pobj, but it may well modify
// the object pointed to (need to check)
...

-- --

So you see. Just because the input to a function is a pointer doesn't
tell me anything other that this pointer is not modified. This does not
really help me when I want to know what the function does with it's
arguments.

cheers,
Martin

Le Chaud Lapin

unread,
Jan 21, 2009, 1:54:48 PM1/21/09
to
On Jan 14, 12:42 pm, David Abrahams <d...@boostpro.com> wrote:
> Don't worry, the validity of the hot bunny's statement is completely
> dependent on implementation details of the particular compiler in
> question.

You're right, of course, but I always qualify that my assertion:

"Right now, in the year [year of assertion], on Microsoft Windows OS's
and very likely others, making destructor virtual solves the inter-
module new/delete, DLL/EXE, heap-mismatch problem."

Each time I discuss this statement with fellow engineers, I muse
serendipitously about its possible reasons until I arrived at the
conclusion that, though it is conceivable that the implementation
might be otherwise, it is most-likely not, as the way it is makes much
more sense to compiler-writer, all things considered.

Unfortunately, I do not have the capacity/context of compiler-writer,
so I forget the reasons almost as soon as I discover them, each time. :
(

-Le Chaud Lapin-

Le Chaud Lapin

unread,
Jan 21, 2009, 1:57:15 PM1/21/09
to
On Jan 14, 12:36 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> On 14 jan, 01:23, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
>
> > if (arg < 0)
> > perror ("Negative value passed to function when positive value
> > expected!!!"); // silly, IMO :)
>
> They simply don't know about assert(arg >= 0).
> Making arg unsigned could also have been a solution (or not).

I think it is. This goes along with the philosophy that part of being
a good engineer is structuring the system so that certain awkward
questions never need be asked:

Q: What happens when value of this inherently positve argument is
negative? Should I assert?
A: You should not have declared it int. Make it unsigned int.

Q: How do I return an error code from a constructor if exceptions are
not available:
A: You should use exceptions.

Q: 80% of my classes are abstract. I get lost in memory management?
What should I do?
A: Ease up off the polymorphism, dude! Try a little concrete for
change. Have a map<> or a set<>.

Q: I like new(). I like it so much that I new() concrete objects that
could just as well have not been auto constructed. Should I auto_ptr<>
the returned pointer to help with memory managment?
A: Ease up off the new().

Q: My 2-month old new and fancy universal deep-copy framework is not
quite right though I have exerted Hurculean effort to make it such.
What should I do?
A: Rethink the very notion of "deep copy".

Q: My 2-month old new and fancy C++ introspection framework will not
automate itself. I just know I am missing something because it happens
all the time in Java. Why won't these classes instrospect themselves?
Is C++ defective?
A: C++ is not Java or any other interpreter-assisted language. Think
very carefully about comparing C++ to something that is fundamentally
different from it. If you insist, define an object that is a
hierarchy, where each node of the hierarchy is a mapping of string to
an associative set of string to string. That's the best you're going
to get in C++.

Le Chaud Lapin

unread,
Jan 21, 2009, 1:54:49 PM1/21/09
to
On Jan 14, 12:37 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> On 14 jan, 01:19, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
>
> > Here is example of big-integer division code that could be buried deep
> > within a cryptographic operation:
>
> > Integer operator / (const Integer &dividend, const Integer &divisor)
> > {
> > Integer quotient, remainder;
> > if (!Integer::divide (dividend, divisor, quotient, remainder))
> > throw Integer::DIVISION_BY_ZERO;
> > return quotient;
>
> > }
>
> > Integer::divide() returns true for success, false for failure, but
> > operator / would lose all its beauty if it had to return an error:
>
> Why not throw directly from Integer::divide?
> You could also return a tuple (here, a pair) rather than taking
> objects to modify by reference.
>
> This would likely result in more efficient code, for a number of
> reasons.

Hmm...I thought I had a reason, but after checking and rechecking my
Integer.cpp file, I cannot find one, so thanks...I will do just
that. :)

> > For example,
> > millions, if not billions, of lines of C++ have been written, and we
> > still do not have obviously fundamental classes like hierarchies and
> > associative hierarchies.
>
> A recursive variant might provide the kind of thing you're looking
> for. See Boost.Variant.
> This is usually how trees are handled in high-level languages, be it
> functional ones (where the variant is explicit) or dynamic ones (where
> the variant is implicit, it's the dynamic typing).

I have several templated hierarchy classes, with operations and O(n)
that one would reasonable expect: not spectactular, but O[log(n)]
where it matters.

> Property trees are also a nice way to deal with trees. (You associate
> a path in a tree with a value, quite useful for hierarchical
> configuration files)
> You might want to look at Boost.PropertyTree.

That would be...

typedef Associative_Monarchy<String<>, Associative_Set<String<>,
String<> > > PropertyTree;
PropertyTree::Path path;

... in the my model.

I agree that one can get very close to XML-like internal data
structures very quickly with these types of containers.

And the reachability provide by combining such primitives, along with
the regular list<>, set<>, map<>, etc., is staggering.

Le Chaud Lapin

unread,
Jan 21, 2009, 1:54:51 PM1/21/09
to
On Jan 19, 1:58 am, David Abrahams <d...@boostpro.com> wrote:
> I agree that this is an extremely important idea in general, and for
> C/C++ programmers in particular (see Stepanov on Regular Types).
> However, most other languages I've encountered either have no value
> semantics (Python, Java, Lisp, **) or inconsistent value semantics (D).
> Isn't it strange that such a fundamental principle is inaccessible to
> programmers in those languages?

Yes, very strange. This is why I think the knowledge barrier for
computer scientists should be pushed all the way down to the point of
transistors, working back up to the language:

1. transistor
2. gate
3. combinatorial device
4. bi-stable device
5. register
6. FSM (CPU + RAM) [study stack and call frames, interrupts, VM, etc.]
7. Bootstrap loader
8. Compiler
9. Language

This sequence is readily accessible, step-by-step. If one understands
it, one will never become confused by pointers, references,
indirection of any kind...and we will never find ourselves faced with
the awkward task of trying to explain why a pointer is not a
reference.

-Le Chaud Lapin-


--

Andrei Alexandrescu

unread,
Jan 21, 2009, 10:59:10 PM1/21/09
to
Le Chaud Lapin wrote:
> On Jan 14, 12:36 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>> On 14 jan, 01:23, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
>>
>>> if (arg < 0)
>>> perror ("Negative value passed to function when positive value
>>> expected!!!"); // silly, IMO :)
>> They simply don't know about assert(arg >= 0).
>> Making arg unsigned could also have been a solution (or not).
>
> I think it is. This goes along with the philosophy that part of being
> a good engineer is structuring the system so that certain awkward
> questions never need be asked:

Hm, I'd have a couple of comments to these questions. To me it doesn't
quite feel the answer to them is universally agreed upon by good engineers.

> Q: What happens when value of this inherently positve argument is
> negative? Should I assert?
> A: You should not have declared it int. Make it unsigned int.

Unfortunately, that's not going to help that much. Due to C and C++'s
proneness to automatically convert any integral with a pulse to any
other integral with a pulse, unsigned is not much useful as a model of
natural numbers. (I owe understanding of this fact to Gabriel Dos Reis,
and it was one of the latest things I'd learned about C++, which may be
evidence for its subtlety.)

> Q: How do I return an error code from a constructor if exceptions are
> not available:
> A: You should use exceptions.

That's negating the hypothesis :o).

> Q: My 2-month old new and fancy universal deep-copy framework is not
> quite right though I have exerted Hurculean effort to make it such.
> What should I do?
> A: Rethink the very notion of "deep copy".

Funny you should say that. I've seen a video on google from a speaker at
Adobe that promoted the notion that all values should be deep copyable,
and that there should be no aliasing. Does anyone know a pointer to that
talk? It was called "Defining good values" or something like that, but I
couldn't find it again.

> Q: My 2-month old new and fancy C++ introspection framework will not
> automate itself. I just know I am missing something because it happens
> all the time in Java. Why won't these classes instrospect themselves?
> Is C++ defective?
> A: C++ is not Java or any other interpreter-assisted language. Think
> very carefully about comparing C++ to something that is fundamentally
> different from it. If you insist, define an object that is a
> hierarchy, where each node of the hierarchy is a mapping of string to
> an associative set of string to string. That's the best you're going
> to get in C++.

Interpreted has nothing to do with it. It's about introspection, of
which Java has more than C++. I see nothing in C++ that makes it
fundamentally opposed to introspection capabilities.


Andrei

Balog Pal

unread,
Jan 21, 2009, 10:54:39 PM1/21/09
to
"Le Chaud Lapin" <jaibu...@gmail.com>

>> However, most other languages I've encountered either have no value
>> semantics (Python, Java, Lisp, **) or inconsistent value semantics (D).
>
> Yes, very strange. This is why I think the knowledge barrier for
> computer scientists should be pushed all the way down to the point of
> transistors, working back up to the language:
>
> 1. transistor
> 2. gate
> 3. combinatorial device
> 4. bi-stable device
> 5. register
> 6. FSM (CPU + RAM) [study stack and call frames, interrupts, VM, etc.]
> 7. Bootstrap loader
> 8. Compiler
> 9. Language
>
> This sequence is readily accessible, step-by-step. If one understands
> it, one will never become confused by pointers, references,
> indirection of any kind...and we will never find ourselves faced with
> the awkward task of trying to explain why a pointer is not a
> reference.

Is this full seq really needed? Those who just started programming assembly
language before using something else sure will have no problems with
pointers -- or anything.

Guess the Knuth school does similar effect with Mix.

Interestingly, I'd guess if you arrive from the opposite direction, the SICP
school, it still work.

The problem is probably what Joel described in 'Perils of Javaschools'...

Bo Persson

unread,
Jan 21, 2009, 10:55:15 PM1/21/09
to
Le Chaud Lapin wrote:
> On Jan 14, 12:36 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>> On 14 jan, 01:23, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
>>
>>> if (arg < 0)
>>> perror ("Negative value passed to function when positive value
>>> expected!!!"); // silly, IMO :)
>>
>> They simply don't know about assert(arg >= 0).
>> Making arg unsigned could also have been a solution (or not).

Or not!

>
> I think it is. This goes along with the philosophy that part of
> being a good engineer is structuring the system so that certain
> awkward questions never need be asked:
>
> Q: What happens when value of this inherently positve argument is
> negative? Should I assert?
> A: You should not have declared it int. Make it unsigned int.

void f(unsigned Arg);

Q: So what happens if I now call f(-1)?
A: Oops, we should add an assert(Arg <= max_value).


Bo Persson

Mathias Gaunard

unread,
Jan 21, 2009, 10:56:51 PM1/21/09
to
On 21 jan, 19:57, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
> On Jan 14, 12:36 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>
> > On 14 jan, 01:23, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
>
> > > if (arg < 0)
> > > perror ("Negative value passed to function when positive value
> > > expected!!!"); // silly, IMO :)
>
> > They simply don't know about assert(arg >= 0).
> > Making arg unsigned could also have been a solution (or not).
>
> I think it is. This goes along with the philosophy that part of being
> a good engineer is structuring the system so that certain awkward
> questions never need be asked:
>
> Q: What happens when value of this inherently positve argument is
> negative? Should I assert?
> A: You should not have declared it int. Make it unsigned int.

To be more general, functions expect some preconditions on their
arguments, and they document them.
If the preconditions of a function are not met, then the function does
not guarantee anything about what it will do : it will lead to
undefined behavior.

It thus becomes obvious that a mechanism to detect that the
preconditions were not met is nice to have in order to know the
program is correct. Some preconditions can be checked using the type
system by restricting the input arguments to their valid values, which
will perform a compile-time check.
For others the predicate is a bit more complicated, and needs to be
performed at runtime: that is what asserts are for.

So using the type system to enforce preconditions when possible is
better than asserts, of course.

But for the case of the signed/unsigned ints, there could be a reason
to prefer asserts (or not, it's really a matter of choice): they're
implicitly convertible to each other, and the implicit conversion,
while not losing information, alters its interpretation.
So I can pass -1 to a function expecting an unsigned int, and it will
work fine. But what it does is probably not what I had wanted to do.
That is entirely my fault, of course, not the fault of the function.
Yet this may require quite some work to debug; which wouldn't have
happened if the function had taken an int and done an assert.

An ideal solution would be to use types without those "dangerous"
implicit conversions, but that simply wouldn't be practical I'm
afraid.

Dave Harris

unread,
Jan 21, 2009, 10:56:07 PM1/21/09
to
jaibu...@gmail.com (Le Chaud Lapin) wrote (abridged):

> Q: What happens when value of this inherently positve argument is
> negative? Should I assert?
> A: You should not have declared it int. Make it unsigned int.

Unsigned int has some peculiar properties in C++, and is best avoided
unless you actively want those properties. You shouldn't use it just as
an int that is >= 0. It's not worth it.

-- Dave Harris, Nottingham, UK.

Mathias Gaunard

unread,
Jan 23, 2009, 6:18:42 AM1/23/09
to
On 22 jan, 04:54, "Balog Pal" <p...@lib.hu> wrote:
> "Le Chaud Lapin" <jaibudu...@gmail.com>

> > 1. transistor
> > 2. gate
> > 3. combinatorial device
> > 4. bi-stable device
> > 5. register
> > 6. FSM (CPU + RAM) [study stack and call frames, interrupts, VM, etc.]
> > 7. Bootstrap loader
> > 8. Compiler
> > 9. Language
>

> Is this full seq really needed? Those who just started programming assembly
> language before using something else sure will have no problems with
> pointers -- or anything.

Anything below a hardware description language like VHDL or Verilog is
electronics, not computer science.
As for a bootstrap loader, it's not really needed unless you're
designing your architecture or OS (and still, I did both of these in
classes and never wrote a bootstrap loader myself).


> Guess the Knuth school does similar effect with Mix.

MIX is far from anything that happens on a real machine of nowadays.

MMIX, its replacement, is also quite different from mainstream
assembly. I find it to be more like low-level C.

So it doesn't start *that* low.


> Interestingly, I'd guess if you arrive from the opposite direction, the SICP
> school, it still work.

Depends what you mean by the SICP school. That kind of course isn't
taught anymore.

You really need a lot of distance when you're used to code in high-
level dynamic languages to realize what is really going on. Starting
with Scheme and Python won't really help unless you then learn from
the bottom-up how these languages work.

Jeff Schwab

unread,
Jan 23, 2009, 6:18:48 AM1/23/09
to
Andrei Alexandrescu wrote:

> I've seen a video on google from a speaker at
> Adobe that promoted the notion that all values should be deep copyable,
> and that there should be no aliasing. Does anyone know a pointer to that
> talk?

Since I'm convinced that all values should be deep copyable, and that
there should be no aliasing, I am unable to send you a pointer.
Unfortunately, for legal and technical reasons, the video itself is
noncopyable.

Seungbeom Kim

unread,
Jan 23, 2009, 6:19:39 AM1/23/09
to
Le Chaud Lapin wrote:
>
> Q: What happens when value of this inherently positve argument is
> negative? Should I assert?
> A: You should not have declared it int. Make it unsigned int.

There have been lots and lots of discussion about this, and I feel
I'm opening that can of worms again, but... an unsigned parameter
doesn't prevent you from passing a negative value. Unsigned in C or
C++ is not "restrictive" in any sense; it just provides an alternate
"view" or "interpretation" of the underlying data and operations.

the valid range for signed
bang! ,-----------------------. bang!
3-bit signed: ~~~~~~~~~~~ -4 -3 -2 -1 0 +1 +2 +3 ~~~~~~~~~~~
-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-
3-bit unsigned: 0 +1 +2 +3 0 +1 +2 +3 0 +1 +2 +3 0 +1 +2 +3

Signed means a finite strip on which you'll be doomed if you walk
past the ends, and unsigned means that the ends are put together
so that the strip forms a ring from which you can never fall off.

--
Seungbeom Kim

Maxim Yegorushkin

unread,
Jan 23, 2009, 6:18:48 AM1/23/09
to

You are trying to solve a good problem (NULL-pointer dereference),
however, you are hacking on the leaves, rather than on the root of the
it.

The root of the problem is that pointers, being what they are, require
good skill and care. The only real solution is discipline.

> > > The compiler probably has a harder job optimizing away the case where
> > > (*a == *b), too.
>
> > Why would it?
>
> Because:
> 1. The domain of a pointer type includes this value called 'null',
> whereas references do not.

Where does it help?

It only helps with casts over a hierarchy, because a pointer cast that
adds a positive or negative delta to the address must not adjust NULL
pointer. Whereas doing the same cast using a reference destination
type does not have to check its input for NULL.

> 2. Pointer types allow arithmetic, whereas references do not.
>
> These possibilities require a higher level of sophistication on the
> part of the compiler to make this particular optimization.
>
> Sometimes it won't even be possible. Like if swap_c_style is called
> from code that's compiled (and even linked) separately?
>
> > A reference argument to a function is the same as a
> > pointer argument on the binary level.
>
> On some, maybe even most, compilers. But not necessarily in general.

References were introduced to the language to allow for operator
overloading. It's essentially a constant pointer with automatic
address-of operator application to the initialiser and automatic
dereference. There are also "reference to const can be bound to an r-
value", "reference is assumed to be non-NULL", and "no pointer
arithmetic" relaxations on top of the standard pointer semantics. But
that is about it.

In every other sense a reference is just a mere pointer.

> > Let's compare [...]
> > Can you spot the difference?
>
> Sure, some compilers will generate identical code for some examples.

Which compilers will not?

--
Max

Thant Tessman

unread,
Jan 23, 2009, 6:18:41 AM1/23/09
to
Mathias Gaunard wrote:

[...]

> For others the predicate is a bit more complicated, and needs to be

> performed at runtime: that is what asserts are for. [...]

I'm not convinced this is good advice. Asserts are for sanity checks.
They're for making sure your code is doing what you think it's doing.
Asserts are not for checking whether a function is being passed an
invalid value. A function--especially a library function--needs to
explicitly test for invalid values and signal an appropriate error, not
just abort.

-thant

Mathias Gaunard

unread,
Jan 23, 2009, 6:18:09 AM1/23/09
to
On 22 jan, 04:59, Andrei Alexandrescu <SeeWebsiteForEm...@erdani.org>
wrote:

> Funny you should say that. I've seen a video on google from a speaker at
> Adobe that promoted the notion that all values should be deep copyable,
> and that there should be no aliasing.

Isn't that universally agreed?
A value needs to have value semantics, which means deep copy.
If you have shallow copy, what you're dealing with is pointers, which
have reference semantics.

The obvious advantage of value semantics is that there is no aliasing,
which makes the code much more robust.
And the thing with C++ is that is is one of the very few languages
that actually allow value semantics.

That is why I personally don't use smart pointers; they still have
reference semantics.
Having the same kind of functionality as smart objects is much better
in my opinion. Even sharing is possible if the object is const.

Le Chaud Lapin

unread,
Jan 23, 2009, 6:21:23 AM1/23/09
to
On Jan 21, 9:56 pm, brang...@cix.co.uk (Dave Harris) wrote:
> jaibudu...@gmail.com (Le Chaud Lapin) wrote (abridged):

>
> > Q: What happens when value of this inherently positve argument is
> > negative? Should I assert?
> > A: You should not have declared it int. Make it unsigned int.
>
> Unsigned int has some peculiar properties in C++, and is best avoided
> unless you actively want those properties. You shouldn't use it just as
> an int that is >= 0. It's not worth it.
>
> -- Dave Harris, Nottingham, UK.

I have read all the responses, and yes, I am familiar with the
conversion issues, so I should have qualified somewhat. It is true
that if one's daily activity involves working with code where

signed-types-for-inherently-unsigned-quantities

are already the norm, a lot the depend upons the ratio of new code to
old code. If there is too much signed-... code, then I agree that one
might as well be consistent.

However, I am forttunate situation where essentially all code I write
is new. I do interact with Windows code, (COM all the time..sigh), but
even still, I follow the unsigned principle.

The key is habit and consistency. When I think of a variable for the
number of apples in a bin, I immediately think unsigned. At no point
does consideration for signed enter my mind. I have been doing this
for almost 20 years.

If one follows this principle, it scales. Ten lines of unsigned-code
becomes 20. Twenty becomes 200. Two-hundred becomes 2000, and 2000
becomes 20,000. All without a single assert on should-not-be-
negative.

IMO, the mental relief is gained is immeasurable.

With such code, one need only worry about boundary conditions, where
negative values can sneak in, like input from command line, etc. But
the rest of the code interacts *very* well together.

I think that not many people have take this path, not fully at least,
so they cannot properly judge the relative merits, IMHO.

Note that, if what I am saying is true, that it is possible to write
unsigned code this way, that the mental relief is significant when
doing it this way compared to the signed way, and that it is possible
for a single individual to be consistent in using unsigned types in
this manner without burden of forethought or making a mistake (just as
person would not make a mistake falling randomly while walking), the
arguments against it would be, by definition, moot.

OTOH, if some says to me, "Chaud, I tried it your way. I wrote 50,000
lines of unsigned int code with no signed anywhere, and I did not
experience any mental relief or other joy you promised....", that
would be a different story. ;)

BTW Andrei, I vaguely recall you being on my side of the signed/
unsigned fence in a previous discussion of this topic. :)

-Le Chaud Lapin-

Tony

unread,
Jan 23, 2009, 6:21:46 AM1/23/09
to

"Le Chaud Lapin" <jaibu...@gmail.com> wrote in message
news:90e9f907-b5cc-42a6...@q9g2000yqc.googlegroups.com...

> Q: How do I return an error code from a constructor if exceptions are
> not available:
> A: You should use exceptions.

A: Don't write constructors that can cause errors. Constructors are mostly
for trivial initialization only. Don't use RAII everywhere: RAII is for
simple little classes like a lock class that will ensure that the lock will
be released when a scope is exited. RAII is not for something like "class
MyApplicationProgram" that launches a chain-reaction of constructors on
instantiation. If something is procedural, code it up that way rather than
shoe-horning everything into an OO extremism. Have more than just a hammer
in your toolbox or else everything will look like a nail.

(Being "a bit" facetious).

Tony

Marsh Ray

unread,
Jan 23, 2009, 12:37:32 PM1/23/09
to
On Jan 21, 9:59 pm, Andrei Alexandrescu
<SeeWebsiteForEm...@erdani.org> wrote:
> [...] I see nothing in C++ that makes it

> fundamentally opposed to introspection capabilities.

Type erasure and "You don't pay for what you don't use".

How often do we see people wanting "C++ without RTTI", even when its
overhead is pretty darn minimal. I'd be amazed if there was an
implementation of a Java-style introspection facility that would be
complete enough to satisfy those wanting such a thing and lightweight
enough for those who don't. It wouldn't be the first time C++ has
amazed me (in a good way of course) though!

- Marsh

SG

unread,
Jan 23, 2009, 12:45:23 PM1/23/09
to
On 19 Jan., 21:24, "Martin T." <0xCDCDC...@gmx.at> wrote:
> Example:
> -- header --
> void pf1(const T* o);
> void pf2( T* o);
> void rf1(const T& o);
> void rf2( T& o);
>
> -- code using it --
> T* pobj = ...
>
> pf1(pobj); // may not modify pobj, but may modify
> // obj pointed to *unless* the signature
> // spec. a const pointer => Need to look
> // at declaration
> rf1(*pobj); // hm. so i know that this function will certainly
> // not modify pobj, but it may well modify
> // the object pointed to (need to check)

No. Check out the following types:

1. "const T*" is a pointer to a const T
2. "T const*" is a pointer to a const T

In the above cases (which are equivalent) you are not allowed to alter
the object of type T within the function. But you can alter the local
copy of the pointer. Still, 'pobj' is not altered because the pointer
is passed by pass-by-value.

3. "T* const" is a const pointer to a T

This is a pointer to an object of type T. The function may alter this
object. The function can't alter the local copy of the pointer itself
because it is declared const.

4. "const T* const" is a const pointer to a const T
5. "T const* const" is a const pointer to a const T

This is a combination of both const qualifiers (again, 4 and 5 are
equivalent). The function can't alter the object of type T (unless you
count const_cast) and can't alter the local copy of the pointer to the
T object because the pointer is also const.

For references it's similar. The only difference is that references
are intrinsically const (the references itself -- not necessarily the
objects they refer to!) and that you don't need to take the address
and use the dereference operator.

6. "const T&" is a reference to a const T
7. "T const&" is a reference to a const T

The function can use the reference to access the original object of
type T. But it is not allowed to change it.

> So you see. Just because the input to a function is a pointer doesn't
> tell me anything other that this pointer is not modified. This does not
> really help me when I want to know what the function does with it's
> arguments.

It does. Well, the argument would be the pointer which is copied. But
the pointer has a type: X*. and X may be const or not in which case
the function can or cannot modify the object pointed to.

Cheers!
SG

Erik Wikström

unread,
Jan 23, 2009, 12:42:31 PM1/23/09
to
On 2009-01-22 04:55, Bo Persson wrote:
> Le Chaud Lapin wrote:
>> On Jan 14, 12:36 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>>> On 14 jan, 01:23, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
>>>
>>>> if (arg < 0)
>>>> perror ("Negative value passed to function when positive value
>>>> expected!!!"); // silly, IMO :)
>>>
>>> They simply don't know about assert(arg >= 0).
>>> Making arg unsigned could also have been a solution (or not).
>
> Or not!
>
>>
>> I think it is. This goes along with the philosophy that part of
>> being a good engineer is structuring the system so that certain
>> awkward questions never need be asked:
>>
>> Q: What happens when value of this inherently positve argument is
>> negative? Should I assert?
>> A: You should not have declared it int. Make it unsigned int.
>
> void f(unsigned Arg);
>
> Q: So what happens if I now call f(-1)?
> A: Oops, we should add an assert(Arg <= max_value).

I assume that max_value == numeric_limits<unsigned int>::max(). The
problem with this is of course if max_value is a valid argument to f().
As pointed out by many C++'s habit of freely converting between integer
types can cause a number of problems for those caught unaware.

--
Erik Wikström

David Abrahams

unread,
Jan 23, 2009, 2:09:28 PM1/23/09
to

on Wed Jan 21 2009, Andrei Alexandrescu <SeeWebsiteForEmail-AT-erdani.org> wrote:

> Le Chaud Lapin wrote:
>> On Jan 14, 12:36 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>>> On 14 jan, 01:23, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
>>>
>>>> if (arg < 0)
>>>> perror ("Negative value passed to function when positive value
>>>> expected!!!"); // silly, IMO :)
>>> They simply don't know about assert(arg >= 0).
>>> Making arg unsigned could also have been a solution (or not).
>>
>> I think it is. This goes along with the philosophy that part of being
>> a good engineer is structuring the system so that certain awkward
>> questions never need be asked:

Brilliantly stated!

> Hm, I'd have a couple of comments to these questions. To me it doesn't
> quite feel the answer to them is universally agreed upon by good engineers.

That's true, but even good engineers can be wrong :-)

>> Q: What happens when value of this inherently positve argument is
>> negative? Should I assert?
>> A: You should not have declared it int. Make it unsigned int.
>
> Unfortunately, that's not going to help that much. Due to C and C++'s
> proneness to automatically convert any integral with a pulse to any
> other integral with a pulse, unsigned is not much useful as a model of
> natural numbers. (I owe understanding of this fact to Gabriel Dos Reis,
> and it was one of the latest things I'd learned about C++, which may be
> evidence for its subtlety.)

It doesn't help that much for overall system reliability if you're
trying to make it resilient against programmer errors, but personally I
think that approach is a dead end. Systems that try to be resilient
quickly become messy and unmaintainable due to the extra resiliency
code, which can almost never be properly tested. Rather, I prefer to
concentrate on making it less likely that programmer errors will occur,
and one way to do that is to build as much information about
preconditions as possible into the parameter types.

Using unsigned pushes the question to the boundary between the function
and its caller rather than allowing the question to occur inside the
function where it complicates code. Callers of functions already need
to understand the relationship between argument and parameter types and
watch out for narrowing conversions (which occur even with signed
types), so it doesn't make for a new point of unreliability.

>> Q: How do I return an error code from a constructor if exceptions are
>> not available:
>> A: You should use exceptions.
>
> That's negating the hypothesis :o).

Nice trick, eh?

>> Q: My 2-month old new and fancy universal deep-copy framework is not
>> quite right though I have exerted Hurculean effort to make it such.
>> What should I do?
>> A: Rethink the very notion of "deep copy".

Nice!

> Funny you should say that. I've seen a video on google from a speaker
> at Adobe that promoted the notion that all values should be deep
> copyable,

Well, not exactly. The video (below) lays out an approach to
programming with _values_. I agree that we should rethink the notion of
"deep copy." There is only one notion of "copy" for _values_; you
simply can't have shallow and deep versions if you're going to preserve
equational reasoning. The stuff that's part of the value gets copied
(modulo implementation sharing due to immutability or COW -- you still
have a logical copy). But that's one of those things that none of the
other GC'd everything-is-a-reference languages seem to understand.

> and that there should be no aliasing.

You can have implementation aliasing but no logical aliasing.

> Does anyone know a pointer to that talk? It was called "Defining good
> values" or something like that, but I couldn't find it again.

http://my.adobe.acrobat.com/p53888531/

It's a great talk; I recommend it to everyone.

Also download the pdf at
http://stlab.adobe.com/wiki/index.php/Papers_and_Presentations

>> Q: My 2-month old new and fancy C++ introspection framework will not
>> automate itself. I just know I am missing something because it happens
>> all the time in Java. Why won't these classes instrospect themselves?
>> Is C++ defective?
>> A: C++ is not Java or any other interpreter-assisted language. Think
>> very carefully about comparing C++ to something that is fundamentally
>> different from it. If you insist, define an object that is a
>> hierarchy, where each node of the hierarchy is a mapping of string to
>> an associative set of string to string. That's the best you're going
>> to get in C++.
>
> Interpreted has nothing to do with it. It's about introspection, of
> which Java has more than C++. I see nothing in C++ that makes it
> fundamentally opposed to introspection capabilities.

I agree with you there, Andrei.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Andrei Alexandrescu

unread,
Jan 23, 2009, 2:05:11 PM1/23/09
to
Marsh Ray wrote:
> On Jan 21, 9:59 pm, Andrei Alexandrescu
> <SeeWebsiteForEm...@erdani.org> wrote:
>> [...] I see nothing in C++ that makes it
>> fundamentally opposed to introspection capabilities.
>
> Type erasure and "You don't pay for what you don't use".
>
> How often do we see people wanting "C++ without RTTI", even when its
> overhead is pretty darn minimal. I'd be amazed if there was an
> implementation of a Java-style introspection facility that would be
> complete enough to satisfy those wanting such a thing and lightweight
> enough for those who don't. It wouldn't be the first time C++ has
> amazed me (in a good way of course) though!
>
> - Marsh
>
>

Compile-time introspection has no upfront cost. Using it, dynamic
introspection can be enabled for certain types as needed. By the way,
what is the status of C++ XTI?

Andrei

Thant Tessman

unread,
Jan 23, 2009, 2:03:48 PM1/23/09
to
Mathias Gaunard wrote:

> [...]


> And the thing with C++ is that is is one of the very few languages
> that actually allow value semantics.

This is either not true, or true only for a very misleading notion of
"value semantics."

Earlier, David Abrahams footnoted:

"...except for immutable objects, for which value semantics are
indistinguishable from reference semantics."

This is incorrect. 'Pure' functional programming languages have value
semantics. They do not have reference semantics despite the fact that
they may (and typically do) implement data structures by way of
pointers. Pointers simply allow for more efficient construction and
manipulation of certain kinds of data structures.

In Scheme (and I presume LISP) the fundamental data structure is the
pair, by which lists are constructed (and by which LISP gets its name).
The second element of the pair is typically a pointer to another pair,
whose second element is typically a pointer to another pair, whose...

It just so happens that in Scheme (and I presume LISP) these pairs allow
for destructive update of their contents, thus giving them reference
semantics. (Ironically, compilers can 'unpointerfy' a list as an
optimization technique when reference semantics aren't used.)

In contrast, Standard ML references are explicit. (They're constructed
with the keyword 'ref'.) Everything else in SML has value semantics
(with the qualification that I/O has side effects of course).

Scheme and LISP may allow for reference semantics, but it is possible
(and usually desireable) to adopt a functional programming style whereby
destructive updates are rare to non-existent in one's code. This does
not at all mean avoiding the use of pointers.

The point is that reference semantics implies the use of pointers as an
implementation detail, but value semantics and the use of pointers are
not mutually exclusive.

Bringing this all back to C++...

> That is why I personally don't use smart pointers; they still have
> reference semantics.

When a C++ programmer talks about value semantics and "deep copy," what
they're too often really talking about is a memory management technique.
C++ is different from the above languages not in its support of "value
semantics," but in its lack of support for automatic memory management.

(More than one C++ programmer has claimed that C++'s lack of GC has
little effect on the way they program. The importance of reference
versus value semantics as a design decision in C++ programs is one of
many reasons I don't take this claim too seriously.)

I use smart pointers all the time in situations where reference
semantics are never used, exactly because they make a deep copy unnecessary.

(Again, none of this should be construed as advocating the addition of
GC to C++.)

[...]

-thant

Mathias Gaunard

unread,
Jan 23, 2009, 2:00:46 PM1/23/09
to
On 23 jan, 12:18, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
wrote:

> In every other sense a reference is just a mere pointer.

void bar(int*);
void bar2(int&);

int main()
{
int foo[42] = {0};

bar(&foo[0]);
bar2(foo[0]);
}

The call to bar2 may be optimized to bar2(0);, but the call to bar may
not be optimized in any way. (foo can be made global, but that's it).
I'm not sure the standard allows this, but if it doesn't, it should.

Unfortunately, as far as I know, no compiler makes that kind of
optimizations.

Mathias Gaunard

unread,
Jan 23, 2009, 2:13:37 PM1/23/09
to
On 23 jan, 12:18, Thant Tessman <thant.tess...@gmail.com> wrote:

> I'm not convinced this is good advice. Asserts are for sanity checks.
> They're for making sure your code is doing what you think it's doing.
> Asserts are not for checking whether a function is being passed an
> invalid value. A function--especially a library function--needs to
> explicitly test for invalid values and signal an appropriate error, not
> just abort.

If you test invalid values and raise an error, then there is no
precondition involved, because you don't invoke undefined behaviour if
you do provide these values.

Think of the operator[] of std::vector as an example. The precondition
is that the index must be between 0 and the vector size minus 1. If
you fail to satisfy that condition, you invoke undefined behaviour.
If your standard library provides a debug mode, it will assert that
this precondition is met in that mode.

A precondition is for something that may NEVER happen. And thus,
checks should only exist in debug mode and the error should not be
recoverable.
If something SHOULDN'T happen, but may well happen, then you have to
check the values and raise an error.

David Abrahams

unread,
Jan 23, 2009, 2:10:39 PM1/23/09
to

on Wed Jan 21 2009, Le Chaud Lapin <jaibuduvin-AT-gmail.com> wrote:

> On Jan 14, 12:42 pm, David Abrahams <d...@boostpro.com> wrote:
>> Don't worry, the validity of the hot bunny's statement is completely
>> dependent on implementation details of the particular compiler in
>> question.
>
> You're right, of course, but I always qualify that my assertion:
>
> "Right now, in the year [year of assertion], on Microsoft Windows OS's
> and very likely others, making destructor virtual solves the inter-
> module new/delete, DLL/EXE, heap-mismatch problem."
>
> Each time I discuss this statement with fellow engineers, I muse
> serendipitously about its possible reasons until I arrived at the
> conclusion that, though it is conceivable that the implementation
> might be otherwise, it is most-likely not, as the way it is makes much
> more sense to compiler-writer, all things considered.

I believe you can create the problem on Linux when you get into things
like this:
http://mail.python.org/pipermail/python-dev/2002-May/024075.html

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Francis Glassborow

unread,
Jan 23, 2009, 2:13:36 PM1/23/09
to
Maxim Yegorushkin wrote:

> References were introduced to the language to allow for operator
> overloading. It's essentially a constant pointer with automatic
> address-of operator application to the initialiser and automatic
> dereference. There are also "reference to const can be bound to an r-
> value", "reference is assumed to be non-NULL", and "no pointer
> arithmetic" relaxations on top of the standard pointer semantics. But
> that is about it.
>
> In every other sense a reference is just a mere pointer.
>

That is true in Java but I think it is an over simplification in C++.
Java references are pointers, C++ references are commonly implemented as
pointers but their semantics are markedly different from C++ pointers.
Just consider the effect of take the address of a pointer versus taking
the address of a reference. In addition, note that you can have a
pointer to a pointer (and it is a new type) but you cannot have either a
reference to a reference or a pointer to reference.

Andrei Alexandrescu

unread,
Jan 24, 2009, 2:19:08 PM1/24/09
to
Le Chaud Lapin wrote:
[...]

> However, I am forttunate situation where essentially all code I write
> is new. I do interact with Windows code, (COM all the time..sigh), but
> even still, I follow the unsigned principle.
>
> The key is habit and consistency. When I think of a variable for the
> number of apples in a bin, I immediately think unsigned. At no point
> does consideration for signed enter my mind. I have been doing this
> for almost 20 years.
>
> If one follows this principle, it scales. Ten lines of unsigned-code
> becomes 20. Twenty becomes 200. Two-hundred becomes 2000, and 2000
> becomes 20,000. All without a single assert on should-not-be-
> negative.

If you say what I think you say, I won't even sugarcoat my opinion in
saying that this is a gross overestimation; a good candidate for "post
of the year" (and it's only January!). You can't be serious. If unsigned
numbers really slashed in half the number of lines of code at large
scale, don't you think programmers and language designers would be on it
like white on rice?

> IMO, the mental relief is gained is immeasurable.
>
> With such code, one need only worry about boundary conditions, where
> negative values can sneak in, like input from command line, etc. But
> the rest of the code interacts *very* well together.
>
> I think that not many people have take this path, not fully at least,
> so they cannot properly judge the relative merits, IMHO.
>
> Note that, if what I am saying is true, that it is possible to write
> unsigned code this way, that the mental relief is significant when
> doing it this way compared to the signed way, and that it is possible
> for a single individual to be consistent in using unsigned types in
> this manner without burden of forethought or making a mistake (just as
> person would not make a mistake falling randomly while walking), the
> arguments against it would be, by definition, moot.
>
> OTOH, if some says to me, "Chaud, I tried it your way. I wrote 50,000
> lines of unsigned int code with no signed anywhere, and I did not
> experience any mental relief or other joy you promised....", that
> would be a different story. ;)
>
> BTW Andrei, I vaguely recall you being on my side of the signed/
> unsigned fence in a previous discussion of this topic. :)

I got convinced otherwise, and on the Usenet no less. Miracles do
happen. But I still use unsigned whenever a natural number is needed,
yet without much joy because I know the limits of the convention. By the
way, the problem is harder than it might seem. We tried to design a new
unsigned for the D programming language. We had the advantage of an
"almost clean" slate in the sense that we could afford to not accept
some C++ expressions, just could not accept C++ expressions and
attribute different meaning to them. In spite of valiant efforts and a
trip through many modern type system appurtenances, we couldn't come
with conversion rules that wasn't either too restrictive or almost as
lax as C++.


Andrei

Dave Harris

unread,
Jan 24, 2009, 2:19:24 PM1/24/09
to
jaibu...@gmail.com (Le Chaud Lapin) wrote (abridged):

> The key is habit and consistency. When I think of a variable for the
> number of apples in a bin, I immediately think unsigned. At no point
> does consideration for signed enter my mind. I have been doing this
> for almost 20 years.
> [...]

> With such code, one need only worry about boundary conditions, where
> negative values can sneak in, like input from command line, etc.

Or anything involving subtraction. You can't rely on (a-1) < a if a is
unsigned.

-- Dave Harris, Nottingham, UK.

--

Bo Persson

unread,
Jan 24, 2009, 10:18:05 PM1/24/09
to

If the previous version of the function was using an int parameter,
the max_value is supposedly <= numeric_limits<int>::max().

Changing the parameter type to unsigned removes the check for negative
values, but introduces one or two other problems:

Can the function now handle parameters larger than
numeric_limits<int>::max() ?

When I call it with f(-1), is it really an advantage to have the call
transformed into f(4 billion) ?

Bo Persson

--

Bo Persson

unread,
Jan 24, 2009, 10:16:47 PM1/24/09
to
Tony wrote:
> "Le Chaud Lapin" <jaibu...@gmail.com> wrote in message
> news:90e9f907-b5cc-42a6...@q9g2000yqc.googlegroups.com...
>
>> Q: How do I return an error code from a constructor if exceptions
>> are not available:
>> A: You should use exceptions.
>
> A: Don't write constructors that can cause errors. Constructors are
> mostly for trivial initialization only. Don't use RAII everywhere:
> RAII is for simple little classes like a lock class that will
> ensure that the lock will be released when a scope is exited. RAII
> is not for something like "class MyApplicationProgram" that
> launches a chain-reaction of constructors on instantiation. If
> something is procedural, code it up that way rather than
> shoe-horning everything into an OO extremism. Have more than just a
> hammer in your toolbox or else everything will look like a nail.
>
> (Being "a bit" facetious).
>

Yeah, a bit. :-)

If you cannot initialize the application properly, what do you do? Run
half the application?

If you don't have to request all the resources during construction,
don't. If you have to, you have to, and the only option is failing.

Bo Persson

Marsh Ray

unread,
Jan 24, 2009, 10:21:46 PM1/24/09
to
On Jan 23, 5:18 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
wrote:
> [...]

> You are trying to solve a good problem (NULL-pointer dereference),
> however, you are hacking on the leaves, rather than on the root of the
> it.
>
> The root of the problem is that pointers, being what they are, require
> good skill and care. The only real solution is discipline.

One could use that same logic to argue against the usefulness of
'const' or any number of other features provided by the type
system. So "Good skill and care" have to be considered baseline
assumptions in any serious discussion.

Experience shows that some percentage of uses of, say, strcpy/strcat()
are going to have pointer bugs. The amount of skill and care help
determine that percentage, but realistically, std::string will catch
stuff at compile that would have been bugs otherwise.

> > 1. The domain of a pointer type includes this value called 'null',
> > whereas references do not.
>
> Where does it help?
>
> It only helps with casts over a hierarchy, because a pointer cast that
> adds a positive or negative delta to the address must not adjust NULL
> pointer. Whereas doing the same cast using a reference destination
> type does not have to check its input for NULL.

If it doesn't help, it's only because doing anything interesting with
a
null pointer is undefined behavior anyway.

> > > A reference argument to a function is the same as a
> > > pointer argument on the binary level.

C++ doesn't have a 'binary level' definition of references and
pointers
like you imply, though current implementations often enough do.
But as compilers, runtimes, GCs, VMs, CPUs, etc. evolve, there's no
reason to make unnecessary assumptions about that in the future.
Consider
the amount of rework (or outright code abandonment) caused by
programmers
making unnessary assumptions about conversions between pointer and
integral types.

> References were introduced to the language to allow for operator
> overloading.

You may be on to something there.

What's inherent about "string_abc + string_def" that needs references
rather than pointers? Couldn't it have been implemented with pointers
somehow? Could the reasons for references include some useful semantic
differences perhaps? (goes and looks for copy of D&E)

> It's essentially a constant pointer with automatic
> address-of operator application to the initialiser and automatic
> dereference. There are also "reference to const can be bound to an r-
> value", "reference is assumed to be non-NULL", and "no pointer
> arithmetic" relaxations on top of the standard pointer semantics. But
> that is about it.
>
> In every other sense a reference is just a mere pointer.

Ya, other than that ... :-)

It's enough to conclude that using a reference where a reference will
do communicates more precisely to the compiler (and other programmers)
what the code is supposed to do. This would be useful even without
their syntactic conveniences.

Over and over again this has saved me, and sometimes in unforseen ways
in the future as designs and languages evolve.

Having complete and detailed type information at compile time has got
to be one of the most powerful enablers of optimizations. Giving the
compiler a less-restrictive type can never improve things.

> > > Let's compare [...]
> > > Can you spot the difference?
>
> > Sure, some compilers will generate identical code for some examples.
>
> Which compilers will not?

Beats me. There are enough reasons to favor references over pointers
at the
language level that I don't need to research specific compilers.

- Marsh

Le Chaud Lapin

unread,
Jan 24, 2009, 10:29:49 PM1/24/09
to
On Jan 24, 1:19 pm, Andrei Alexandrescu

<SeeWebsiteForEm...@erdani.org> wrote:
> Le Chaud Lapin wrote:
> > If one follows this principle, it scales. Ten lines of unsigned-code
> > becomes 20. Twenty becomes 200. Two-hundred becomes 2000, and 2000
> > becomes 20,000. All without a single assert on should-not-be-
> > negative.
>
> If you say what I think you say, I won't even sugarcoat my opinion in
> saying that this is a gross overestimation; a good candidate for "post
> of the year" (and it's only January!). You can't be serious. If unsigned
> numbers really slashed in half the number of lines of code at large
> scale, don't you think programmers and language designers would be on it
> like white on rice?

I think you misunderstood.

What I mean to say is that we have a problem, trying to figure out
what to do about negative values for inherently non-negative
quantities. We currently "solve" this problem using "asserts".

But there exists an altenative model, which necessitates an
alternative mindset. This alternative model, if followed consistently,
up to 20,000 lines of code and beyond, would obviate the very question
of the problem. The problem is one that we ourselves created out of
habit and is not a fundamentally necessity of the language.

Early in the days of C, our preference for integral quantity was
'int', for several reasons, including being the default type for naked
integral constants, shortness of length, etc. If he had instead
preferred unsigned int, and there would only remain relatively few
situations inter-type casting would be necessary. It should be
intuitively obvious that such situations would not be untenably
absurd, certainly less absurd than throwing exception from a function
because of a negative value when that function has no business
thinking about negative values in the first place.

The thesis of what I wrote above is that this alternative habit scales
well with consistency, such that its effect propagates throughout the
system.

Liberation does not occur without consistency. For example, if faced
with a 500,000-line 5-year-old program that is riddled with ASSERT-ON-
NEGATIVE in every imaginable place, it might not make sense to use
unsigned.

I, however, do have a 50,000 line project, where there are zero
instances of ASSERT because of negative values, and all the subtle
type-conversion issues are non-issues because the type space is
iherently 99%+ unsigned anyway, and for those few places where it is
not, I use static_cast<>...*but*...as it turns out, in such places, it
actually makes intuitive sense that the conversion is necessary. IOW,
I never encounter a situation where I am blindsided by unanticipated
conversion irregularities. I see it coming well in advance.

> > BTW Andrei, I vaguely recall you being on my side of the signed/
> > unsigned fence in a previous discussion of this topic. :)
>
> I got convinced otherwise, and on the Usenet no less. Miracles do
> happen. But I still use unsigned whenever a natural number is needed,
> yet without much joy because I know the limits of the convention. By the
> way, the problem is harder than it might seem. We tried to design a new
> unsigned for the D programming language. We had the advantage of an
> "almost clean" slate in the sense that we could afford to not accept
> some C++ expressions, just could not accept C++ expressions and
> attribute different meaning to them. In spite of valiant efforts and a
> trip through many modern type system appurtenances, we couldn't come
> with conversion rules that wasn't either too restrictive or almost as
> lax as C++.

That's interesting. I would say...let things be as they are in C++.
Never been a problem for me. :) Because of unsigned, and not in spite
of it, I do not encounter the subtle conversion issues that happen so
often.

The more I think about this issue, the more I suspect that the so-
called bad habit is induced by secondary bad-habts, one of which is
overloading the return value for a function to both:

1. return a value that is an inherently unsigned quantity
2. return an error.

The best example I know of is SetFilePointer from Windows systems. I
suggest all readers ask themselves if they would have designed this
function this way today:

http://msdn.microsoft.com/en-us/library/aa365541(VS.85).aspx

The return value is too busy trying to be all things. I might have
been better to simply return bool for success/failure, and use
GetLastError() to see what happened if there was an error.

Another example:

Question: What are the actual integral values for the macros

1. S_FALSE
2. S_OK

...in the land of COM programming?

Answer:

1. S_FALSE = 1
2. S_OK = 0;

Not only that, sometimes you can ask "is the return value == S_OK", to
see if everything is ok, and sometimes you cannot - you have to use a
special macro.

In the future model, a regular model might be found, one that
Microsoft has been moving toward several years now, which is, "Yay/
Nay" for the return value and some kind of GetLastError() to see what
went wrong:

So instead of this:

long int get_file_size (fp); // returns < 0 if there is an error

We will have this:

bool get_file_size (fp, unsigned long int &); // returns false if
there is error.
int get_last_error(); // Tells you what went wrong.

That would be one step closer to getting ride of ASSERTS in functions
that only understand inherently non-negative values.

Anecdote:

I vaguely recall a large computer company (Honeywell?) advertising in
mid 1980's claiming that they had "new technology" that could allow
existing customers' file sizes to grow larger than 2147483647 bytes,
up to 4294967295 bytes. [Hmmm...wonder what happened here? ;)] The add-
on package was extremely expensive.

-Le Chaud Lapin-

Mathias Gaunard

unread,
Jan 24, 2009, 10:21:30 PM1/24/09
to
On 23 jan, 20:03, Thant Tessman <thant.tess...@gmail.com> wrote:
> Mathias Gaunard wrote:
> > [...]
> > And the thing with C++ is that is is one of the very few languages
> > that actually allow value semantics.
>
> This is either not true, or true only for a very misleading notion of
> "value semantics."

Just to clarify.
T a;
T b = a;
b.mutate();

a is not mutated -> value semantics
a is mutated -> reference semantics

Of course, when dealing with references/pointers, it is important not
to confuse mutating the object and mutating the pointer. Pointers are
values by themselves so we have to agree on what the value of an
object is on a case-by-case basis: for some it is the pointee, for
some it is the pointer.

>
> Earlier, David Abrahams footnoted:
>
> "...except for immutable objects, for which value semantics are
> indistinguishable from reference semantics."
>
> This is incorrect. 'Pure' functional programming languages have value
> semantics.

In pure functional languages, there is no state. All objects are
immutable.
So value and reference semantics are equivalent to the user.

It's just not interesting to talk about it at all then, except as
implementation strategies.


> In Scheme (and I presume LISP) the fundamental data structure is the
> pair, by which lists are constructed (and by which LISP gets its name).
> The second element of the pair is typically a pointer to another pair,
> whose second element is typically a pointer to another pair, whose...
>
> It just so happens that in Scheme (and I presume LISP) these pairs allow
> for destructive update of their contents, thus giving them reference
> semantics. (Ironically, compilers can 'unpointerfy' a list as an
> optimization technique when reference semantics aren't used.)

So you say it has reference semantics, but if it is proven never to be
mutated, the implementation may fall back to value semantics.
Well, that's exactly what has been said: they're equivalent if there
is no mutability, so it doesn't change anything to the visible
observable behaviour.

I don't know enough about lisp to talk about it however.


> In contrast, Standard ML references are explicit. (They're constructed
> with the keyword 'ref'.) Everything else in SML has value semantics
> (with the qualification that I/O has side effects of course).

You're forgetting records with mutable fields (which are the thing
closest to C++ objects).
a ref is actually the same as a record with a single mutable field.

type t = {mutable value : int};;

let a = {value = 0} in
let b = a in
b.value <- 1;
print_int a.value;; // prints 1

Of course, the let/in syntax makes it obvious a and b are really
aliases for the same thing: {value = 0}.
ML doesn't really have variables like C++.

Since there is nothing else in ML that can be mutable (except for
syntactic sugar in for loops I guess), talking of value vs reference
semantics doesn't make much sense for these.

By the way, I find ML comparison to be fairly weird. = means
structurally equal and == means physically equal.
Here, we have a = b and a == b.
However, if b was defined as to be an alias of the same thing as a,
but not as an alias of a, we would have a = b but not a == b.
== really exhibits an implementation detail IMHO. I would personally
find ML more coherent if all the same values had the same address,
since we should be aliasing immutable symbols in the first place with
let/in. Heh, that'd almost be Haskell.


> Scheme and LISP may allow for reference semantics, but it is possible
> (and usually desireable) to adopt a functional programming style whereby
> destructive updates are rare to non-existent in one's code. This does
> not at all mean avoiding the use of pointers.
>
> The point is that reference semantics implies the use of pointers as an
> implementation detail, but value semantics and the use of pointers are
> not mutually exclusive.

Copy-on-write is typically the kind of implementation for value
semantics that uses reference semantics with GC or similar as an
implementation detail.

One contra-argument against COW is simply that if you copy, it's
because you need to, so you might as well do so eagerly. Implicit
sharing (of which COW is a form) is also very useful when you share
only partial information: but again it can be argued you can have even
better code by being explicit about it.

>
> Bringing this all back to C++...
>
> > That is why I personally don't use smart pointers; they still have
> > reference semantics.
>
> When a C++ programmer talks about value semantics and "deep copy," what
> they're too often really talking about is a memory management technique.

Indeed, using value semantics, additionally to leading to safer code,
does not require complex memory management techniques, since there is
no aliasing[1].

Personally, I find it a better alternative to avoid aliasing than the
"make everything immutable" solution, which is just too restrictive.

[1] This, of course, is only strictly true if you only pass by value.
However, passing by const-reference and passing by const-value are
semantically equivalent, so the observable behaviour is the same; plus
passing a reference to a function is usually not a problem since the
object being passed will necessarily outlive the function unless said
function is called asynchronously, which is quite specific.


> C++ is different from the above languages not in its support of "value
> semantics," but in its lack of support for automatic memory management.

C++ allows a lot of automatic memory management techniques just fine.
(the usage of the stack is one, for example)
Even precise garbage collection *can* be done, albeit non-optimally,
in standard C++ if you use special pointer types (shared_ptr cannot do
it because it takes an already allocated pointer, but another
interface could).

The thing is that C++ doesn't apply garbage collection to the whole
environment, because it is an intrusive mechanism and C++ doesn't want
to put it on people that don't need it.


> (More than one C++ programmer has claimed that C++'s lack of GC has
> little effect on the way they program. The importance of reference
> versus value semantics as a design decision in C++ programs is one of
> many reasons I don't take this claim too seriously.)

You're taking the problem the wrong way.
People use value semantics because it doesn't need a GC.
It's not because they don't have a GC that they use value semantics.

Value semantics also has other potential advantages: safer code (no
aliasing), deterministic resource management that works for any
resource, more efficient code due to locality and avoided
dereferences, etc.


> I use smart pointers all the time in situations where reference
> semantics are never used, exactly because they make a deep copy
unnecessary.

Then you shouldn't use them. Only use shared_ptr if you have shared
ownership going on.
I suppose you use shared_ptr to avoid costly copies when they are
actually not needed. The thing is, if they're not needed, they
shouldn't be done in the first place.
Thankfully, move semantics come to the rescue. Alternatively you can
use COW.

David Abrahams

unread,
Jan 24, 2009, 10:30:28 PM1/24/09
to

on Fri Jan 23 2009, Thant Tessman <thant.tessman-AT-gmail.com> wrote:

> Mathias Gaunard wrote:
>
>> [...]
>> And the thing with C++ is that is is one of the very few languages
>> that actually allow value semantics.
>
> This is either not true, or true only for a very misleading notion of
"value
> semantics."
>
> Earlier, David Abrahams footnoted:
>
> "...except for immutable objects, for which value semantics are
indistinguishable from
> reference semantics."
>
> This is incorrect.

Disagreed. And, reading ahead, I don't see anything you wrote that
supports you in saying so.

> 'Pure' functional programming languages have value semantics.

And they have immutable data.

> They do not have reference semantics

My point is that as a user you can't tell. When all values are
immutable, data sharing is undetectable unless you explicitly ask about
the addresses where things are stored.

<snip lots>

> The point is that reference semantics implies the use of pointers as
> an implementation detail, but value semantics and the use of pointers
> are not mutually exclusive.

I didn't say they were. Using pointers to implement value semantics is
perfectly possible, even in the presence of mutable data. If the data
is immutable, you can have value semantics with shared data.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

It is loading more messages.
0 new messages