C4Swimmers Newsletter  

(Interview) C++ QUESTIONS ANSWERS From Bjarne Stroustrup

(Interview) C++ QUESTIONS ANSWERS From Bjarne Stroustrup

1) Has OOP run out of steam?

After 20-some years, it's obvious that object-oriented programming is not a panacea. What are your thoughts on the future of the OO paradigm? What other paradigms do you see challenging it?

Bjarne:

Well. It was obvious to me 20-some years ago that OOP wasn't a panacea. That's the reason C++ supports several design and programming styles.

If you like long words, you can say C++ is a "multi-paradigm language," but simply saying "C++ is an OOPL" is inaccurate. I wrote a paper about that "Why C++ isn't just an Object-Oriented Programming Language. I presented that paper at OOPSLA - and survived.

In the first edition of "The C++ Programming Language," I didn't use the phrase "object-oriented programming" because I didn't want to feed the hype. One of the problems with OOP is exactly that unscrupulous people have hyped it as a panacea. Overselling something inevitably leads to disappointments.

That said, OOD/OOP is my favorite way of approaching design and programming. It just isn't the right style for every program and for every detail of a program. Some good abstractions are best represented outside class hierarchies. Trying to force everything into a hierarchy - especially into a single-rooted hierarchy - can give truly contorted programs.

I use a lot of simple data abstraction (classes without inheritance) and generic programming (templates and algorithms parameterized on types). However, I don't see these as "paradigms challenging OOP." Rather, they are complementary techniques. The key is always to find designs that fit the problems and use the language constructs that best represent the designs in the code.

Combinations of style can lead to very elegant code. For example,

void draw_all(list& lst)
{
for_each(lst.begin(),lst.end(),mem_fun(&Shape::draw));
}

Here, list and for_each() are examples of the C++ standard library's generic facilities. What they are used for is to invoke a virtual function of a base in a traditional class hierarchy.

You can even write a version of draw_all() that works for every standard container type with Shape* elements:

template
void draw_all(Container& c)
{
for_each(c.begin(),c.end(),mem_fun(&Shape::draw));
}

I chose this example to wake up people who still live in the dark ages and think that C++ is C with a few uninteresting additions.

Naturally, the code generated for draw_all() is as efficient as an list traversal code and the polomorphism used to call Shape::draw() boils down to a call of a function through an array of pointers to functions. The mechanism has been used to invoke device drivers.

New "paradigms" are touted every year, but most are not fundamental and few are genuinely new. Probably the most interesting are those based on "components" such as COM and CORBA. I'm undecided whether these constitutes a "new paradigm" or simply a new set of systems-level building blocks. Would we talk of Unix processes as a new paradigm? Anyway, my main concern with such components is that they are still less than perfectly integrated with programming languages, such as C++, and with the various "traditional" programming paradigms, such as OOP and generic programming.

2) C++ for systems programming

C has long been the UNIX-world's systems programming language, and still remains that way. I know you don't like to compare languages, so I'll just ask if you feel that there are any core reasons why C++ either does not make a good systems programming language or failed to capture the interest of the systems C programmers.

Bjarne:

It is hard to teach old dogs new tricks.

Unix was first written 25+ years ago (I first tried in in 1973). All of its interfaces are defined in terms of C function calls, arrays, and structs. By the time C++ became available, there were 10+ years of tradition for almost exclusively using C.

There is no good reason for Unix programmers to avoid C++ and several good reasons to use it. However, there are no end of reasons/excuses offered. Let me list a few:

  • C++ is slow

  • C++ generates bloated code

  • There are no good C++ compilers

  • C++ is complicated

  • C++ doesn't offer much to systems programmers

  • "Advanced" C++ features are unsuitable for systems work

  • C++ code isn't portable

  • C++ doesn't have an ABI.

These are all either flat wrong or largely irrelevant. Some of these reasons make some sense on other systems (say on a minute embedded system without proper C++ tools), but not on Unix/Linux. Let me briefly try to explain why. Naturally, a complete discussion would take hundreds of pages because it is not possible to prove a negative. That is, it is not possible to prove that there isn't some problem that could be unsolvable for somebody.

  • C++ generates as good code as C for equivalent programs. Just try it.

  • C++ was designed to do that and current compilers deliver on that promise.

Naturally, you can write poor programs in any language. C++ is a powerful tool and in the wrong hands it can generate code that is *obviously* contorted and bloated. That may be preferable to the traditional spaghetti that poor programmers produce in C. Note that someone who is a good C programmer isn't automatically a good C++ programmer. Many problems have been caused by good C programmers assuming that they could adopt a semi-random collection of C++ language features and then magically become a good C++ programmer in a week.

A C programmer can benefit from C++ in a week, but only by sticking to a subset of C++'s facilities and by using libraries.

C++ supports powerful techniques that are at best weakly supported by C and learning these techniques takes time. C programmers might do well remembering how long it took them to become "master level" C programmers. I see no reason why it would take less time to become a "master level" C++ programmer.

The current generation of C++ compilers is far superior in standards conformance to compilers from a couple of years ago. The optimizers are shared with C. This can be a problem because it precludes some useful optimizations that cannot be done for C, but at least sharing of major compiler parts should convince sceptics that equivalent code it produced.

I'll deal with the roots of language complexity in my answer to question 9. Here, I'll just point out that many of the C++ facilities directly help people who needs to write efficient, reliable, and maintainable code. If you don't use these facilities, you typically end up simulating them with lower-level language constructs.

Even the "new"/"advanced" features of C++, such as templates, exceptions, and run-time type information (RTTI) are designed to meet the 0-overhead rule. If you need such features, it is more efficient (in run-time and memory space) to use them with a modern compiler than to fake their functionality in C. I do not know of a C++ language feature that has not been found useful and affordable by someone in some systems or embedded application.

Naturally, if you don't need a feature (often, RTTI is not needed) or if is unsuitable in a particular context (I can think of programs where exceptions would be unsuitable), you just don't use that feature. The 0-overhead rule is there to allow such decisions. This is not so different from not using a C feature that is unsuitable for a given application (in some embedded systems, malloc() is banned).

C++ is as portable as C. In both cases you need to encapsulate the system dependences to ease portability. Large classes of programs are portable across Unix platforms, and some programs can be made portable across other platforms also. The techniques are well known and when used well, C++ even has an edge when it comes to formalizing the notion of a system to allow trivial portability. For example, see the C++ library that defines the ACE platform (link on my C++ page).

The technical hardest problem is probably the lack of a C++ binary interface (ABI). There is no C ABI either, but on most (all?) Unix platforms there is a dominant compiler and other compilers have had to conform to its calling conventions and structure layout rules - or become unused. In C++ there are more things that can vary - such as the layout of the virtual function table - and no vendor has created a C++ ABI by fiat by eliminating all competitors that did not conform. In the same way as it used to be impossible to link code from two different PC C compilers together, it is generally impossible to link the code from two different Unix C++ compilers together (unless there are compatibility switches).

The current solution is usually a combination of using a single compiler and of providing C-level interfaces. This is not ideal - see also my answer to question 10.

That said, I think the main problem is educational. Many simply have seriously inaccurate ideas of what C++ is and what can be done with it. Often "inaccurate ideas" add up to a strong disincentive to learn. C++ is now very different from Release 1 from 1985. The ISO standard for C++ was ratified in 1998 and current compilers approximate it well enough for me to move programs that stress the newer facilities from compiler to compiler for performance testing on a variety of platforms. The standard library makes a difference here.

[Read More..]