LWN.net Logo

Quote of the week

That's the problem with C++; it is far too easy to misuse. And with a project as big as the Linux Kernel, and with as many contributors as the Linux Kernel, at the end of the day, it's all about damage control. If we depend on peer review to understand whether or not a patch is busted, it is rather important that something as simple as
	a = b + c;
does what we think it does, and not something else because someone has overloaded the '+' operator. Or God help us, as I have mentioned earlier, the comma operator.

-- Ted Ts'o


(Log in to post comments)

Quote of the week

Posted Dec 9, 2004 4:04 UTC (Thu) by josh_stern (guest, #4868) [Link]

That's sort of like pointing out how awful it would be to program a
project as large and complicated as the Linux kernel with a language like
C or C++ that features a brain damaged pre-processor allowing arbitrary
textual replacement. At least stuff at global scope with the operator
keyword is a lot less common than #define. In either case, a patch would
not be accepted if it didn't follow sanity conventions (like using upper
case names for macros).



Quote of the week

Posted Dec 10, 2004 2:57 UTC (Fri) by mbp (subscriber, #2737) [Link]

The thing is that because C is a simpler language, it is easy to describe the necessary rules: no #ifdefs in code, avoid macros, strongly avoid macros don't generate "self-contained" results, use inlines rather than macros where possible, etc. These are easy to understand, easy to check for, and most experienced programmers will see they make sense.

For C++ in principle you can decide to avoid some features, but it is hard to agree on which ones. I have heard people propose any of the following: no templates, no MI, no RTTI, no non-virtual members, no typedefs, no operator overloading, etc. And yet if you stick to just slightly-improved C then people will complain...

Quote of the week

Posted Dec 10, 2004 4:37 UTC (Fri) by josh_stern (guest, #4868) [Link]

The quote above and my response both speak to the same programming
principle: don't change the global namespace and/or default builtin
operations in unexpected ways. That's a principle of good practice that
isn't practically reducible to a set of specific syntactic. My point was
that the worst abuses are equally possible in C itself, and it is actually
easier to check for abuse in common C++ idioms(i.e. #define is more easily
avoided and operator definitions at global scope are easy to notice and/or
check for).

I don't advocate changing kernel development practice, but the quotation,
taken out of context, was simply wrongheaded. A statement like this: "C++
would be bad for kernel development because fewer practitioners have
sufficient mastery of that language" isn't something I would care to
dispute.




Quote of the week

Posted Dec 10, 2004 6:58 UTC (Fri) by mbp (subscriber, #2737) [Link]

Ted's response has nothing to do with changing the global namespace or default builtin operations. That statement can cause trouble if any of a, b or c are of types defined to behave in a surprising way. And while it is possible to write surprising code in any language, it is common (almost unavoidable) in C++ to write code which can have surprising interactions. It is generally a reasonable assumption in C that there are no macros that will mess with a line; it is not a reasonable assumption in C++ that there are no conversion or copy constructors or operators.

C++ has more magic, therefore it is harder to look at code and see what it does without examining all of the definitions which may be in force.

Quote of the week

Posted Dec 10, 2004 18:39 UTC (Fri) by josh_stern (guest, #4868) [Link]

"Ted's response has nothing to do with changing the global namespace or default builtin operations. That statement can cause trouble if any of a, b or c are of types defined to behave in a surprising way."

Huh? I must be giving Ted more credit than you apparently do. I assumed he was worried about the possibility that someone had redefined global assignment or addition operators for POD (built-in) or standad types or known types which use a default assignment operator. That would be analogous to creating some weird and unexpected macro that made normal looking code into something utterly different. Such things should be ruled out by coding standards that are fully comparable to 'Minimize macro use and always use captial letters for macro names'. "Surprising" behavior by user-defined member functions is not something that would cause diagnostic problems for a competent developer, because a) we don't have much prior expectation on the behavior of a new user-defined type, and b) the member function declaration and definition has to be part of the class declaration and definition, so we know exactly where to look for it and who is supposed to be modifying it (so someone cannot come along and redefine it in an unrelated header file). It is part of basic developer competency to know which types are being operated on, and understanding that the argument type is actually part of the function name in C++ is not a huge mental leap.

"And while it is possible to write surprising code in any language, it is common (almost unavoidable) in C++ to write code which can have surprising interactions."

It's not common for competent C++ developers to write code that is seen as having surprising interactions by competent C++ developers.

"It is generally a reasonable assumption in C that there are no macros that will mess with a line; it is not a reasonable assumption in C++ that there are no conversion or copy constructors or operators."

Copy constructors and assignment operators are a basic part of C++. There is nothing surprising about them - every class has one copy constructor and one assignment operator, default or otherwise (though they can be made private members to eliminate their actual usage). Other member function operators, global operators, and type conversion member operators all have their place in some types of coding, and there are various coding standards describing their appropriate usage. Moreover, they could all be prohibited from a given type of project without seriously impacting common development idioms.

"C++ has more magic, therefore it is harder to look at code and see what it does without examining all of the definitions which may be in force."

ISO C++ is a more complicated, larger language than C99. One only needs to look at the standards documents for each to see how much larger C++ is. This means that C++ takes that much longer to learn well, and this is a serious criticism of it as a language (both in comparison to C and to Java).

But I disagree with the statement above in the sense that it is actually easier for a competent C++ developer to inspect and/or debug application code written by another competent C++ developer. There are a lot of reasons for that, of which I'll highlight a few: a) compactness/locality of class member declarations, definitions, and access control, b) encapsulation of resource acqusition and release mechanisms, c) namespace separation that is independent of header separation, d) less need/usefor macros, e) stricter typing, and f) more utility built into presumably debugged standard library code (I realize this is irrelevant for kernel programming).

Quote of the week

Posted Dec 10, 2004 8:37 UTC (Fri) by daniel (subscriber, #3181) [Link]

Hi Martin,

"The thing is that because C is a simpler language, it is easy to describe the necessary rules: no #ifdefs in code, avoid macros, strongly avoid macros don't generate "self-contained" results, use inlines rather than macros where possible, etc. These are easy to understand, easy to check for, and most experienced programmers will see they make sense."

If it was C++, we'd add the rule "no operator overloading", big deal. This argument, like many I've seen for the "against" case, baffles me. The only argument that really matters is: it's Linus's kernel and he doesn't want any C++ in it. But I do wonder how many of the "against" crowd have actually used C++ on a project of any significance?

John Carmack developed the Doom III engine in C++, his first engine written in C++. Where John goes, there is usually truth. I don't know what his reasons were, but my impression is, C++ is an altogether more regular and descriptive language than plain old C, and superior for large projects. One small example: with C++ templates we could have type checking in the list ops, instead of having every list be (struct list_head *).

But I don't mind sticking with C. I'm an oldtimer who learned to code before there was any such thing as C++. I'd just as soon things stay as they are, at least until I get time to improve my C++ skills. I suspect that C++ _is_ actually better for a large OS project, but that somebody will actually have to sit down and prove it by building a whole kernel in it, and that may take a while.

Quote of the week

Posted Dec 16, 2004 22:33 UTC (Thu) by renox (subscriber, #23785) [Link]

> John Carmack developed the Doom III engine in C++

How many developers worked on the engine of Doom III?
I'd bet that this is less than 5, so the situation is different..

Quote of the week

Posted Dec 9, 2004 4:11 UTC (Thu) by elanthis (guest, #6227) [Link]

"That's the problem with C++; it is far too easy to misuse."

Mean-while pretending that we didn't have this new tool called "sparse" to check the bazillion and one ways that C itself can be misused...

Really, C++ can offer some benefits, including a *cleaner* interface. If operator overloading is considered evil, is it *really* that hard to just add some scripts (or sparse enhancements) to raise hell about them?

In honesty, it doesn't matter - most of the truly interesting C++ stuff (i.e., the stuff beyond just syntactic sugar) really isn't a good idea to use in a kernel, and I'd rather the developers use what they're comfortable with.

C seems to have done just fine for Linux for the last decade+ after all. ;-)

Quote of the week

Posted Dec 9, 2004 8:25 UTC (Thu) by simlo (subscriber, #10866) [Link]

In honesty, it doesn't matter - most of the truly interesting C++ stuff (i.e., the stuff beyond just syntactic sugar) really isn't a good idea to use in a kernel, and I'd rather the developers use what they're comfortable with.

I don't know what you call interesting in C++, but I can easily point out 2 places where C++ would be really nice:

1) Instead of using pointers to a struct of function pointers use virtual functions in C++. What will actually happen runtime will be the same.

2) Ingo Molnar have replaces spin-locks with muteces in his real-time patch. Some are left as spin-locks, some are changed. But the interface is the same. So how does the kernel know which function to call when the lock has to be locked/unlocked? This is now done by explicitly making typecheck's with if's in the code. Ugly! In C++ you just overload the function. It will all be done compile time! In general it is much easier to fix interfaces in C++ without breaking compability with existing code - without making extra runtime checks!

C++ also has a lot stronger type-checking so you can do a lot more checking compile time.

I don't know about templates. In some way they are very elegant and I have done some really nice stuff with them. You can make the compiler do a hell of a lot of the work for you and make much more efficient code because a lot of the logic can be done compile-time. But they are really hell to debug from outsiders and doesn't really work the same in all compilers...

Quote of the week

Posted Dec 9, 2004 18:01 UTC (Thu) by iabervon (subscriber, #722) [Link]

For (1), I think kernel developers tend to consider pointers to structs of function pointers to be more clear than virtual functions, because it is easier to see exactly what has virtual functions and exactly what code fulfills each role in a particular case.

I haven't looked at Ingo's patch, but any problems which prohibited a compile-time solution in C (probably passing locks of unknown implementation through another function) would also prohibit it in C++, and C++ would then general virtual function calls, which would then be prohibitively expensive for such fast-path code. The reason for doing that sort of ugly thing is really that you have a special case with critical performance, and you have to open-code such things in any language.

C++ does have some advantages in type-checking, but C compilers warn about cases where these checks are either impossible or are violated, with the exception of some of the C++ checks which are actually detrimental. Sparse also does better type-checking than C++ supports, since it allows project-defined special conditions (like typed pointers to values in a different address space).

Actually, now I'm curious as to how hard it would be to get sparse to support "struct list_head * __of(struct dcookie_struct)" and type-check that like C++ templates or Java generic types.

Quote of the week

Posted Dec 9, 2004 21:29 UTC (Thu) by simlo (subscriber, #10866) [Link]

I haven't looked at Ingo's patch, but any problems which prohibited a compile-time solution in C (probably passing locks of unknown implementation through another function) would also prohibit it in C++, and C++ would then general virtual function calls, which would then be prohibitively expensive for such fast-path code. The reason for doing that sort of ugly thing is really that you have a special case with critical performance, and you have to open-code such things in any language.

Ofcourse you wouldn't do it by virtual functions in C++! You use virtual methods when you want to branch out run-time, not compile time. In C++ you simply define two classes with the same public methods. In this case class spinlock and class mutex with public methods lock() and unlock(). Then all code can compile with no further change no matter if you declare your lock by "mutex mylock;" or by "spinlock mylock;". The compiler sees a object with a lock() method and calls the right one (might be inlined). No branching at all. If you then want a flat function interface which matches the current one you can make a very simple templates to generate lock() and unlock() functions.

The way Ingo have done it in flat C is by doing making a macro containing "if(TYPE_EQUAL(lock), mutex) lock_mutex((mutex_t*)lock) else if(TYPE_EQUAL(lock, raw_spinlock_t)...." I know, with -O2 this wont actually generate a branching code but it is very, very ugly.

C++ is a huge benifit to people who know C++. It will _not_ make inefficient code unless you use it the wrong way ofcourse (see the above examble). On the contrary you can do more efficient code since you can do a lot of the stuff compile time. But to avoid complexity stay away trying smart templates combined with operator overloading. THAT is hell figure out when it can't compile. On the other hand when it can compile there are very few errors in the resulting program due to the strong type-checking.

Quote of the week

Posted Dec 13, 2004 7:10 UTC (Mon) by hppnq (guest, #14462) [Link]

Well, think of it as a work of art. You probably wouldn't have immediately recognized David when Michelangelo started hammering away. I'm quite confident that this will all be forgotten once Ingo has hammered a bit more.

As a side note, ugliness is not necessarily bad, because it will keep urging people to fix the associated problem.

Open-code?

Posted Dec 14, 2004 19:41 UTC (Tue) by Max.Hyre (subscriber, #1054) [Link]

What does ``open-code'' mean in this context? So far, I've not run across anything that would fit this use.

Of course, I tried Google:
"open code" -"open source" -"free software",
but was swamped with Free-Software-ish links anyway. :-)

Open-code?

Posted Dec 14, 2004 21:06 UTC (Tue) by iabervon (subscriber, #722) [Link]

Write out the full code that the convenient syntax would stand for, so that you can apply some optimizations based on information the compiler wouldn't have. So rather than having a C++ virtual method or using a function pointer, you'd actually write out tests and calls for all of the functions it could be, because the time to call a function pointer would be significant.

Quote of the week

Posted Dec 10, 2004 12:40 UTC (Fri) by kleptog (subscriber, #1183) [Link]

1) Instead of using pointers to a struct of function pointers use virtual functions in C++. What will actually happen runtime will be the same.

There is actually one thing pointers to functions can do that virtual functions can't, and that is be modifed at runtime. For example, you load a module that looks up the ops for UDP sockets and switches one for itself. In C++ the indirection is fixed by the type. Virtual functions would only work if you created the object of the right type in the first place, you can't affect existing objects.

Individual object can define their own callbacks setting a pointer. In C++ you acheive the same affect by deriving a new class for each possibility. The compiler needs to know at compile time all possible variations of that class.

Note, some other languages do allow run-type retyping including Perl and Python.

At least in C you can assume that the only operators that work on your own types are &, -> and .(dot). If none of those are involved you know you're only dealing with base or pointer types, whose rules are very simple.

Quote of the week

Posted Dec 11, 2004 12:51 UTC (Sat) by mmutz (guest, #5642) [Link]

The compiler needs to know at compile time all possible variations of that class.

If this is true, please explain to me how on earth the compiler manages to compile this code:

 
foo.h: 
class Foo { 
public: 
  virtual void f(); 
}; 
 
foo.cpp: 
#include "foo.h" 
void Foo::f() {} 
 
bar.h: 
#include "foo.h" 
class Bar : public Foo { 
public: 
  void f(); 
}; 
 
bar.cpp: 
#include "bar.h" 
void Bar::f() {} 
using something like
cc -o foo.o -c foo.cpp 
cc -o bar.o -c bar.cpp 
cc -o foobar foo.o bar.o 

Quote of the week

Posted Dec 11, 2004 17:23 UTC (Sat) by simlo (subscriber, #10866) [Link]

Make the examble better by making f pure virtual in the base class: Then you eliminate the problem of uninitialized function pointers compile time! C++ will simply not allow you to initialize an object of this class before a method have been specified.

By having structures of function pointers there is no check on the actual pointers point to anything sensible. The only thing safe to do is to check if they are non-NULL before using them! Waste of instructions...

Quote of the week

Posted Dec 10, 2004 5:16 UTC (Fri) by mbp (subscriber, #2737) [Link]

<em>If operator overloading is considered evil, is it *really* that hard to just add some scripts (or sparse enhancements) to raise hell about them?</em>

Yes.

Writing a sufficiently correct parser for C++ that understands the interactions between all the language features is much harder than writing one for C. Indeed I think you cannot do anything useful with just a parser alone (as for C), since that won't catch problems that come up during template expansion (I think.)

Quote of the week

Posted Dec 10, 2004 16:31 UTC (Fri) by viro (subscriber, #7872) [Link]

Have fun implementing a C++ frontend with proper type checking.
C has dark corners and preprocessor is definitely not my idea
of fun, but handling it correctly is far more straightforward than
doing C++.

Quote of the week

Posted Dec 16, 2004 22:33 UTC (Thu) by renox (subscriber, #23785) [Link]

>> "That's the problem with C++; it is far too easy to misuse."
>Mean-while pretending that we didn't have this new tool called "sparse" to check the bazillion and one ways that C itself can be misused...

Thanks elianthis, I remember when learning about sparse thinking that they're trying to reinvent Ada!

Apparently C's weak typing is an issue for big projects which is not surprising.. OTOH C++ isn't exactly an elegant language (I do consider C's simplicity elegant for small to middle project but not for large projects) and there is a lack of fashionable replacement language..

Quote of the week

Posted Dec 9, 2004 16:11 UTC (Thu) by alq666 (subscriber, #11220) [Link]

To get an idea about how "powerful" c++ is, check out Guru of the week's problems. Yes c++ has interesting features, but having to battle around the language is not something that is very appealing in the context of system software, which in the event of a failure will bring everything down.

Quote of the week

Posted Dec 10, 2004 8:16 UTC (Fri) by aleXXX (subscriber, #2742) [Link]

Among others e.g the kernel of the open source realtime operating system
eCos (http://ecos.sourceware.org) is written in C++.
No problems there (for deeply embedded devices (!) )
And I think there were OSs too , but I can't remember exactly right now
which ones.

Alex

OSes in C++

Posted Dec 10, 2004 12:12 UTC (Fri) by eru (subscriber, #2753) [Link]

And I think there were OSs too , but I can't remember exactly right now which ones.

ChorusOS is another one (was proprietary, now open-sourced as "Jaluna" under a modified MPL-like license). However, years ago having browsed its kernel code, I got the impression it uses C++ in very limited way as a "better C", using classes mainly to manage name spaces.

OSes in C++

Posted Dec 10, 2004 18:16 UTC (Fri) by daniel (subscriber, #3181) [Link]

"ChorusOS is another one (was proprietary, now open-sourced as "Jaluna" under a modified MPL-like license). However, years ago having browsed its kernel code, I got the impression it uses C++ in very limited way as a "better C", using classes mainly to manage name spaces."

Ahem. Doesn't that make a frightening amount of sense?

Regards,

Daniel

OSes in C++

Posted Dec 11, 2004 12:59 UTC (Sat) by mmutz (guest, #5642) [Link]

> Ahem. Doesn't that make a frightening amount of sense?

Well, either it means that the original authors valued stricter type
checking so much as to use C++ for only that purpose, and/or it means that
the code stems from a time when C++ wasn't really what it is now (e.g.
namespaces are a fairly recent addition) and people understood OOP as a
misspelt oops...

In any case, I don't see the angst factor in that quote, sorry.

OSes in C++

Posted Dec 17, 2004 20:50 UTC (Fri) by zakaelri (guest, #17928) [Link]

I figured that the point of using C++ inside of the kernel would be to take the features that make the code cleaner, without using all of the advanced features that are unnecessary. Anything that has a specific reason to be nasty code (e.g. the function pointer stuff used to increase performance (above)) could keep itself that way.

Meanwhile, you can organize the kernel code better, and clean up someof the grittier parts with C++'s syntactic sugar. This would make it easier to manage in the long run, and new features could be tested and added easially.

Then again, I am not a kernel hacker, and never will be. As long as it works, I don't particulalry care what the code looks like. I can leave that up to the developers. (Furthermore, C++ isn't always faster than C... look at how slow and bulky some of the apps in KDE are... and binding to modules/object libraries is a pain, in my experience.)

Quote of the week

Posted Dec 16, 2004 12:55 UTC (Thu) by danielos (subscriber, #6053) [Link]

Looking at http://criticalmass.sourceforge.net/kernel.php
I see that writing C++ modules (or other langs) could be done in a clean way. But using c++ for core kernel would involve a total redesign of kernel in a object oriented way. The problem is not on language, and if C is better of C++, the question is on the way you attack a problem in C and the way you do it in C++.
Yes, C++ do a lot of work behind, but if you know what happen, you can control even this. C approach, instead is to see everything coded cleanly ..
I swiched to C++ no long time ago, and I found I can write good code if I constantly take in mind what happen after compiling the code, so I found C++ way not so limited.
But Linux is wrote in C, there are no question here.

Copyright © 2004, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds