James said:
Pavel said:
[...]
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++.)