LWN.net Logo

My advice on implementing stuff in C:

My advice on implementing stuff in C:

Posted Oct 15, 2010 14:09 UTC (Fri) by dskoll (subscriber, #1630)
In reply to: My advice on implementing stuff in C: by michaeljt
Parent article: Russell: On C Library Implementation

I agree with the comments about C++. It's a horrible, horrible language that doesn't know if it wants to be object-oriented or not. Other brokenness besides that mentioned in the parent:

Virtual Base Classes. A real WTF if ever there was one.

Insanely complex rules about how overloaded functions are picked, what you can and can't do in constructors/destructors, etc. that mostly come about because of compiler design constraints rather than intentional language design.

RTTI. Doesn't that go against OO design?


(Log in to post comments)

My advice on implementing stuff in C:

Posted Oct 15, 2010 15:16 UTC (Fri) by nix (subscriber, #2304) [Link]

I think I agree with michaeljt's comments more than yours. Most of the things you complain about are either very rare (virtual base classes) or provided by every other OO language (RTTI) *and* rare in C++ code.

The overloaded function resolution rules aren't all that bad... however, the name lookup rules all added together are fiendish: add 'export' and I can understand people's brains dribbling out of their ears.

My advice on implementing stuff in C:

Posted Oct 15, 2010 16:57 UTC (Fri) by HelloWorld (guest, #56129) [Link]

A language doesn't need to choose if it's object-oriented or not, it can support many different programming styles. I think that this is a good thing, and many other people seem to think so too. For example, Python is also a multi-paradigm language. It allows you to do imperative, object-oriented and functional programming, yet nobody seems to complain about it.

Also, I don't see your point about virtual inheritance. It was added to the language in order to solve the diamond inheritance problem, which occurs very rarely anyway, and it does solve that problem for me. If you absolutely can't stand the feature, then just avoid it by avoiding diamond inheritance.

My advice on implementing stuff in C:

Posted Oct 15, 2010 19:20 UTC (Fri) by michaeljt (subscriber, #39183) [Link]

> Also, I don't see your point about virtual inheritance. It was added to the language in order to solve the diamond inheritance problem, which occurs very rarely anyway, and it does solve that problem for me.

I'm wondering if I understood dskoll correctly, but pure virtual classes are actually one of the things in C++ that I find good without having to qualify. I find that inheriting behaviour often makes code harder to understand without a good reason.

I do like the way go handles this with its semi-implicit interfaces (from reading about it, not actually programming in it). But of course go is still too young and little used for people to have discovered its warts.

My advice on implementing stuff in C:

Posted Oct 18, 2010 0:10 UTC (Mon) by HelloWorld (guest, #56129) [Link]

I'm wondering if I understood dskoll correctly,
I don't think so. I think that dskoll was talking about virtual inheritance. Say, you have code such as this:
struct A { int a; };
struct D1 : A { int d1; };
struct D2 : A { int d2; };
struct B : D1, D2 { int b; };
Then, B will inherit A::a twice, once from D1 and once from D2. So, inside B, you'll actually have two fields named a, and if you want to use one of them, you always have to use full qualification, that is, you have to write D1::a or D2::a instead of just plain a. Virtual inheritance solves this problem. If you change the code as follows,
struct A { int a; };
struct D1 : virtual A { int d1; };
struct D2 : virtual A { int d2; };
struct B : D1, D2 { int b; };
then the two A sub-objects in B will be collapsed to a single one. It's also explained in the C++ FAQ lite: http://www.parashift.com/c++-faq-lite/multiple-inheritance.html

My advice on implementing stuff in C:

Posted Oct 15, 2010 17:19 UTC (Fri) by HelloWorld (guest, #56129) [Link]

Oh, and by the way, if you honestly think that rules such as "Don't call virtual functions in constructors or destructors. Don't throw exceptions in a destructor." are "insanely complex", then perhaps you shouldn't be programming at all (at least not low level programming, which is what C++ is about). There are very good reasons for these rules, and you probably wouldn't be complaining if you had understood them.

You do have a point about the name lookup rules, but at least some of those problems ultimately come from C, for example the "implicit int" rule from C89 assigns a meaning to some constructs that could otherwise be made illegal (and will be made illegal in the next C++ standard). Also the rule that a function declaration must come before the first call to the function comes from C. It's actually funny to see how almost every design mistake in C++ can be traced back to some kind of fuckup in C.

My advice on implementing stuff in C:

Posted Oct 15, 2010 17:28 UTC (Fri) by dskoll (subscriber, #1630) [Link]

There are very good reasons for these rules, and you probably wouldn't be complaining if you had understood them.

I understand the rules perfectly. They came about because Stroustrup et. al. threw everything + the kitchen sink into C++. Then when the dust settled, they discovered all kinds of corner-cases that needed clarification, or weird combinations that just don't work so need to be forbidden, or silly rules that came about because of how C++ compilers must be implemented.

I've programmed since 1982 (professionally since 1990) and used C and C++ extensively. While it's obvious that C was designed carefully and thoughtfully and the C standardization committees have done a stellar job, it's also obvious that C++ was a "yeah, throw in that feature!" design followed by "Oh crap... now we have to document the weird corner-cases."

My advice on implementing stuff in C:

Posted Oct 15, 2010 18:25 UTC (Fri) by HelloWorld (guest, #56129) [Link]

Oh, another victim of the C hacker syndrome. Please get well soon :)

My advice on implementing stuff in C:

Posted Oct 15, 2010 21:33 UTC (Fri) by dskoll (subscriber, #1630) [Link]

What did I write (in grandparent to this comment) that isn't factual?

My advice on implementing stuff in C:

Posted Oct 15, 2010 22:36 UTC (Fri) by HelloWorld (guest, #56129) [Link]

Err, like, everything? Aside from the statement that you've been programming since 1982, all of what you've written is not a fact but your personal opinion, and it's also so vague that it's basically impossible to disprove it, making any discussion pointless. The good thing is that I don't need to convince you, which is why this comment ends here :)

My advice on implementing stuff in C:

Posted Oct 15, 2010 23:21 UTC (Fri) by chad.netzer (✭ supporter ✭, #4257) [Link]

Why does the author of this essay not sign his name to it? It took some hunting to find the presumed author.

In any case, C++ has had 25 years to convince the "C hackers" that it is the logical and valid step up from C. It should stand on its own merits, and not need a bunch of evangelists *still* trying to convince people from year to year. It isn't just stubborness; even after all this time, there are many valid reasons not to use it. I mean, if you set out to design a language from scratch, today, who in their right mind would come up with C++?

It would be much more convincing (IMO) to simply tell the C hackers to skip C++, and start using one of the languages that has learned the lessons of C++, and is trying to replace it. And in fact, many organizations have been essentially doing that exact thing throughout the lifetime of C++. It's quite telling...

My advice on implementing stuff in C:

Posted Oct 15, 2010 23:34 UTC (Fri) by dlang (✭ supporter ✭, #313) [Link]

what language 'learned the lessons of C++' and is so clearly superior?

the fact that C++ is still being used so heavily (in spite of people saying for 25 years that it is junked and should be skipped and people instead use a 'real' language ;-) makes me believe that all of these next-generation languages are missing something.

companies keep trying to ignore C++, but they aren't succeeding so spectacularly that C++ is dieing away.

My advice on implementing stuff in C:

Posted Oct 16, 2010 1:25 UTC (Sat) by dskoll (subscriber, #1630) [Link]

what language 'learned the lessons of C++' and is so clearly superior?

C++ is "good enough" for many purposes, and it's certainly possible to write decent C++. The problem is that you have to restrict yourself to a subset of the language and have a very disciplined programming team with strict style guidelines.

Once you start going mad with esoteric C++ features (or even not-so-esoteric ones like templates), you code can become unreadable and unmaintainable.

Too bad Objective-C didn't catch on more than C++. I think it is a much better language if your goal is "C with objects".

My advice on implementing stuff in C:

Posted Oct 16, 2010 10:59 UTC (Sat) by marcH (subscriber, #57642) [Link]

> C++ is "good enough" for many purposes, and it's certainly possible to write decent C++. The problem is that you have to restrict yourself to a subset of the language and have a very disciplined programming team with strict style guidelines.

and the result of such discipline is called... Java.

OK, maybe too much discipline :-)

My advice on implementing stuff in C:

Posted Oct 16, 2010 13:15 UTC (Sat) by HelloWorld (guest, #56129) [Link]

If your code is unmaintainable, it's usually not because of language features, but because you have misused a language feature or you have chosen the wrong one for the problem.

My advice on implementing stuff in C:

Posted Oct 16, 2010 14:31 UTC (Sat) by Baylink (subscriber, #755) [Link]

And I believe that the assertion being made here by the anti-C++ faction is that *the fundamental design of the language and it's library/template environment is such* that this is much much harder than seems warranted, given the competition.

My advice on implementing stuff in C:

Posted Oct 16, 2010 19:50 UTC (Sat) by dskoll (subscriber, #1630) [Link]

If your code is unmaintainable, it's usually not because of language features, but because you have misused a language feature or you have chosen the wrong one for the problem.

What I assert is that there are many dangerous features in C++ that are easy to misuse. This is what I mean when I write that C++ is a horrible language; there are much better-designed languages that take a lot more effort to misuse. :) (C, Tcl, Lisp spring to mind immediately...)

My advice on implementing stuff in C:

Posted Oct 19, 2010 10:23 UTC (Tue) by nix (subscriber, #2304) [Link]

Tcl and Lisp are so flexible that they can be very easy to misuse in the wrong hands. This is mostly because of the very feature that gives them expressivity: the macro system (for Lisp) or ability to redefine every word in the language (for Tcl and for that matter Forth).

Subsets of C++

Posted Oct 17, 2010 4:53 UTC (Sun) by CChittleborough (subscriber, #60775) [Link]

dskoll is right: if you select an appropriate subset of C++, you can use it to write perfectly good code, even very large systems. Several teams have done this.

The problem is that the other teams using C++ are very unlikely to be using the same subset. Worse still, the team that wrote a library that your team would like to use probably selected a different subset than your team ...

My advice on implementing stuff in C:

Posted Oct 16, 2010 3:24 UTC (Sat) by chad.netzer (✭ supporter ✭, #4257) [Link]

Note that the context is convincing diehard "C hackers", not necessarily C++ programmers, to migrate. Although, for the latter case, certainly Java is an example of a language directly intended to replace many of C++'s use cases, and both it and C# have been quite successful overall. C++ obviously has a long road ahead of it, and some exciting changes are on the horizon.

That said, of the languages meant to be "a better C++ for C hackers", several have been mentioned, and I don't claim any of will ever be "superior", in the sense of mindshare, marketshare, etc. Just that convincing those C hackers who have repeatedly objected to C++ (pardon the pun), is perhaps a fruitless battle. Personally, I'd kind of like "ooc" to become popularĀ…

As for the "lessons learned", its no surprise that many of the newer languages deliberately make an effort to ease the burden of implemention, speed of compilation, etc. Waiting for non-buggy implementations of C++'s newer features over the years left quite an impression on peopleĀ…

Ignorance at work

Posted Oct 16, 2010 11:07 UTC (Sat) by marcH (subscriber, #57642) [Link]

Oh, another victim of the C hacker syndrome.
I have read until the 5th line:
"[Linus'] opposition to any programming paradigms and concepts related to those paradigms which are not possible or very awkward to use in C. These include things like object-oriented design, abstraction, etc."
... which immediately helped me stop wasting my time.

Ignorance at work

Posted Oct 19, 2010 10:32 UTC (Tue) by nix (subscriber, #2304) [Link]

There's no object-oriented design or abstraction evident in the kernel until you look really deep into it, like the directory structure or the header files.

(oops)

Ignorance at work

Posted Oct 25, 2010 1:00 UTC (Mon) by vonbrand (subscriber, #4458) [Link]

Au contraire, it is very evident each time you take a peek a device drivers, filesystems, ...

It is in operating systems (and then simulation) where OOP was first used...

Ignorance at work

Posted Oct 25, 2010 6:54 UTC (Mon) by nix (subscriber, #2304) [Link]

Exactly my point. :)

My advice on implementing stuff in C:

Posted Oct 16, 2010 14:28 UTC (Sat) by Baylink (subscriber, #755) [Link]

Yes, I've read the first third of that page, and the C++ partisan is clearly purposefully failing to interpret Linus' words in a reasonable context, so as to have something to attack.

Nope, sorry.

My advice on implementing stuff in C:

Posted Oct 15, 2010 18:22 UTC (Fri) by chad.netzer (✭ supporter ✭, #4257) [Link]

> then perhaps you shouldn't be programming at all

That suggestion seems overly condescending.

> (at least not low level programming, which is what C++ is about).

I assert that "low level programming" is *not* what C++ is about. Modern C++ style recommends that you use smart pointers, rather than C pointers, for example. Nor should you be using C strings, C arrays, C structures, C stdlib functions, C-like error handling, C macros, etc. Basically, modern C++ encourages using full high-level abstractions for data structures and algorithms, and is thus, fundamentally, high-level. And when all these new features and abstractions are used properly, it can be a beautiful, elegant thing IMO (that takes a lot of time and memory to compile). But it's not low-level.

The fact that many people still want to use C++ as only "a better C" (ie. no exceptions, multiple inheritance, namespaces, virtual functions, the stdlib, RTTI, or even templates and RAII), and thus *not* use the new features added in the last 15 years, is a direct consequence of many of those features being "insanely complex".

But the C++ FAQ, and C++ FQA make both ends of this argument in a more elegant fashion than I can:

http://www.parashift.com/c++-faq-lite/index.html
http://yosefk.com/c++fqa/

Note how rarely the FAQ mentions pointers, btw.

My advice on implementing stuff in C:

Posted Oct 15, 2010 19:40 UTC (Fri) by Ed_L. (guest, #24287) [Link]

"But its not low level."
To a certain extent its a circular argument. As others have observed, if you want to do system level (low level) programming on *nix, then you will ultimately end up calling libc, which libraries like glibmm admittedly do a wonderful job of wrapping. For the most part. But for that small part they don't, I've yet to find a substitute for just calling libc (or a syscall) directly. And for me that's one of the beautiful things about C++: its not dogmatic, and allows one to write grotty Fortran when nothing else will do.

:-)

My advice on implementing stuff in C:

Posted Oct 16, 2010 14:32 UTC (Sat) by Baylink (subscriber, #755) [Link]

> And for me that's one of the beautiful things about C++: its not dogmatic, and allows one to write grotty Fortran when nothing else will do.

How come that's not one of the Quotes of the Week?

My advice on implementing stuff in C:

Posted Oct 15, 2010 20:29 UTC (Fri) by HelloWorld (guest, #56129) [Link]

I assert that "low level programming" is *not* what C++ is about. Modern C++ style recommends that you use smart pointers, rather than C pointers, for example.

How does that make the language any less "low level"? A smart pointer just automates stuff you'd normally do by hand (i. e. free resources or decrement a reference counter). It's just as efficient

Nor should you be using C strings, C arrays, C structures, C stdlib functions, C-like error handling, C macros, etc. Basically, modern C++ encourages using full high-level abstractions for data structures and algorithms, and is thus, fundamentally, high-level.

The use of C arrays isn't discouraged because they're "low-level", but because there are better alternatives. std::tr1::array is just as low-level-ish as a C array, it does't do bounds checking or anything fancy, and it's just as efficient. The difference is that it offers the interface of an STL container, allowing you to use STL algorithms with it.

The same basically applies to C macros. The MAX(x,y) macro kind of works, but std::max(x,y) works better. It'll complain if x and y are not of the same type, and it won't evaluate its arguments more than once. std::max isn't somehow higher-level than MAX, it just sucks less.

Some things in C++ actually raise the level of abstraction, for example with std::string you don't have to worry about memory allocation any longer, since the class will do it for you when needed. If you can't afford that, nobody is going to blame you for not using it. C++ was deliberately designed not to force some style of programming on the user, be it a high or a low level one (unlike C, which forces you to program on a low level of abstraction all the time).

My advice on implementing stuff in C:

Posted Oct 15, 2010 22:49 UTC (Fri) by chad.netzer (✭ supporter ✭, #4257) [Link]

> unlike C, which forces you to program on a low level of abstraction all the time

And so why did you claim above (while admonishing others) that: "low level programming [...] is what C++ is about"? My claim is that it is about much more than that. Agree?

My advice on implementing stuff in C:

Posted Oct 15, 2010 22:55 UTC (Fri) by HelloWorld (guest, #56129) [Link]

Yes, perhaps I should have made it more clear that C++ is also about low level programming.

My advice on implementing stuff in C:

Posted Oct 15, 2010 22:23 UTC (Fri) by wahern (subscriber, #37304) [Link]

the "implicit int" rule from C89 assigns a meaning to some constructs that could otherwise be made illegal (and will be made illegal in the next C++ standard). Also the rule that a function declaration must come before the first call to the function comes from C. It's actually funny to see how almost every design mistake in C++ can be traced back to some kind of fuckup in C.

Both of those were features at the time (and the latter at least arguably still). They made writing a compiler and linker significantly easier. Given that ease of implementation was evidently at the very, very, very bottom of C++'s list of priorities, you should blame C++, not C, if those were carried forward.

Compatibility is no excuse because C++ is not compatible with C at the source level. C++ people always seem to demur on this issue, arguing that they're only incompatible at the fringes. As a primarily C developer who occasionally has to muck around w/ C++, getting real C code to compile in "extern C" mode is a nightmare. In my experience, I prefer to view C++ and C as not compatible at all; this makes for fewer headaches. In practice compatibility really stems from shared ABIs, and languages like Go and D make little pretense about this reality.

I don't have any real gripes with C++. I choose not to use it for very idiosyncratic reasons; namely that it dropped implicit conversion of void pointers. I also eschew Java largely because it has no unsigned integers, and also because Java is incredibly unportable outside of Windows and Linux.

My advice on implementing stuff in C:

Posted Oct 16, 2010 21:40 UTC (Sat) by jzbiciak (✭ supporter ✭, #5246) [Link]

Compatibility is no excuse because C++ is not compatible with C at the source level. C++ people always seem to demur on this issue, arguing that they're only incompatible at the fringes. As a primarily C developer who occasionally has to muck around w/ C++, getting real C code to compile in "extern C" mode is a nightmare. In my experience, I prefer to view C++ and C as not compatible at all; this makes for fewer headaches.

Hmmm... I haven't had too much trouble moving C code to C++. If your point is that the experience isn't edit-free, I'll give you that though. C++ is generally much pickier. My main issues have been that the C++ compiler is much more righteously indignant about const-abuse1, and it wants me to explicitly cast pointers to void * (which you also mentioned).

I never thought I'd get into C++ much, but I have totally gotten hooked on templates and the stricter type checking. Modern compilers do a fantastic amount of work at compile time, and I love bringing that force to bear on programming problems. I also actually like that I have to propagate const around more proactively: it exposes thinkos in my design earlier. And I especially like the new reinterpret_cast vs. static_cast vs. dynamic_cast vs. const_cast. It's like having a torque wrench, flat head screwdriver, Philips head screwdriver and a hammer, rather than just having a 20lb sledge.

All that said, I've written way more C than I have C++ and still find C my default go-to language when writing in a compiled language. And lately, I've been writing a lot more Perl. You won't catch me CamelCasing in C or C++, although I will name classes Like::This in Perl. When in Rome...

What I don't understand is all the language hate between C and C++. Save your ire for Python. ;-)

(Just kidding on the Python part!)


1 Yes, I know you can get the same with C code if you use a good compiler and crank up the compiler warnings. And believe me, I do crank them up.

My advice on implementing stuff in C:

Posted Oct 17, 2010 19:14 UTC (Sun) by wahern (subscriber, #37304) [Link]

C99 has diverged considerably from C89, the forking point of "extern C". The biggest headaches for me are named initializers and compound literals, both of which are used in headers and macros of newish C code.

While the different kinds of casts are nice in C++, casting is frowned upon in both C and C++. Bjarne says that he purposefully made casting in C++ ugly to dissuade people from casting, and that part of the design criteria of C++ was to reduce the need to cast. And yet in actual code I see casting as far more prevalent in C++ than in C, maybe because people see the feature and feel it was put there to use freely; I dunno. Much complexity was added to replace the loss of implicit void pointer conversions, and I'm not sure there was any net gain. In any event, it's a PITA at the boundary of C and C++

My advice on implementing stuff in C:

Posted Oct 17, 2010 19:50 UTC (Sun) by jzbiciak (✭ supporter ✭, #5246) [Link]

I hear you on the missing named initializers. I had forgotten about that. I guess, other than using restrict generously, I haven't started using too many C99-specific features.

I didn't know about the compound literals.... nifty!

I don't use a lot of casting, personally, but where I do, I like the ability to specify what exactly I'm trying to accomplish. Where I use casting most is in embedded programming, where I need to cast between a pointer type and an unsigned int. That comes up a lot when talking to peripherals. reinterpret_cast makes it so much clearer what I'm trying to do, IMHO.

My advice on implementing stuff in C:

Posted Oct 17, 2010 23:47 UTC (Sun) by foom (subscriber, #14868) [Link]

> And yet in actual code I see casting as far more prevalent in C++ than in C

I suspect this is simply because you *can* see the casting in C++: a "static_cast<Whatever *>(x)" sticks out like a sore thumb, vs the almost-invisible C-style parenthesized type expression.

My advice on implementing stuff in C:

Posted Oct 19, 2010 9:54 UTC (Tue) by nix (subscriber, #2304) [Link]

What? Implicit int doesn't have anything to do with the complexity of name lookup. I was thinking of things like Koenig lookup, the effect of templates on name lookup in general, and what happened to it when 'export' came in. All the rules in isolation are sensible, but in combination it's fearsome.

My advice on implementing stuff in C:

Posted Oct 19, 2010 14:46 UTC (Tue) by HelloWorld (guest, #56129) [Link]

It does have to do with the name lookup rules in very non-obvious ways. I quote from "Design and Evolution of C++", page 141/142:
typedef int P();
typedef int Q();
class X {
  static P(Q); // define Q to be a P. 
               // equivalent to ''static int Q()''
               // the parentheses around Q are redundant

               // Q is no longer a type in this scope

  static Q(P); // define Q to be a function taking an argument of type P
               // and returning an int.
               // equivalent to ''static int Q(int());
};

Declaring two functions with the same name in the same scope is fine as long as their argument types differ sufficiently. Reverse the order of member declarations, and we define twi functions called P instead. Remove the typedef for either P or Q from the context, and we get yet other meanings.

This example ought to convince anybody that standards work is dangerous to your mental health. The rules we finally adopted makes[sic] this example undefined.

Note that this example -- like many others -- is based on the unfortunate ''implicit int'' rule inherited from C.

My advice on implementing stuff in C:

Posted Oct 19, 2010 15:32 UTC (Tue) by nix (subscriber, #2304) [Link]

Ah yes, I forgot that ingenious example. However, my point stands: this is not a name lookup problem, it is a particularly ingenious use of the parsing rules around implicit int to produce radically different parse trees from nearly identical input (and by no means the only example: see Alexandrescu's wonderful code in _Modern C++ Design_ to execute arbitrary code at compile time via abuse of sizeof().)

But, yes, this sort of example is probably an indictment of C++. Clarity in coding this is not!

My advice on implementing stuff in C:

Posted Oct 21, 2010 19:02 UTC (Thu) by ccurtis (guest, #49713) [Link]

Virtual Base Classes. A real WTF if ever there was one.

I've used a virtual base class to serialize access to a piece of hardware with software fallback. Allows for easy rate-limiting when the amount of queries might exceed the hardware precision and reading the device is (potentially) slow ...

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