Bjarne Stroustrup talks with Bill Venners about the perils of staying too low level and venturing too object-oriented in C++ programming style. Stroustrup is the designer and original implementer of C++.
Bjarne Stroustrup talks with Bill Venners about the perils of staying too low level and venturing too object-oriented in C++ programming style. Stroustrup is the designer and original implementer of C++.
C++ really is a cool language. People compare it to Java, but Java really isn’t even in the same league. C++ is complex, difficult, and in the hands of less skilled programmers, can be downright dangerous. However, someone who knows how to use it can really get a lot out of it.
My personal problem with C++ is that its now aspiring to a higher level of abstraction, and the limits of its design are starting to show. A few examples:
1) Some of the creative uses of templates are really desperate pleas for proper macros. The C++ metafunction proposal goes farther, but really can’t come close to the power of a proper macro system. C++’s syntax is simply too complex to support macros cleanly. CL has the most powerful macros available, but its syntax is the height of simplicity. Dylan offers a slightly less powerful pattern-matching macro system, but even though it has an infix syntax, its still highly regular.
2) C++, as a multiparadigm language, is attempting to support the functional paradigm. The STL and its dependence on function objects basically begs for proper lambdas. The Boost Phoenix library provides proper both lambdas and closures but using them is cumbersome. God help you if you need to debug a compile error while using one of those libraries, because all the template expansion will totally mask the real error.
3) C++ is too low-level to take advantage of advanced compiler technology. Modern compilers for high-level languages (real high-level languages, not Java) have very powerful algorithms that can get rid of the performance loss associated with high-level features. For example, many languages conceptually heap-allocate all data (so no primitive/class division like Java). However, compilers can detect when a reference to an object never leaves a function, and can stack allocate it in that case. They can detect when a closure doesn’t leave a function, and stack-allocate it. They can do type inference and copy down, so you can code as if all functions are dynamically typed and generic (like Python), but the compiler will detect the types of variables from available information (assignments, type declarations, etc), and generate specialized copies of each function for different types. C++’s memory model is too loose to take advantage of most of these features, so you still have to worry about stuff like passing by value vs passing by reference (and tons of other things) even though the compiler could figure it out itself anyway.
Overall, even though C++ is a lot better than some of what is out there, and I still think you can’t beat it for low-level code, there are tons of other languages out there that are much nicer than C++/Java/C# etc.
Some links for those who are interested:
http://www.gwydiondylan.org
http://www.cons.org/cmucl
http://www.bigloo.org
http://www.smalltalk.org
http://www.haskell.org
http://www.ocaml.org
Yes, I’m currently stuck programming in Java.
Can you tell me if there are any equivalents to these C/C++ features in Java:
Pass by reference- all I want is a swap function! I think it can be done by passing an array, but it is not an elegant solution.
Templates- I’m designing generic algorithms, and would like to not have to reimplement every one of my algorithms for each data type. Do I really need to maintain a quicksort for floats, ints, and doubles? REALLY?!
A decent editor- I’m using Eclipse, but the debug function seems to mysteriously stop working after a while.. I set breakpoints, the program just runs instead of debugging. Sometimes a complete re-install of Eclipse fixes it (which is why I have two seperate installs, JIC), but not always.
The simple fact that c++ is higher level than c and lower level than say, python is precisely what makes it so atractive, it has the best of both worlds. If you like python better then use that, personally i can’t stant python or any other language that makes whitespace part of the language itself.
I also like STL the way it is, what need is there to add lisp stuff.
And you forgot about eiffel, that simply unforgivable 😉
Bruce Hoult, a Gwydion Dylan maintainer, had a funny quote on comp.lang.dylan:
“Eiffel is from the bondage-and-discipline school of software
engineering, similar to (but better than) Pascal or Modula-2.”
Of course, I can’t say anything bad about Eiffel, I haven’t used it, but “strict” languages just aren’t my bag
I fully agree that C++ has its charm because its nice in the middle. In fact, its nice at the low-level too, even if you use seemingly high-level features like templates. It can do these things because all of its semantics map very cleanly to an essentially overhead-free memory model. However, it is this same property that makes it difficult to cleanly add-on high-level features.
PS> For the record, Dylan has replaced C++ as my favorite language. That bumps C++ down to second, and Python down to third.
Especially versus C++ ?
Anonytroll: Yes, yes, and yes.
Not sure what editor you might like – don’t even know what platform you’re on – but the choices of C/C++ IDEs are as broad as they come.
In C++, non-array arguments can be passed by reference by inserting an ampersand after the type name in the formal parameter list. Templates have been supported by C++ for over 10 years. Neither of these features are supported by ANSI C.
The following generic C++ function illustrates both:
in utils.h:
template<class T>
void swap( T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
example use:
#include “utils.h”
void myfunc()
{
float f = 10.0;
float g = 20.0;
swap( f, g ); // compiler groks swap<float>( f, g );
/* f now 20., g now 10. */
int i = 5, j = 6;
swap( i, j ); // compiler groks swap<int>( i, j );
/* etc */
}
Thanks for the detailed examples, but I’m looking for these features in Java, not in C++.
I need to get to bed :-). I heard Java 1.5 (next year) is supposed to have generics, so in other words Java doesn’t have it now.
ObjectiveC is a good object oriented language in the spirit of Smalltalk. For OO purposes, its better than C++ in ways. OOP in ObjectiveC is message-based. For example, objects can forward messages they don’t understand to other objects. ObjectiveC is also dynamically typed, while C++ is statically typed. Many people consider dynamic typing to be an advantage, because it facillitates rapid development and refactoring. The downside, however, is that you can have runtime type errors in dynamically typed languages. For the kinds of things Smalltak was designed for (GUIs!) Objective C is quite good.
Outside of OOP, its not really competitive with C++. C++ is a multiparadigm language, and its template mechanism, and the types of libraries (like Boost) that it enables really put it ahead any of the other C-like languages in terms of raw power.
Hi,
There’s no simple way to pass arguments by reference as you know from C++ in Java. A method that uses class holder that wraps the value is used. You do it this way:
class IntHolder {
public int value;
IntHolder(int value) { this.value=value};
}
public class Math {
public static void swap(IntHolder a, IntHolder b) {
int tmp = a.value;
a.value = b.value;
b.value = tmp;
}
public static void Main(String[] args) {
IntHolder val1 = new IntHolder(10);
IntHolder val2 = new IntHolder(20);
System.out.println(“val1=”+val1.value+” val2=”+val2.value);
swap(val1, val2);
System.out.println(“val1=”+val1.value+” val2=”+val2.value);
}
}
It’s just the illustration and I hope this helps…
The simmilar aproach is used in Java binding of CORBA where Holder classes are generated.
igm
Things that I miss in C++:
* A ‘with’ keyword; the VB-style of .field to denote with-scope would be nicest
* Method callbacks. In Object Pascal, for example, you can do ‘var MyCallback: procedure(field1: integer…) of object;’. That is how most events get assigned to GUI controls etc, but it’s use is also great in engine code. In C++ you have to do message maps or listeners or something messy.
* Properties. Again ala Object Pascal.
Hey, do I begin to sound like an Object Pascal fanboy? I am, just forced to use C++ 😉
Seems these comments are suddenly littered with “What’s your favorite language?” comments, but I think there was a lot of cool stuff discussed in this article.
I very much agree with everything Bjarne Stroustrup has to say about overuse of object orientation. When I was a college student, our Java textbooks taught that the principles of object orientation be exercised down to the smallest detail. This included, for example, implementing linked list nodes with get/set methods for everything, as opposed to simply making the member variables public. This is ludicrous. A node in a linked list is merely a placeholder for the linked list invariant itself, and as there is no way to “taint” the linked list’s invariant by exposing the member variables of the linked list node as the nodes themselves are never exposed.
Objects should be used as state management mechanisms for managing complex states. The constructor should establish an invariant and all get/set methods should manipulate the invariant while also performing proper input validation to ensure that the invariant cannot be “tainted”.
Using object orientation where such state management isn’t required needlessly increases program complexity and wastes programmer time. This is what Bjarne argues as one of the two mistakes most C++ programmers make… making their code “too” high level and thus overabstracted needlessly, thereby making it more complex and harder to maintain.
Method callbacks. In Object Pascal, for example, you can do ‘var MyCallback: procedure(field1: integer…) of object;’. That is how most events get assigned to GUI controls etc, but it’s use is also great in engine code. In C++ you have to do message maps or listeners or something messy.
Well, looking at the article Bjarne would suggest you look at libsigc++; signals and slots may be the answer to your woes: http://libsigc.sourceforge.net/
C++ is already an overcomplicated language… adding more syntax isn’t necessary when the same functionality can be had through existing libraries.
I think the suggested programming style could reduce
most of the problems typical for C++ projects.
However, I haven’t so far encountered this style.
Are there any known APIs or larger OS projects that
are implemented like that?
It was precisely that bit by Bjarne that touched my c++ callback nerve:
“If you want callbacks, don’t use just plain C functions. Get libsigc++, and you’ll have a proper library that deals with callbacks—callback classes, slots and signals, that kind of stuff. It’s available. It’s conceptually closer to what you’re thinking about anyway.”
My point being that Object Pascal has a far nicer way to do it, IMO. I’ll accept Bjarne’s assertion that it is “conceptually closer” to what I am looking for, but it ain’t close enough to feel ‘neat’.
But life goes on and I still choose c++ if that is the best tool for that job.
I am currently writing an app that evolves around a special custom control for formated text entry. The whole usability and therefore success of this project evolves around me being able to get a extremely nice feeling custom widget working. And for that, I choose Delphi. I just wonder if, once I have worked out the code, I’ll be reimplementing in wxWindows.. arggh.
Actually, in the C++ APIs I’m writing for SkyOS, some of the more frequently created and otherwise light-weight classes/structs are not derived from the base Object class (which on it’s own doesn’t do much more than support locking/unlocking for thread support, and overrides the new and delete operators for memory management reasons (not unlike Gtk::manage())
Since it is a message-passing framework (heavily influenced on BeOS’s), the message type has to be relatively light-weight and is essentially just a linked list, and I have another MessageHandler class which manipulates a message linked list. Messages, Rects, Points and such all have public members, because it makes no sense to Get and Set such things and create the overhead.
One just has to use their head a bit and realize how their objects will be used, and from there decide if then need to add the weight of derived classes or not. Small, light-weight things work just fine stand-alone.
If, say, I wanted to convert a number into a string, or vice-versa, it’s ridiculous to have an Int class, a Float class and so on, and have member function to convert between them all, and based off some abstract Number class, which has the virtual functions to implement. Using a class of static numbers is much more worth-while in terms of performance.
crap, I meant “static functions” 🙂
Here (in the chapter Java and C++) Stroustroup explains why he does not like Java:
http://www.research.att.com/~bs/ieee_interview.html
Andreas
“Yes, I’m currently stuck programming in Java. ”
I really think you really should ask this question on a java usergroup or on javaranch.com or something. You will probably get more feedback there. But to answer your question:
1. Classes are passed by reference in Java, and built-in data types are passed by value. In order to pass built-in data-types such as int by reference, you can wrap them around in their wrapper classes. Integer for int, Double for double, etc. C# does this a bit better by including the ref key, but it does essentially the same thing.
2. There is a version of generics coming out for Java 1.5. I am not sure if it’s going to fit your needs. All numeric datatypes have wrapper classes in Java though, and those wrapper classes have a common ancestor. I think it’s lang.Numeric or something. You can try to sub-class Numeric and go from their.
3. From what I gathered, you are doing this for school? In that case, you can go to Oracle.com and download a copy of Oracle JDeveloper. It’s free of charge for educational purposes and comes with a great deal of stuff so you don’t have to fiddle with it. Good luck.
Yes. But Stroustrups suggestion is somewhat more radical.
He says if a function cannot screw up an invariant, it shouldn’t be a member function.
And if you don’t have to maintain an invariant for a datastructure, it’s a struct, not a class.
AFAIK calling a non-virtual member function or calling a static function that works on the object should not make a difference in performance.
“1. Classes are passed by reference in Java, and built-in data types are passed by value. In order to pass built-in”
Actually, everything in Java is passed by value. In the case of object, when an “object” variable is declared, it’s actually a reference to the object. So, Java actually pass _object_reference_ by value.
void function(ClassA arg) {
arg.increamentMe(5);
ClassA f_obj = new ClassA(…);
arg = f_obj;
}
… main(…) {
ClassA myObj = new ClassA(…);
function(myObj);
//myObj != f_obj. However, myObj has
//been increased by 5.
}
@Bascule
When I was a college student, our Java textbooks taught that the principles of object orientation be exercised down to the smallest detail. This included, for example, implementing linked list nodes with get/set methods for everything, as opposed to simply making the member variables public. This is ludicrous.
Getting back away from the article… (sort of) It’s been a while since I studied them (like 4 yrs), but I believe that both Eiffel & Oberon programmers tend to follow this philosophy, made easier by those languages’ option to make data members “read only”.
For all I program, C++ always been the ultime language of choice. Just the right balance between low-level control and high-level comfort.
The only two things I would love to be included as standard tho, are Properties and Closure Pointers (2x32bits). Those are generally supported as language extension by some commercial compilers (Microsoft, Borland, etc), but I’m still waiting for those to be included in GCC.
If only they can be added to the official C++ standard …
“When I was a college student, our Java textbooks taught that the principles of object orientation be exercised down to the smallest detail. This included, for example, implementing linked list nodes with get/set methods for everything, as opposed to simply making the member variables public. This is ludicrous.”
That’s why I love Properties so much. They offer the very safe encapsulation of a “get/set”, but are used in the exact same easy way as a public variables.
“1. Classes are passed by reference in Java, and built-in data types are passed by value. In order to pass built-in”
Actually, everything in Java is passed by value. In the case of object, when an “object” variable is declared, it’s actually a reference to the object. So, Java actually pass _object_reference_ by value.
void function(ClassA arg) {
arg.increamentMe(5);
ClassA f_obj = new ClassA(…);
arg = f_obj;
}
… main(…) {
ClassA myObj = new ClassA(…);
function(myObj);
//myObj != f_obj. However, myObj has
//been increased by 5.
}
<BR>
<BR>
Your 5 is being wrapped in an instance of Int for you by the compiler. Then presented back as a primitive.
One of the best examples of how to write a utility library using multi=paradigms is the Standard C++ Library (STL). There is a good book called Generic Programming and the STL written by Austern.
Stroustrup provides good advice about keeping interfaces simple so that libraries can be built ontop of general classes without creating excess bagage and dependancies. I think that Stroustrup’s thinking is similar to Arthur J. Reil in his book called “Object-Oriented Design Heuristics”. The discussion involving the invariant is worth thinking about because it means that for each class there is some semantics that you are defending, I think that heuristics can help make sense out of that by providing guidelines. I noticed a couple years ago that these authors do not share views with Grady Booch. Anyway it’s interesting and there is a lot to learn…but discovering quality software guidelines probably won’t help you get an entry level job anywhere.