Future of C++

J

James Kanze

On Aug 10, 10:17 pm, Pavel <dot_com_yahoo@paultolk_reverse.yourself>
wrote:

[...]
Besides, even functions with telling names can (and often do)
return value in different ways. I will take your example:

int increment(int i) { return i + 1; } // #1

int increment(int &i) { return ++i; } // #2

Increment is a verb. Thus, the first function is very purely
named, since it doesn't increment anything.

But all this is accademic, because we don't define such silly
functions. Real functions do something significant, and have
names which tell us what. (In practice, it's very, very rare
for a function to take a non-const reference to begin with. If
a function is going to modify something, it is a member of that
something.)
I know, I know.. but people (not you and me of course :)) write things
like #2, sometimes for reason (of course on the hindsight and out of
context we are ready to blame them, but think of the standard C function
-- "time_t time(time_t *);" -- because it takes an old bad pointer,
nobody has any problem with it).

Historically, I think it was "void time( time_t* )". Because (I
think; I'm not really sure about this) the earliest versions of
C didn't support returning a long. When the return value was
added, they couldn't remove the argument without breaking code.
And obviously, with the possibility of the above 2
definitions, the simple code
int i;
int j = increment(i);
poses very different different complexity to the reviewer of
the C or Java code, from one side, and C++, from another.

I think you've chosen a poor example. Suppose you are dealing
with a user defined type. In Java, you can only pass a
reference to it, and there are no references to const. All you
have to go on is the name of the function. In C++, you can
implement value semantics, if that is appropriate to the type
in question, and the function can take the argument by value.
Or you can use a const reference. At the language level, you
can express in more detail what the function really does.

In practice, of course, it's not that important, since in Java,
too, the name of the function (and the semantics of the type
you are passing) make it clear whether the object will be
modified or not.
 
I

Ian Collins

James said:
On Aug 10, 10:17 pm, Pavel <dot_com_yahoo@paultolk_reverse.yourself>
wrote:

[...]
Besides, even functions with telling names can (and often do)
return value in different ways. I will take your example:

int increment(int i) { return i + 1; } // #1

int increment(int &i) { return ++i; } // #2

Increment is a verb. Thus, the first function is very purely
named, since it doesn't increment anything.
purely?
 
J

James Kanze

James said:
Pavel said:
Bo Persson wrote:
[...]
int i = 0;
f(i);
myprint(i);
If it is written in Java (or C, for that matter), I can
tell with some certainty that myprint() receives 0 as an
argument. But to tell something about it in C++ I have to
know whether f() takes a reference or a value
Using a better name than f() would help a lot!
You are trying to avoid the point really hard -- I am clapping my hands!
No he's not. He's addressing the real problem directly.
Imagine you changed f() to doSomething() where Something
really speaks for itself. In C, you can just leave it at that,
in C++ you have to say:
doSomethingAndNeverChangeTheArgument(i) or
doSomethingAndMaybeChangeTheArgument(i)
If you have to say that, then doSomething didn't speak for
itself. I've never used anything like
"AndMaybeChangeTheArgument" in the name of a function, and I've
never found readers of my code had any problem knowing which
functions modified their arguments, and which didn't. Even in
the standard library (which doesn't particularly shine by its
naming conventions), it seems rather obvious that v.insert()
will change the vector, and that v.size() won't. I don't need
to look and verify that size() is a const function, and insert()
isn't.
Standard Library is a very bad example because it does have
consistent conventions and it is Standard, that is, a C++
programmer is expected to know these conventions.

The standard library is in many ways an ad hoc collection of
code which comes from different sources. Different parts follow
different conventions. And significant parts are not
particularly well designed.
Any proprietary program of a considerable size is full of its
own conventions, most of the time they are little less
consistent than in the Standard (both because they have to
cover bigger code base and their authors did not have luxury
of decades to polish these conventions).

Most large projects I've seen have much more rigorous naming
conventions than the standard. Because they were developed by a
single organism, which defined its standards.
Now can you please fix the code instead of only criticizing it? Bo gave
a good example:
int i = 0;
int j = increment(i);
cout << i << endl;
Please could you apply your vast experience of working in
well-run programming teams at which you hinted previously, and
replace the ill-selected names in this code to make it obvious
for a reviewer what line #3 is supposed to output without
their having to resort to the information outside of this
original source code?

Well, everywhere I've been, we'd have written:

int i = 0 ;
int j = i ++ ;

:). The original isn't that bad as it stands; given the
meaning of the English word increment, it's fairly obvious that
i will be equal to 1. But the standard name for incrementation
in both C++ and Java is ++. Given a function which increments a
numeric value any other name is a little bit of obfuscation.

And if i and j weren't int, but BigInteger, the correct way of
writing it would still be:
BigInteger i = 0 ;
BigInteger j = i ++ ;
(Regretfully, you can't write this in Java.)

The example is probably poorly chosen, however, since most
functionalities don't have "standard" names like this, defined
by the language. But if you take real examples: does
Transaction::commit() modify the object it's called on? You bet
it does (and it modifies all of the objects that are part of the
transaction), or it's very poorly named. Does
Transaction::isCommitted() modify the state of the object it's
called on? It better not.

The only real difference between C++ and Java here is that in
C++, I can declare a parameter or a function const, so the
compiler will catch any accidental mistakes; if I pass a
Transaction to a function in C++, and I'm not sure whether that
function might commit or not, I can look at the declaration of
the function, and see whether it was passed by const reference,
or by non-const reference; in Java, I would actually have to
look at the implement code of the function. Of course, if the
functions are correctly named, and do what they are supposed to,
and I know why I'm calling the function, it shouldn't be a
problem in either language. And Transaction::commit is perhaps
a poor example, since normally the only function which will
commit a transaction is the one which created it. But I can
still know, in C++, whether the called function might add
objects to the transaction or not. More importantly, in C++,
the destructor of Transaction will do a roll back if commit
hasn't been called; in Java, I have to use a catch block (which
is easy to forget), and call the roll back explicitly; in other
words, in C++, the Transaction can be completely responsible for
the semantics of what it manages, where as in Java, it must
count on explicit collaboration from the client code.
Oh, and yes -- what name choice would convince the reviewer
that the (renamed, of course) increment() function actually
behaves with regard to its argument exactly as its new perfect
name unambiguously implies it is supposed to behave?

Why would you want to rename a function whose name already
explicitly says what it does?
[...]
I am not saying there is no good C++ programmers (and, by
now, you should have an idea what I mean by "good" -- it
includes "not extra expressive") -- but my mileage says
there are more Python, C and Java good programmers than
C++.
That's certainly not the case of Python (the total number of
Python programmers is probably less than the number of good
C++ programmers), and almost certainly not for C. But it
is, in many ways, irrelevant. Supposing you meant the
percentage of good programmers, rather than the absolute
number:
Yes, that is what I meant.
I mostly agree with all that follows,

Well, that's something:).
but you introduced a lot
of new concepts, so I have some questions (see below):
How do you define small vs big languages?

By use. And it's a continuum, of course. While it's obvious
that Java, C# and C++ are at one end, and Modula-3 and a lot of
languages I've never even heard of are at the other, I'm not too
sure where I'd put Ada, for example.
From the above, it seems it is by the number of practitioners;
then, I would say, C++ is now fairly small as comparing to
Java;

Judging from what I see, there are more programmers using C++
than Java at present, for whatever reasons. I'm not aware of
any real hard statistics, but in my field (and in many others),
Java just hasn't made any inroads (except as small GUI
front-ends, which represent less than 5% of the total code).
then, following your logic, reviewing Java code must be,
on average, more time-consuming and disappointing activity
than that of C++ code (per LOC or whatever other metrics) --
but my experience tells different. But let me know if I
guessed wrongly.

Java tends to be used for smaller, less critical projects. I
suspect that a lot of Java code simply isn't reviewed. My
experience, however, is that it is more difficult to review Java
code than C++ code (supposing both well written), for two
essential reasons:

-- Java doesn't keep the formel specification of the interface
well separated from the implementation. In well run shops,
this isn't too much of a problem, because the formal
specification of the interface will be written (and
reviewed) in UML, or something like that. (I've seen the
same technique applied to C++. Human programmers never
touched the header file, not even to read it.) But unless
you're using such tools (and a lot of places don't use them
rigorously), the mixing of the interface specification and
the implementation code is a pain.

-- Java is less expressive with regards to some of the more
fundamental distinctions: value type vs. entity, for
example. Again, if the project is well run, the name of the
type pretty much makes things clear, and code review
verifies that the semantics do correspond with the name: in
Java, a class with value semantics will be final, and not
have any mutating functions, for example; in C++, it will
support copy and assignment (but not derivation). (Note
that the default in Java is an entity class---you need extra
work to make a value---, whereas in C++, it's the opposite.)
(In earlier Java, the lack of strictly typed containers was
also a real pain. And even today, you have to constantly
check that a try...finally hasn't been forgotten, where as
in C++, you can't forget to call the destructor of a local
variable.)
Same experience -- but with some replaced epithets. No, I do
not blame you for s-word, I am just not sure we are thinking
of the same issue. To clarify, please could you classify the
code like my snippet above according to your standards you
applied in the preceding paragraph -- whether it is, say, bad
code in your opinion, and if it is, how would you fix it.

I'd say that it's really too small (and unrealistic) to judge.
But on the whole, I don't see any real problem with it. The
name of the function says that it will modify its argument.
Local conventions specify what the return value should be
(although I'm slightly sceptical about using it, since local
conventions vary, a new hire might not feel at home with
them, and there's really no reason to use the return value
here).
(To be on record, here is my answer to this question: it is
just the code the like of which C++ (and C or Java)
practitioners are likely to meet in any development
organization, no matter how run, that supports large enough
amounts of code. But their difficulties with this code will
differ depending on whether it is C++ or C or Java).

First, as I say, I don't really see any problem with it. And
I've never worked in an environment where there would be one.
And finally, you can't implement an increment function in Java,
at least not for int. You need a work-around with an
intermediate class (which definitely makes things harder to
read): look at the hoops the Corba binding in Java has to jump
through in order to implement out parameters.
Yes, of course, we are are talking about the languages that
could be used. Basically, C++ was unique in that it was the
only [wide-spread] language "with OOP" that could provide
winning efficiency.

Strictly speaking, that's not really true. When I started C++,
Eiffel was just as available, and Modula-3 wasn't really an
impossibility. What made C++ unique was its C compatibility
(which is also a source of some of its most serious problems: an
illegible declaration syntax full of ambiguities, undefined
behavior at the drop of hat, etc.)
I believe OOP is useful for big project,
BTW -- it takes some work to imitate them in C although it is
possible and was done successfully in those "poorly run"
development organizations I worked for. It required some
discipline, but probably less than C++ requires so those
organizations were not good enough for C++ (can't help it,
sorry)

My experience with C was that you'd define a struct and a set of
functions which manipulated that struct, and cross your fingers
that no one accessed the struct except through your functions.
In C++, you have classes, with access specifiers. If only for
that, C++ is more effective than C.

C++ can, of course, be misused. I have seen people add
complexity to a project by using a feature simply because it was
there. It requires management.
Java was very slow with exposing system level functionality
and did not perform too well for long time, so C++ was
practically the only language of choice for such apps.

But performance is not the reason Java failed in so many
application domains. The fact that it doesn't support large
scale, reliable programming is a more fundamental reason.
But my point is not about technical aspects of C++ -- it is
quite competitive in those, but about its communication
aspects (I would use the word "expressiveness" but it was
already overused on this thread, I think).

C++ allows expressing many things in the language which must be
expressed in other forms in other languages. Correctly used, it
means more readable code. (Incorrectly used, of course, no
language can guarantee readable code.)
Basically, my concern is: will C++ keep enough practitioners
to make it safe for organizations to approve C++ for new
programs (I am talking about PMO programs, not computer
programs here)?

I don't know what PMO programs are, but off hand, the amount of
code written in C++ seems to be growing significantly. There
are many domains where it is practically the only language now
being used.
At some critical level, they will just say: "Yes, you guys may
be smarter than others, but we cannot find as many smarties as
we need and we learned hard way we can only use the best for
C++ specifically and it is too expensive to really train them
inside (as opposed for, say, Java or Python or C#) and you are
so expensive to both hire and fire (C++ legacy is a bigger
liability FORTRAN or C, from my experience) -- we are going to
start this application in Java (or even C) to reduce our risks
of not finishing it or not being able to fix or maintain it in
the future". In fact, I have been seeing it happening during
the last 3-5 years (I have also ween exceptions where
organizations returned to C++ from Java but these were just
that -- rare exceptions, and these are now struggling with
their C++ projects).

Except that it is significantly more difficult to maintain Java
than C++. Java's successes have mainly been in domains where
throw away code and very small programs predominate.
Hmm. I cannot recall another language where same piece of code
can mean so many different things depending on the non-local
[from the reader's perspective] context.

Anytime you call a function, you have to deal with "non-local"
context. What does the function do? Even any time you use an
operator: a / b does something different depending on the types
of a and b. That's why you use expressive names. '/' is the
"standard" name for division, so we use it for division, even if
the semantics of division aren't quite the same for ints and for
doubles. Java looses out here, because you can't use this name
for BigDecimal or Complex; C++, of course, allows you to use it
for addition. In other words, C++ leaves it up to you whether
you write clear, readable code or not; Java doesn't allow you to
write clear, readable code. (But of course, this is more or
less a special case.
My original example was the simplest one I could come up with
-- I don't even want to start on operator overloading etc. I
mean, it may not be the most popular feature these days, but
this does not help much while reviewing the others' code: who
knows what the other guy was thinking when he wrote this or
that piece.

If you don't know what he was thinking, you've got problems,
yes. And operator overloading is abused... including in the
standard library (e.g. STL iterators). But it's one of those
things that, when you need it, you need it: used correctly, the
code is clearer than it could possibly be without it.
And the beauty of a preprocessor is a completely different
song (C shares the feature, but not Java or C#).

:)

The preprocessor is a mis-feature, best avoided. With a few
small exceptions: in Java, how do you efficiently and
automatically insert the filename and line number in log
records? But for the most part, it's a powerful tool for
obfuscation, and not much more. (You need it in C for manifest
constants, but not in C++.)
 
J

James Kanze

James said:
On Aug 10, 10:17 pm, Pavel <dot_com_yahoo@paultolk_reverse.yourself>
wrote:
[...]
Besides, even functions with telling names can (and often do)
return value in different ways. I will take your example:
compare:
int increment(int i) { return i + 1; } // #1
and
int increment(int &i) { return ++i; } // #2
Increment is a verb. Thus, the first function is very purely
named, since it doesn't increment anything.

? What was I thinking of? (Clearly, maybe?)

Anyway, it's a bad example, because unlike most functionalities
you're likely to need, C, C++ and Java have a standard name for
this already: ++. If you don't name this function ++, it's
really obfuscation.

Of course, if the type is BigDecimal, rather than int, you're
going to have a difficult time naming it ++ in Java:).
 
J

James Kanze

* James Kanze:
[...]
Besides, even functions with telling names can (and often
do) return value in different ways. I will take your
example:
compare:
int increment(int i) { return i + 1; } // #1
and
int increment(int &i) { return ++i; } // #2
Increment is a verb. Thus, the first function is very purely
named, since it doesn't increment anything.
purely?
? What was I thinking of? (Clearly, maybe?)
As I recall, in Pascal those Peano arithmetic functions were
called Succ and Pred.

I probably meant "clearly", but "poorly" is better. In C++,
it's name is ++.
If recursion was more used in C and C++ we'd presumably have
standard operators for them ("+ 1" works nicely for numeric
types, but not for e.g. enums).

What does that have to do with recursion? If enum's were
enumeration types, the language could doubtlessly define ++ and
-- for them, as well. They aren't, so it can't.
I haven't checked C++0x draft, but perhaps, if it isn't there already,
namespace std
{
template< typename T >
T succ( T x ) { ++x; return x; }
template< typename T >
T pred( T x ) { --x; return x; }
}
wouldn't be so bad an idea (for clarity I didn't sketch in use
of e.g. traits type to use for choice between implementations,
e.g. for an immutable arithmetic type T one would instead have
"return x + 1;" and "return x - 1;").
Sometimes when using iterators this functionality is needed.

They'd be useful for enum types as well, with the restriction
that they're really only meaningful for enum types without
explicit initializers for the enum values. Of course, for that,
you'd need a traits which caused the functions to add the
necessary casts, and which automatically chose the correct
underlying type.
 
P

Pascal J. Bourguignon

James Kanze said:
They'd be useful for enum types as well, with the restriction
that they're really only meaningful for enum types without
explicit initializers for the enum values.

Why the restriction? On contrary, explicit enum initializers is only
an irrelevant low level mapping.

namespace Magic { enum Magic { normal = 1, more = 42, full = 13 }; }

Magic::Magic m=Magic::normal;
m++;
assert(m==Magic::more);
assert(m==/*low level value you don't really want to watch*/42);
m++;
assert(m==Magic::full);
assert(m==/* close your eyes till the eoln*/13);
 
B

Bo Persson

Alf said:
I haven't checked C++0x draft, but perhaps, if it isn't there
already,
namespace std
{
template< typename T >
T succ( T x ) { ++x; return x; }

template< typename T >
T pred( T x ) { --x; return x; }
}

wouldn't be so bad an idea (for clarity I didn't sketch in use of
e.g. traits type to use for choice between implementations, e.g.
for an immutable arithmetic type T one would instead have "return x
+ 1;" and "return x - 1;").

They are there, called 'next' and 'prev', but intended for iterators
only.
Sometimes when using iterators this functionality is needed.

See! :)


Bo Persson
 
P

Pavel

James said:
I am afraid I was not clear enough. No macros was assumed to
be involved in the above (see "*some* certainty" cited above).
The actual point is that in C++ you cannot say whether
void f(int i);
or
void f(int &i);
is called, without looking elsewhere and getting distracted by
that.

But that's a false problem. Which one is called depends on the
semantics of the function, and you have to know that in order to
understand the code. And even if you know which of the above
actually corresponds, you still don't know the semantics.
(on some platforms, the fastest way for me to say is to vi the
output of the preprocessor -- that's from where the reference
to the p).
"Expressive" to me means something close to the opposite to
having to go to the reference (header file, browser window
etc) to understand what the "expressive expression" really
does. You will probably agree that the syntactic meaning of
something as simple as " int i = 0; f(i);" is supposed to
become obvious without much effort -- if it is not, getting to
a comfortable level of understanding of a syntactic meaning of
a little bit less trivial piece of code is supposed to take
infinite amount of time by definition -- isn't it?

Yes and no. What should be obvious without any effort is the
effect of the statement. Which depends on the semantics of the
function, and not just minor syntactic details. In well written
code, the name of the function will give enough information, and
in poorly written code, you're stuck. But that's true in just
about any language; I don't see where C++ causes additional
problems here.
Apparently your mileage differs from mine. Java became quite
agile (somewhere after 2000-2001 when major bugs were
eliminated from implementations) but in my experience C++
projects blew by far more deadlines than FORTRAN, Assembler, C
or Java (after the latter matured with the adoption of 1.3.1
and 1.4) -- and some of the firms I worked for were really
well-run hardware/software shops.

Apparently not.
Of course I mean the deadlines for commercial products, not
some semi-working prototypes.

That's interesting, because it is incredibly difficult to get
really robust Java. The language actually bans many of the
techniques which would help you here.

[...]
Yes, I have heard above this free cheese before -- but have
never eaten one :).

In other words, you've never actually worked in a place which
had a good software development process in place.
I guess, I am a poor guy -- not only in software development but never
in real life did I see benefits without the costs.

BTW do you really believe any development process, especially a
successful one, can be build around "things" rather than around people
and their motivations? I mean -- "things" will tolerate any management
but people may become very uncooperative..

I am not trying to change a subject: basically, looking for technical
solutions for a non-technical problems is a common blunder of technical
people (I am including myself here). I just recalled something that I
feel is very relevant: Ludwig Wittgenstein, an Austrian philosopher, in
the first past of his life was looking for a "perfect language"
(metaphysical language to describe real life); late Wittgenstein came to
the conclusion that speaking a perfect language is like walking
frictionless ice: no connection to reality; in our case, "most
expressive" language has difficulties in getting traction with the
programmers and their management (yes, it is the language that is in
trouble if there is no traction, not people: tools exist for people, not
vice versa and people prove this by changing stubborn tools).

A programming language that is extra "expressive" and "concise" has
difficulties attracting practitioners. Who other than a scientist really
would want to line up all definitions and then apply a zillion of rules
of overloading in a specific order in their sane minds to determine the
"Best Viable Function" (see 13.3.3. of the Standard) -- no matter how
logical and mathematically perfect these rules are so that they
theoretically allow the highest possible number of interpretations per a
token of the source code taken out of context? Basically, live people
have limited context memory, so the more is taken to think of syntax,
the less is left for thinking of semantics. I guess, if all those
suggestions for C++0x that try to make C++ "even better functional
language" pass, the only people who will find C++ usable will be the
language lawyers and that's when C++ will really become "legacy
language" (I heard Sun engineers calling it that back in 2004 or around
that time when they came to try to sell to us some stuff and then I was
bored and unwise enough to start arguing with them.. now it seems to me
they were right; of course, Java is coming there, too, but not as fast
as C++).

-Pavel
 
P

Pavel

James said:
On Aug 10, 10:17 pm, Pavel <dot_com_yahoo@paultolk_reverse.yourself>
wrote:

[...]
Besides, even functions with telling names can (and often do)
return value in different ways. I will take your example:

int increment(int i) { return i + 1; } // #1

int increment(int &i) { return ++i; } // #2

Increment is a verb. Thus, the first function is very purely
named, since it doesn't increment anything.

But all this is accademic, because we don't define such silly
functions. Real functions do something significant, and have
names which tell us what. (In practice, it's very, very rare
for a function to take a non-const reference to begin with. If
a function is going to modify something, it is a member of that
something.)
I know, I know.. but people (not you and me of course :)) write things
like #2, sometimes for reason (of course on the hindsight and out of
context we are ready to blame them, but think of the standard C function
-- "time_t time(time_t *);" -- because it takes an old bad pointer,
nobody has any problem with it).

Historically, I think it was "void time( time_t* )". Because (I
think; I'm not really sure about this) the earliest versions of
C didn't support returning a long. When the return value was
added, they couldn't remove the argument without breaking code.
Well, my point is that it is easy to understand whether this function
can change its argument just looking at the place in code where the
time_t variable is defined and time() is called (that is, locally).

That the issue has history only tells me that the language is viable
(good): even after redesign, under additional requirement to provide
compatibility, the syntactic meaning of the client code is still more
clear than if it were C++ function and the prototype used the reference
(of course, you can write in C++ like in C, but I mean "C++-ish" C++).
I think you've chosen a poor example. Suppose you are dealing
with a user defined type. In Java, you can only pass a
reference to it, and there are no references to const. All you
have to go on is the name of the function. In C++, you can
implement value semantics, if that is appropriate to the type
in question, and the function can take the argument by value.
Exactly. I think we are talking of the same things but our assessments
of them are directly opposite. Here is how I read what you wrote:

In C++, you can define a function in more ways (which have different
meanings, and these different meanings can be desirable in different
situations -- I agree with you) BUT the client code can look same for
more of these different meanings (which to me is equivalent to poor
readability, VERY poor).

Yes, to change the value of the argument in Java, you would have to
write something like (not necessarily this, but to localize, this seems
to be about the shortest way):

int i = 0;
int[] iHolder = new int[] { i };
int j = f(iHolder);
i = iHolder[0]; // line #4

Yes, it may look ugly (and, barring some optimizations about which I am
not 100% sure they are actually performed, be less efficient) --
however, syntactically, nobody would have any doubts that i may not be 0
after line #4, and that for this f() changing its argument is business
at normal, whether or not its name gives a good idea of its business
purpose.
> Or you can use a const reference. At the language level, you
> can express in more detail what the function really does.
But only where the function is defined, not where it is used. Remember,
we are talking about reading the client code of a function, where both a
function and a client code are written not by us, not about designing
the ideal prototype for a function. In any language, you will probably
not have problems with understanding functions written by yourself,
whether they are well or poorly designed.. at least for a first couple
of months.
In practice, of course, it's not that important, since in Java,
too, the name of the function (and the semantics of the type
you are passing) make it clear whether the object will be
modified or not.
Well, not "too". To see the semantics of the type, you need to jump to
the type definition (in both C and Java) -- but for the primitive types
it is not necessary (in C++, you have to jump to the definition of the
function in any case, even if just to see whether it takes const
reference or what).

-Pavel
 
P

Pavel

Well, everywhere I've been, we'd have written:

int i = 0 ;
int j = i ++ ;

:). The original isn't that bad as it stands; given the
meaning of the English word increment, it's fairly obvious that
i will be equal to 1. But the standard name for incrementation
in both C++ and Java is ++. Given a function which increments a
numeric value any other name is a little bit of obfuscation.
Nice, even though wrong (I meant j = ++i). But still it is only half of
the solution. I believe though I know your take on the second part
(passing by value): "int j = i + 1;", right?

But what about a little more real-life increment:

definitions:

int incrementPercentageIfPossible(int p) {
return p < 100 ? ++p : p;
}

vs

int incrementPercentageIfPossible(int &p) {
return p < 100 ? ++p : p;
}

Would you inline these, too (as well, to help the reader to clearly see
the syntactic meaning of:

int i = 0;
int j = incrementPercentageIfPossible(i);

or else how would you fix these?

Notice this is not a hypothetical example: both methods are identical
with the exception of a single '&' so a bug you are searching for (why
else would you look into this kind of code?) may be the result of a
stupid typo, not even a bad design.
And if i and j weren't int, but BigInteger, the correct way of
writing it would still be:
BigInteger i = 0 ;
BigInteger j = i ++ ;
(Regretfully, you can't write this in Java.)
I call it fortunately.
The example is probably poorly chosen, however, since most
functionalities don't have "standard" names like this, defined
by the language. But if you take real examples: does
Transaction::commit() modify the object it's called on? You bet
it does (and it modifies all of the objects that are part of the
transaction), or it's very poorly named. Does
Transaction::isCommitted() modify the state of the object it's
called on? It better not.
Yes, these are clear functions, in any of the languages we mentioned, so
no points to any. Isn't it telling to you, BTW, that the most readable
code is that uses the least of C++ "magic" (that is, that can be written
in same way in any language -- ok, in C you would have
transaction_commit() or similar, but still same clarity).
The only real difference between C++ and Java here is that in
C++, I can declare a parameter or a function const, so the
compiler will catch any accidental mistakes; if I pass a
Transaction to a function in C++, and I'm not sure whether that
function might commit or not, I can look at the declaration of
the function, and see whether it was passed by const reference,
or by non-const reference; in Java, I would actually have to
look at the implement code of the function.
> Of course, if the
functions are correctly named, and do what they are supposed to,
and I know why I'm calling the function, it shouldn't be a
problem in either language. And Transaction::commit is perhaps
a poor example, since normally the only function which will
commit a transaction is the one which created it. But I can
still know, in C++, whether the called function might add
objects to the transaction or not. More importantly, in C++,
the destructor of Transaction will do a roll back if commit
hasn't been called; in Java, I have to use a catch block (which
is easy to forget), and call the roll back explicitly; in other
words, in C++, the Transaction can be completely responsible for
the semantics of what it manages, where as in Java, it must
count on explicit collaboration from the client code.
Destructors are a good feature of C++. I am not saying C++ has too few
good features -- what I am saying is that it has way too many features
that allow writing poorly maintainable code and in fact result in
writing such code relatively much more often than in other languages
(all in my personal experience, without generalization). Of these
features a lot are good ones, but, taken together, they overload the
language (in both special and common meanings), and they would do so
even if ALL of them were good.
Why would you want to rename a function whose name already
explicitly says what it does?
No, I thought you told me I was not good in naming so I asked you for a
suggestion.
[...]
I am not saying there is no good C++ programmers (and, by
now, you should have an idea what I mean by "good" -- it
includes "not extra expressive") -- but my mileage says
there are more Python, C and Java good programmers than
C++.
That's certainly not the case of Python (the total number of
Python programmers is probably less than the number of good
C++ programmers), and almost certainly not for C. But it
is, in many ways, irrelevant. Supposing you meant the
percentage of good programmers, rather than the absolute
number:
Yes, that is what I meant.
I mostly agree with all that follows,

Well, that's something:).
but you introduced a lot
of new concepts, so I have some questions (see below):
How do you define small vs big languages?

By use. And it's a continuum, of course. While it's obvious
that Java, C# and C++ are at one end, and Modula-3 and a lot of
languages I've never even heard of are at the other, I'm not too
sure where I'd put Ada, for example.
From the above, it seems it is by the number of practitioners;
then, I would say, C++ is now fairly small as comparing to
Java;

Judging from what I see, there are more programmers using C++
than Java at present, for whatever reasons. I'm not aware of
any real hard statistics, but in my field (and in many others),
Java just hasn't made any inroads (except as small GUI
front-ends, which represent less than 5% of the total code).
Ok, not sure where you took the numbers, but my experience is completely
different. I have been doing application programming for financial
services last 3 years; before it was 5 years of middleware and EAI
frameworks; before it was GIS, embedded systems, modeling etc. Here is
an observation from those 5 years in middleware business: we offered our
EAI framework in both C++ and Java so the users could choose in which
language to write their plug-ins; but in fact, we wrote most of the
plugins for them, too. So, it started with 90% of installations of C++
and 10% in Java and ended with a reverse proportions. The users always
could select which one the wanted to use -- and their decision-making
process was mostly like this:

1. They asked -- what really worked? -- the answer was "both".

2. They asked -- what's faster? -- the answer was "almost always C++,
but marginally".

3. Then they would say.. ok I do not need to write anything additional
now (or, as a variant: maybe you guys could do this small thing for us
as a favor -- and we said "of course!") but, if we need it in the
future, Java "resources" are more readily available and time-to-market
is shorter and we won't have coredumps (they meant coredumps in *their*
code -- our framework was equally stable (or unstable, depending on how
you measure) in both languages and supported equally).

They were not even stopped by the fact a Java instance would take
several times more memory -- apparently most of them had more memory
than us... again, this is a pure observation and you can draw your
conclusions. Just remember a client cannot be stupider than a provider:
otherwise they would not have money to pay for the provider's wares and
still make profit...
Java tends to be used for smaller, less critical projects. I
suspect that a lot of Java code simply isn't reviewed. My
experience, however, is that it is more difficult to review Java
code than C++ code (supposing both well written), for two
essential reasons:

-- Java doesn't keep the formel specification of the interface
well separated from the implementation. In well run shops,
this isn't too much of a problem, because the formal
specification of the interface will be written (and
reviewed) in UML, or something like that. (I've seen the
same technique applied to C++. Human programmers never
touched the header file, not even to read it.) But unless
you're using such tools (and a lot of places don't use them
rigorously), the mixing of the interface specification and
the implementation code is a pain.
That is true about specifications; but IMHO automatically generated
javadoc and folding IDEs really level this field
-- Java is less expressive with regards to some of the more
fundamental distinctions: value type vs. entity, for
example. Again, if the project is well run, the name of the
type pretty much makes things clear, and code review
verifies that the semantics do correspond with the name: in
Java, a class with value semantics will be final, and not
have any mutating functions, for example; in C++, it will
support copy and assignment (but not derivation). (Note
that the default in Java is an entity class---you need extra
work to make a value---, whereas in C++, it's the opposite.)
(In earlier Java, the lack of strictly typed containers was
also a real pain. And even today, you have to constantly
check that a try...finally hasn't been forgotten, where as
in C++, you can't forget to call the destructor of a local
variable.)
Yeah, yeah, destructors are good, [but] see above.
I'd say that it's really too small (and unrealistic) to judge.
But on the whole, I don't see any real problem with it. The
name of the function says that it will modify its argument.
Local conventions specify what the return value should be
(although I'm slightly sceptical about using it, since local
conventions vary, a new hire might not feel at home with
them, and there's really no reason to use the return value
here).
There is, if you need to chain it

int i;
j = incrementPercentage(i) * quantify...

Why would otherwise they add return value to a time(), when returning
long became valid?
First, as I say, I don't really see any problem with it. And
I've never worked in an environment where there would be one.
And finally, you can't implement an increment function in Java,
at least not for int. You need a work-around with an
intermediate class (which definitely makes things harder to
read): look at the hoops the Corba binding in Java has to jump
through in order to implement out parameters.
I gave an example in my other post -- it might look uglier, but it's
meaning is much more unambiguous and therefore, it is much more readable.
Yes, of course, we are are talking about the languages that
could be used. Basically, C++ was unique in that it was the
only [wide-spread] language "with OOP" that could provide
winning efficiency.

Strictly speaking, that's not really true. When I started C++,
Eiffel was just as available, and Modula-3 wasn't really an
impossibility. What made C++ unique was its C compatibility
(which is also a source of some of its most serious problems: an
illegible declaration syntax full of ambiguities, undefined
behavior at the drop of hat, etc.)
I believe OOP is useful for big project,
BTW -- it takes some work to imitate them in C although it is
possible and was done successfully in those "poorly run"
development organizations I worked for. It required some
discipline, but probably less than C++ requires so those
organizations were not good enough for C++ (can't help it,
sorry)

My experience with C was that you'd define a struct and a set of
functions which manipulated that struct, and cross your fingers
that no one accessed the struct except through your functions.
In C++, you have classes, with access specifiers. If only for
that, C++ is more effective than C.
You can of course cross your fingers.. or you can make only forward
declaration of your structures available to client code.. then you do
not have to cross fingers and neither do you need access specifiers
(other than protected whose misuse is, BTW, an often trouble in C++. But
sometimes they are useful. As I said before, C++ problem is not lack of
features but their abundance and unforeseen or ignored interactions
between them). As an additional benefits you receive:

- binary compatibility across versions with different implementation but
same API (something for which in C++ you would have to use same C trick
which they decided to call Bridge for whatever reason but it is wordier
with classes and more prone to breakage)

- really fast compilation of your client code

So, I prefer forward declarations to crossing fingers. :)
C++ can, of course, be misused. I have seen people add
complexity to a project by using a feature simply because it was
there. It requires management.


But performance is not the reason Java failed in so many
application domains. The fact that it doesn't support large
scale, reliable programming is a more fundamental reason.
Hmm. Tomcat and JBoss do not strike me as either small or unreliable..
and I do not even talk of commercial software.. Think Weblogic or
Websphere. Or Eclipse with all heterogeneous pieces working together as
a charm. Or Google Web apps for that matter (including gmail and Office).
C++ allows expressing many things in the language which must be
expressed in other forms in other languages. Correctly used, it
means more readable code. (Incorrectly used, of course, no
language can guarantee readable code.)
So, which one is correct: pass parameters by value or by reference? You
have to take a position, because, used together, they *guarantee* less
readable code than in other languages (by anyone but its author).
I don't know what PMO programs are,
Program Management Office. In well-run big corporations, these are
people who decide on roadmaps and strategic investments. They are also
gatekeepers. If they say: "we go Java", no C++ projects will be funded
no matter what techies like you or me say. "Program" in PMO terms is an
activity for which recurrent financing is allocated for a number of
future years, before any concrete projects with their concrete
deliverables and cost/benefits are even identified. For PMO to be good
is to correctly envision what kind of projects will be needed and assess
all costs and risks including maintenance costs, potential lack of
resources, potential missing deadlines etc, and choose the correct
strategy for the enterprise for some years ahead.
but off hand, the amount of
code written in C++ seems to be growing significantly.
Again, my personal observation is that C++ share shrinks in comparison
to Java, C#, Python etc.. Can you name a project in which C++ *replaced*
one of these (unless they were planned as prototype languages from the
very beginning)? I see the opposite trend (upcoming Microsoft IDE
written in C# being the current point in case)
> There
are many domains where it is practically the only language now
being used.
Certainly there are. Do you honestly think it be used there in 3-5 years
as the only language? What new domains will it gain and what will it
lose? We are talking about the future, right? Is C++ coming or leaving?
My answer it is leaving.. I am not happy about it, believe me.
Except that it is significantly more difficult to maintain Java
than C++. Java's successes have mainly been in domains where
throw away code and very small programs predominate.
This is an opinion -- or personal experience. Mine is opposite (see
above). Any other opinions -- anybody?
Hmm. I cannot recall another language where same piece of code
can mean so many different things depending on the non-local
[from the reader's perspective] context.

Anytime you call a function, you have to deal with "non-local"
context.
Not any time, but often, right. But I say -- the less I have to do it
the better readability is.
> What does the function do? Even any time you use an
operator: a / b does something different depending on the types
of a and b. That's why you use expressive names. '/' is the
"standard" name for division, so we use it for division, even if
the semantics of division aren't quite the same for ints and for
doubles. Java looses out here, because you can't use this name
for BigDecimal or Complex; C++, of course, allows you to use it
for addition. In other words, C++ leaves it up to you whether
you write clear, readable code or not; Java doesn't allow you to
write clear, readable code. (But of course, this is more or
less a special case.
But C++ does not leave it up to me whether I *read* clear, readable
code. It is up to another guy whose conventions I do not always share
and, what's worse, whose judgment I am not supposed to trust when I
review his code (and I learned hard way that not to trust is the right
thing to do for this purpose). So the less uncertainty the language
encourage the writer to leave for me -- the more readable code I will
have to review, on average.
If you don't know what he was thinking, you've got problems,
yes. And operator overloading is abused... including in the
standard library (e.g. STL iterators). But it's one of those
things that, when you need it, you need it: used correctly, the
code is clearer than it could possibly be without it.
But don't you agree you do not really *need* it -- it is just nice to
have.. taken alone.. but in conjunction with all other feature else it
becomes much less nice.. maybe even harmful? So many other languages
live without -- and thriving.
:)

The preprocessor is a mis-feature, best avoided. With a few
small exceptions: in Java, how do you efficiently and
automatically insert the filename and line number in log
records?
By creating and logging exceptions (instead of throwing them) -- you
will have class name instead of a file name but they are mostly
equivalent; and now you have an API to access stack frames one by one --
something I would not mind to see in C++. Extra APIs have one "free"
advantage to the language features -- they usually do not add potential
ambiguities (when class names do not clash and methods are not overloaded).

But for the most part, it's a powerful tool for
obfuscation, and not much more. (You need it in C for manifest
constants, but not in C++.)
#include is part of pre-processing; and in its current form it requires
#defines and #ifdefs, too, if only to make obeying ODR practical. How
much can you write in C++ without #include? How would you use templates
and all that fashionable meta-programming without #includes when
exported definitions are still impractical if existent at all?

My think the pre-processor is another useful feature even though it
could be made more difficult to abuse, but as it is so old there is no
real point; I only mention it to point to another source of ambiguities
in the code; combined with other, purely C++-ish, sources, it maybe
quite dangerous..

-Pavel
 
J

James Kanze

James said:
[...]
That's why you need a development process, and to pay
attention to software engineering issues. Good code review
can do wonders in this regard. Greater expressivity does
allow more ways of screwing things up, but it also allows
more effective solutions. The trick is to manage things so
you get the benefits without the costs.
Yes, I have heard above this free cheese before -- but have
never eaten one :).
In other words, you've never actually worked in a place
which had a good software development process in place.
I guess, I am a poor guy -- not only in software development
but never in real life did I see benefits without the costs.

You were the person who talked about 0 cost. There's no way to
develop software for free. But a good process reduces cost, and
results in more maintainable code.
BTW do you really believe any development process, especially
a successful one, can be build around "things" rather than
around people and their motivations?

Obviously not. Where did I say that? Code review is a process
which involves people.
I mean -- "things" will tolerate any management but people may
become very uncooperative..
I am not trying to change a subject: basically, looking for
technical solutions for a non-technical problems is a common
blunder of technical people (I am including myself here).

But that's exactly what I said: you need a software process (to
manage people), regardless of the language, and the process is
more important than the language. Only once you've got that can
you begin to discuss the various advantages and disadvantages of
the different languages.

[...]
A programming language that is extra "expressive" and
"concise" has difficulties attracting practitioners. Who other
than a scientist really would want to line up all definitions
and then apply a zillion of rules of overloading in a specific
order in their sane minds to determine the "Best Viable
Function" (see 13.3.3. of the Standard)

A lot of computer scientists:). A lot of them tend to think of
it as a challenge, or fun.

Of course, from a software engineering point of view, it's
wasted effort. You don't bother with the exact details of the
rules of overload resolution when you're developing code,
because you don't overload unless it doesn't matter which
function will be called. (To a certain degree---obviously, if I
am working in double, it would matter if sin(float) were called
instead of sin(double). But we more or less count on the final
results of overload resolution being somewhat sensible, at
least.)

More generally, C++ is overly complicated, and offers a lot of
possibilities for obfuscation, if that's what you want. But you
don't have to use them; the KISS principle applies when writing
C++ as well, and you shouldn't use a feature of the language
except when it makes the code simpler and more easily
understood. (And code reviews are a very effective technique
for ensuring this.)

Thus, for example, overloading the binary + operator (and the
other arithmetic operators) for BigDecimal definitly makes the
code more readable.
 
J

James Kanze

* James Kanze:
* James Kanze:
[...]
Besides, even functions with telling names can (and often
do) return value in different ways. I will take your
example:
compare:
int increment(int i) { return i + 1; } // #1
and
int increment(int &i) { return ++i; } // #2
Increment is a verb. Thus, the first function is very purely
named, since it doesn't increment anything.
purely?
? What was I thinking of? (Clearly, maybe?)
"poorly", <url:http://www.thefreedictionary.com/poorly>.
As I recall, in Pascal those Peano arithmetic functions were
called Succ and Pred.
I probably meant "clearly", but "poorly" is better. In C++,
it's name is ++.
Oh, then you're not thinking of the first increment function,
which isn't ++. But you wrote "the first function". So now
I'm confused.

The first function was "increment"---I understood the word
compare to mean that there were two versions of only one
function (the first version, obviously, erroneous, since no one
would name a function increment if it didn't increment---except
maybe the authors of the STL, see std::remove).

[...]
The programmer can define these operations for each enum type.

And get it wrong:). (I often define operator| and operator& on
enum types. And even today, typically end up with infinite
recursion first go at it.)

I might add that the ++ and -- operators are trickier than they
look. Since I had code to parse enum declarations already (in
order to generate name to value mappings), I added the
possibility of generating the logical operators (but only if all
of the enum constants had assigned values) and incrementation
and decrementation (but only if none of the enum constants had
assigned values). Getting the incrementation and decrementation
right was a lot more difficult than I would have thought (but
probably because I was looking for something generic, which
could also be used to iterate over all of the values of the
enum).
succ and pred really only useful for enum types with defined
++ and -- operations.

As written above, yes. I think I was reading more into it than
you meant.
Explicit initializers or not is irrelevant, as far as I can
see. Well, mostly :), anyway, but there is the in-practice
that one is less likely to define ++ and/or -- operators for
an enum type with where explicit initializers are used to
provide non-successive values in general.
No, when I mentioned a traits class I was thinking of
different implementations of succ and pred depending on
whether the type T is immutable (pure value type) or not.
The above implementation should work well for an enum type T
with defined ++ and --.
At least, that was my intention. ;-)

Yes. I was thinking of it as a replacement for ++, for some
reasons. I think I understand what you're getting at now.
 
I

Ian Collins

James said:
More generally, C++ is overly complicated, and offers a lot of
possibilities for obfuscation, if that's what you want. But you
don't have to use them; the KISS principle applies when writing
C++ as well, and you shouldn't use a feature of the language
except when it makes the code simpler and more easily
understood. (And code reviews are a very effective technique
for ensuring this.)
Pair programming and collective code ownership are even more effective
in preventing arcane or overly complex code. It's unlikely for a pair
to add such code and if by chance they do, the next pair to edit the
code will probably remove it!
 
J

James Kanze

James Kanze wrote:
Pair programming and collective code ownership are even more
effective in preventing arcane or overly complex code. It's
unlikely for a pair to add such code

It's unlikely for an individual programmer to add such code,
since he knows it won't pass code review. And code review is
far more cost effective than pair programming (in most cases, at
least).
and if by chance they do, the next pair to edit the code will
probably remove it!

With effective code review, there's almost no chance of the code
ever getting checked in with such a problem.

Of course, if we're talking about a public function, used by
many people on the team, it will be reviewed as part of design
review. And there's no risk of the developer changing it, since
he can't even check out the header files. (But such an
organization is only justified on fairly large projects.)
 
I

Ian Collins

James said:
It's unlikely for an individual programmer to add such code,
since he knows it won't pass code review. And code review is
far more cost effective than pair programming (in most cases, at
least).
Possibly maybe in isolation, but pair programming coupled with
collective code ownership is at least as effective as code review
because it equates to continuous code review. It gives you the quality
of code review without interrupting programmer's work flow. Having
worked for many years with both styles, I don't think I could ever go
back to formal code reviews.
 
G

glen stark

They were not even stopped by the fact a Java instance would take
several times more memory -- apparently most of them had more memory
than us... again, this is a pure observation and you can draw your
conclusions. Just remember a client cannot be stupider than a provider:
otherwise they would not have money to pay for the provider's wares and
still make profit...

I found your discussion interesting, but what the hell are you talking
about here? You seriously think there is a high correlation between
intelligence and wealth? I would think that just a wee big of real world
experience would destroy that misconception. But if you still hold on to
it, I'd recommend "Fooled by randomness", which has some words to say on
the subject.
 
J

James Kanze

Possibly maybe in isolation, but pair programming coupled with
collective code ownership is at least as effective as code
review because it equates to continuous code review.

By "collective ownership", I presume you mean rotating the teams
working on each part regularly (weekly, or whatever), so that
each bit of code gets someone new working on it on a regular
basis. I'll admit that I was unaware of this possibility until
a few months ago, when someone posted it here; it does address
one major problem with pair programming: that both people
working on the code (and those reviewing each other's work) are
"insiders", and that it's important for someone from "outside",
who wasn't involved with the code, to review it. I'm still not
convinced, however:

First of all, of course, pair programming more expensive. If it
takes you one week (5 days) to write the code, it shouldn't take
a reviewer more than 2 hours to review it. Two reviewers, and
the total cost is 5.5 man-days. Using pair programming, I doubt
you'd gain more than a single day in writing it; maybe not that.
That's 8 (2*4) man-days (2*4). In order to be cost effective,
pair programming must gain considerably more than code review
downstream.

And is the original programmer present in the discussion when
the new team takes over? One of the most important aspects of
code review is that after a while, programmers end up writing
for the review, and not just for the compiler (and their
collegue who is as involved in the code as they are). They
learn from the feedback they get from the review, and end up
writing code that is clean, correct and maintainable from the
start. Typically, introducing code review is fairly expensive,
because most programmers aren't aware of just how much "obvious"
things aren't, if you're not immersed in the code. So the
initial review fail the code, and it has to be rewritten.
Fairly quickly, however, the programmers adapt to writing for
review, start looking at their code differently, and most
importantly, writing it differently. (Obviously, it takes some
management skill to implement this in a way that doesn't offend
anyone. Pointing out that the code is not of sufficient quality
to be acceptable is hardly flattering, and you have to somehow
convey the idea that it is normal when the programmer first
encounters review.)

I find, too, that psychologically, the pride factor plays an
important role. It's *my* code that I present for review, and I
want something that I'm proud of, and that others will estime.
The "collective ownership" aspect shouldn't come into play until
a higher level---once I've delivered. In the end, it's much
like a team sport; you're proud of your teams accomplishments,
and you want to do your best for the team. But you're also
proud of your own contributions to that team (including your
contributions as a reviewer); you're proud that anyone can take
your code and understand it and modify it without problems.
It gives you the quality of code review without interrupting
programmer's work flow.

But you sometimes want to interrupt the work flow. You want the
programmer to think about something else for awhile, before
coming back to what he was doing. (For the most part, this
happens naturally---at the end of the work day, I go home. And
the correct solution to the problem I was working on often pops
up automatically, as soon as I'm thinking about something else.)
Having worked for many years with both styles, I don't think I
could ever go back to formal code reviews.

Well, I've done a little pair programming (not much), and I
didn't care for it very much. Where as I just love reviews;
there's something particularly satisfying to have someone who's
only looked at my code for an hour or so come out and say they
liked it, because it was so easy to understand what was going
on, and to find the pertinent parts. And I learn from them,
even from relatively inexperienced programmers. (Again,
psychology plays a role: the times I've done pair programming,
I've always had considerably more experience than my partner,
to the point where the partner seems to consider me more of a
mentor than a peer, and was, perhaps, afraid to make any real
open comments. Where as in the code reviews, it seems clearer
that comments are wanted, since that's the only reason the
reviewer is there. Officially, at least: it's good policy to
mix the experience levels in code review, too, so that the
lesser experienced programmers learn from the more experienced
ones.)
 
J

James Kanze

On Aug 12, 8:06 am, Pavel <dot_com_yahoo@paultolk_reverse.yourself>
wrote:

Google won't let me post a detailed answer to all your points;
it's too long. So I'll just summarize a few of the more
pertinent ones...

First, I won't bother going into which language is more popular,
or growing, or shrinking, or whatever. Neither of us have the
gift of prophecy, so in the end, the only real factual statement
we can make is "we'll see". I just don't see C++ dying out any
time soon, especially since Microsoft has embraced it (at least
in name---perhaps in the future, will all be programming in
C++/CLI:)).

Second, with regards to the function "increment":

-- Even in the case of Percentage, the way C++ spells it is
Percentage::eek:perator++() :) (but that's really neither here
nor there---there are definitely functions which modify
state, for which there is no pre-defined operator).

-- Functions which modify state are normally members. You
don't write increment( object ), but rather
object.increment(). At least in principle: there are times
when, for various reasons, it's not possible, but it does
mean that the number of times the question comes up is
greatly reduced.

-- A function named "increment" increments, i.e. it modifies
state. Otherwise, it would have a different name, e.g.
"incrementedValue", or borrowing from Pascal "successor"
(which is better depends on the semantics of the object).
If for some reason it is not a member, it takes a non-const
reference as parameter. Period. You don't have to look
further.

-- A function which modifies state is not used in more
complicated expressions: if it returns a value, it is the
old value, to allow client code to restore it. In the case
of increment, this is silly, of course, because if you know
the new value, you know the old. But in the case of things
like std::ios_base::setf(), etc., it's very useful. In the
case of member functions of objects with identity, it's
sometimes useful to return *this, to allow chaining, but
that's about the only case I can conceive of where you'd
return the "new" value.

For the rest, expressivity is there for you to use, when
appropriate. It can obviously be abused, but that's not the
point. It is the responsibility of the development process to
ensure that it is not. And you need the development process,
regardless; abuse of expressivity isn't the only way you can
foul up a project, far from it. Expressivity is a positive
feature, always. (That doesn't mean that C++ doesn't have other
problems. But expressivity, or the lack of it, certainly isn't
one.) The most obvious proof (except that it probably doesn't
prove anything about software engineering, but more likely
something about social psychology) is that all languages evolve
in the direction of increasing expressivity: Java has corrected
some of its problems, for example, by adding to the language
(e.g. templates), and will doubtlessly continue to do so if it
is to survive (and even if it just survives---today's Fortran is
a far more expressive language than the one I learned in the
1970's).

If you want to discuss specific problems in C++, fine. There's
certainly no lack of subject matter. But too much expressivity
is NOT a defect, it's a definite advantage. And for the most
part, the points you've raised are points where C++ has a
definite advantage. (You can't write your increment() function
at all in Java, for a built in type. So if even discussing the
function has any relevance, Java is at a disadvantage.)

(One last comment, with respect to using forward declarations in
C. Been there, done that. While they're obviously the way to
go for entity objects, you can't implement true value semantics
using forward declarations. Which is a major drawback if you
want or need value semantics.)
 
I

Ian Collins

James said:
By "collective ownership", I presume you mean rotating the teams
working on each part regularly (weekly, or whatever), so that
each bit of code gets someone new working on it on a regular
basis.

Pairs should rotate often. How often depends on the team. Some change
every 90 minutes or so, we found half a day worked for us.
I'll admit that I was unaware of this possibility until
a few months ago, when someone posted it here; it does address
one major problem with pair programming: that both people
working on the code (and those reviewing each other's work) are
"insiders", and that it's important for someone from "outside",
who wasn't involved with the code, to review it. I'm still not
convinced, however:

First of all, of course, pair programming more expensive.

That's simply not true. My (and other teams) experience is that a pair
is more productive then two individuals.
And is the original programmer present in the discussion when
the new team takes over?

One programmer will continue working on the same story when the pairs
rotate, so there is always someone who knows the code working on it.
One of the most important aspects of
code review is that after a while, programmers end up writing
for the review, and not just for the compiler (and their
collegue who is as involved in the code as they are). They
learn from the feedback they get from the review, and end up
writing code that is clean, correct and maintainable from the
start.

When your code is continuously reviewed the same is true.
I find, too, that psychologically, the pride factor plays an
important role. It's *my* code that I present for review, and I
want something that I'm proud of, and that others will estime.

In XP teams, the team owns the pride :)
The "collective ownership" aspect shouldn't come into play until
a higher level---once I've delivered. In the end, it's much
like a team sport; you're proud of your teams accomplishments,
and you want to do your best for the team. But you're also
proud of your own contributions to that team (including your
contributions as a reviewer); you're proud that anyone can take
your code and understand it and modify it without problems.
To stretch the analogy further, teams function better with player who
know when to pass the ball rather than run with it.
But you sometimes want to interrupt the work flow. You want the
programmer to think about something else for awhile, before
coming back to what he was doing. (For the most part, this
happens naturally---at the end of the work day, I go home. And
the correct solution to the problem I was working on often pops
up automatically, as soon as I'm thinking about something else.)
That's true, but one of the strengths of pair programming is two minds
working on the same problem are less likely to block, or waste time
going up a blind alley.
Well, I've done a little pair programming (not much), and I
didn't care for it very much. Where as I just love reviews;
there's something particularly satisfying to have someone who's
only looked at my code for an hour or so come out and say they
liked it, because it was so easy to understand what was going
on, and to find the pertinent parts. And I learn from them,
even from relatively inexperienced programmers.

But think how much more you would learn if you had been writing the code
with the reviewer. Pairing gives you all those benefits you get from
reviews, all the time rather than in short bursts.
(Again,
psychology plays a role: the times I've done pair programming,
I've always had considerably more experience than my partner,
to the point where the partner seems to consider me more of a
mentor than a peer, and was, perhaps, afraid to make any real
open comments. Where as in the code reviews, it seems clearer
that comments are wanted, since that's the only reason the
reviewer is there.

I really enjoy pairing with inexperienced programmers. That's probably
because I enjoy mentoring. It's amazing how quickly they learn and
improve. I can't conceive of a better way to accelerate the learning
process than pairing with a more experienced peer. The same also
applies to new team members, they can contribute from day one without
having to read through mountain of documentation.
Officially, at least: it's good policy to
mix the experience levels in code review, too, so that the
lesser experienced programmers learn from the more experienced
ones.)
Again, think how much faster they learn working in pairs with the more
experienced ones.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,818
Latest member
Brigette36

Latest Threads

Top