OO C is passable

My problem with C++ bashing is that I'm overqualified. Ever since I've published C++ FQA Lite, I've tried to stay out of "C++ sucks" discussions. I've said everything before and I don't want to say it again. And then much of the C++ arcana is finally fading from my memory; good, no need to refresh it, thank you.

I don't always quit those discussions though. How should I do that? If I were someone else, I could send a link to the C++ FQA to end the discussion ("if you really care, check out yada yada"). But I can't use this link myself, because "OMG, did you actually write a whole site about this?!"

So the last time I didn't quit this discussion, I said: "You know, at some point you actually start to prefer plain C". The seasoned C++ lover replied: "You mean, the C with classes style, with no tricky stuff? Sure, don't we all end up writing most of the code like that?" No, said I, I meant plain C. No plus signs attached. File names ending with .c. C without classes. C.

"Now that is fanaticism," said the guy. "What could be the point of that? You know what? You may like C++ or you may dislike it, but face it: C just isn't good enough".

Fanaticism?! Now that is an insult. Yes, I recently decided to write a bunch of code in plain C inside a C++ code base. I did this after maintaining the previous, C++ implementation of that stuff for 4 years. For at least 3 of those years, I didn't do any significant rewriting in that code, because it could interfere with schedules and it would complicate merges. Although some pieces were hopelessly awful. Sloppy text parsing interleaved with interrupt handling (I exaggerate, but only very slightly). But it worked, so it was hardly urgent to fix it.

And then it had to be ported to a new platform. And I obsessed over the rewrite-or-extend question for a lot of time. There was a critical mass of incompatible new stuff, so I chose "rewrite". But. I decided to write the new version in C++. Why? Because it's a C++ code base, and people are used to C++ and its style and idioms, however brain-damaged. "So you, YOU will do it in C++ after all?! You're a wuss," said my manager, an old-time C++ hater. Time for the rhetorical question. Does this sound like the story of a fanatic?

OK then, why did I decide to do it in C after all, you may ask. I'll tell you why. I did it because everybody was sick and tired of the build time of my auto-generated C++ code.

You see, the whole thing is data-driven. The data objects describing its workload are generated at build time. Do you know any way of generating C++ code that doesn't compile slowly as hell? I don't. I've generated both "real" code (functions doing stuff) and "data definition" code (which does practically nothing except for calling object constructors). And it always compiles slowly. Sometimes turning optimization off speeds up compilation significantly, sometimes less significantly. But the best part is this: you never know exactly what's your problem. Try to profile a C++ compiler.

It's not that manually written C++ code is such a blast. For example, we have a ~2K LOC file defining a class template and explicitly instantiating it 4 times. It worked fine for years, until it met a particular version of the Green Hills C++ compiler. That version decided that it needs more than 1.5G of memory to compile that file. I don't know how much more exactly, because at that point my workstation suffocated, and could only breathe again when the process ran out of swap space and died. Here's another rhetorical question: how the fuck are you supposed to figure out what the fuck causes this problem?

What's that? "It's a tool problem, not a language problem?" Bzzzt, wrong answer! It's neither a language problem nor a tool problem; it's my problem, because I must fix it. And this is why I don't want to deal with a language that consistently breeds tools which create such problems for me. But since I'm hardly a fanatic, and I know exactly why I do want to work on this particular C++ code base, I hold my nose and I delve right into the pile of excrements and find out that if you instantiate each template in its own file, then the process memory consumption barely crosses the 350M mark. Nice and compact, that. So, let's use 4 files.

Nope, manually written C++ code isn't a picnic. But auto-generated code is worse, because it relies on some set of features and uses them a lot. The number of uses per feature per file matters. 1 explicit template instantiation per file = 350M of compiler process memory. 4 instantiations = out of memory. What about "simpler" features, but hundreds of uses? The compiler will grind to a halt for sure. Um, you might say, don't other languages have "features" which will be used hundreds of times by generated code? Yes, they do. Those features just DON'T SUCK quite as impressively. Go ahead, show me a problem with compilation speed anywhere near what C++ exhibits in another language.

Face it: C++ just isn't good enough. "If you really care about C++ parsing complexity, check out the FQA yada yada". I wrote "a whole site" about it, you know. Bottom line: if you generate native code, you want to define your object model such that the generated code can be C code. Assembly is pointlessly low-level and non-portable, and C++ sucks. Believe me, or die waiting for your code to compile.

So, C. My object model will be in C. Um. Bummer. It's an OO thing, with plugins and multiple inheritance and virtual inheritance. It has to be. You have orthogonal plugins which want to derive classes from a common base - a classic diamond hierarchy. Well, I can have a couple of macros for doing MI-style pointer arithmetic, by fetching the derived-type-specific offset of each base class object at run time. No big deal. I even like it better than the C++ MI downcasting syntax - at least you know exactly what you're doing, and you don't need to think whether it should be dynamic_cast or static_cast or eat_flaming_death_cast to really work.

But I miss virtual functions. I really do. I sincerely think that each and every notable feature C++ adds to C makes the language worse, with the single exception of virtual functions. Here's why not having virtual functions in C sucks:

  • You can't quite fake them with C macros.
  • Virtual function call is a shortcut for obj->vtable->func(obj, args). The OO spelling - obj->func(args) - is of course better.
  • You'll usually try to make the C version shorter: obj->func(args), obj->vtable->func(args), or obj->func(obj, args). Quite likely you'll find out that you really needed to pass obj to func and/or the vtable level of indirection. Updating the code may be tedious/too late/really annoying (because of having to admit a stupid mistake). The eventual lack of call syntax uniformity will also be annoying.
  • Decent C++ debuggers automatically downcast base class object pointers to the real run time type when you inspect the object, even when C++ RTTI support is turned off at compile time. They do it by looking at the vtable pointer. Achieving this with OO C is only possible on a per-OO-faking-style, per-debugger basis, using the ugly debugger scripting facilities. Most likely, you won't do it and choose interactive suffering each time you debug the code, having to figure out the actual type yourself and cast pointers manually.
  • With virtual functions, base class implementations are inherited automatically. With explicit vtable structures, you need to either have links to base class implementations (the slow MFC message table way), or you need to explicitly and fully fill the vtables in each derived class. Possibly using the C default of trailing zeros in aggregate initializers as in vtable_type vtable={foo_func,bar_func} /* and the third member, baz_func, is 0 - we check for zero vtable entries before calling our pseudo-virtual functions */. Run time checks for null function pointers can make initialization code smaller, but they also make function calls slower.
  • With explicit vtable initializers, you only see the position of the function in the vtable initializer and its "derived class" name (my_class_baz_func), not its "base class" name (baz_func). You are likely to have a slightly inconsistent "derived class method" naming convention, making it annoying to figure out exactly which base class function we're overriding here.

An impressive list, isn't it? You can see from it that I've really put my employer's money where my mouth is and actually worked with OO C for a while. Aren't C++ classes with virtual functions simply better? No, because C++ classes don't support aggregate initialization. If you have C structures with vtable pointers, you can use the frob_type g_obj={&vtable,5,"name"} initialization style. This translates to assembly that looks like so:

g_obj:
.word vtable
.word 5
.word .str34
.str34:
.asciz "name"

This compiles and loads as fast as it gets. Now, if you choose real C++ vtables, you rule out aggregate initialization once and for all. Your initialization will instead have to be spelled as frob_type g_obj(5, "name"), and even if you have no constructor arguments, C++ will generate a constructor to set the vtable pointer.

The good news: at least the explicit reference to the vtable in our source code is gone. The bad news: with many objects, the C++ version compiles very slowly (I've checked with GNU and Green Hills C++). It also generates a much larger image, since it emits both global data objects and assembly code copying them into object members at run time. The latter code also costs you load time. And if you crash there, good luck figuring out the context. But as far as I'm concerned, the worst part is the build time explosion.

Yes, yes. It's not important. And it's FUD. And it's a tool problem. A "good" compiler could optimize the C++ version and generate the same assembly as the C version. And it could do it quickly. Those compiler writers are just lame. I completely agree with you, sir. Just go away already.

By the way, the same trade-off happens with C++ containers, like std::vector. They're better than {T*base;int size;} structures because you have the shortcut of operator[] (as opposed to array->base[i]). And because debuggers can gracefully display all elements of std::vector as a list of the right size. Some of the debuggers can. Sometimes. Sometimes it breaks. But when it doesn't, it's fun. But, again, you can't use aggregate initialization once your structure has a std::vector in it. And C++0x won't solve it, because its pseudo-aggregate initializers are syntactic sugar, and my problem here isn't the syntax, it's the build time.

And std::vector forces allocation of data on the heap (let's not discuss custom allocator templates, 'cause I'm gonna vomit). Can't have the base address of a std::vector point to a global variable generated specifically to hold its data.

I like things to point to globals generated to hold their data. Helps a lot when you debug, because your pointer is now firmly associated with a symbol table name. And no matter what memory-corrupting atrocity was committed by buggy code, that association will be there to help you figure things out. And heap corruption is very common in C++, because it's a completely unsafe language. So I care a lot about debugging core dumps with corrupted memory. Which is why base, size structures get my vote.

And that's an important point: you can live with just safety, or just control, and of course with both, but if you have neither safety nor control, then, sir, welcome to hell. Which is why OO C is worse than OO in Java or Lisp or PowerShell, but better than OO in C++.

And OO C is not all bad after all. Manually faking virtual functions has its benefits:

  • You can dynamically "change the object type" by changing the vtable pointer. I first saw this in POV-Ray, which has a vtable for circles and a vtable for ellipses. When a geometric transformation applied to a circle object transforms it to an ellipse, the vtable pointer is changed to point to the more generic and slower ellipse rendering functions. Neat. You could do this using C++-style OO by having another level of indirection, but with C, it's marginally faster, which can matter sometimes. And the C way is much neater, which is useful to balance the frustration caused by the drawbacks of fake OO. I use this trick a lot for debug plugins in my fake OO stuff.
  • Likewise, you can overwrite individual vtable entries. This changes the type of all objects of that "class" - primarily useful for logging and other sorts of debugging.
  • Sometimes you really don't need obj->vtable->func(obj, args) - say, obj->func(args) is good enough. And then you win.
  • You don't have to use a structure with named members to represent a vtable. If a bunch of functions have the same prototype, you can keep them in an array. You can then iterate over them or use an index into that array as a "member function pointer". This way, you can have a function calling a method in a bunch of objects, and the method to call will be a parameter of that function. The C++ member function pointers don't support the iterate-over-methods trick as efficiently, and their syntax is remarkably ugly.
  • That each function has a unique non-mangled name (as opposed to A::func, B::func, etc.) has the benefit of making the symbol table clean and independent of the non-portable C++ mangling. And you no longer depend on the varying definition look-up abilities of debuggers and IDEs (I don't like the lengthy disambiguation menus when I ask to show me the definition of "func", and these menus show up too often).
  • If you serialize the objects, or you have programs shoveling through process snapshots and analyzing them, the memory image of OO C objects is easier to deal with because of being more portable. Basically you only need to know the target endianness, the alignment requirements of built-in types, sizeof(enum) and the implementation of bitfields, if you use that. With C++, you need to know the layouts of objects when multiple/virtual inheritance/empty base class optimization/etc. is at play; this isn't portable across compilers. And even that knowledge is only useful if you know the members of each class; otherwise, you need to parse the layouts out of non-portable debug information databases - parsing C++ source code is not an option. With C, you can parse its files and find the struct definitions and figure out the layouts and it will be really easy and portable. Been there, done that.

Of course, you can use all those tricks in a C++ object model when needed, and use virtual functions elsewhere. And if C++ didn't suck so much (for example, if code compiled reasonably fast and if it had a standard ABI and and and…), that would be the way to go. But since C++ just isn't good enough, and I have to use a pure C object model, I point out the benefits of the sucky thing that is OO C to make it a little less bitter.

Overall, OO C is passable, more so than the endless rebuild cycles caused by our previous C++ object model. So you have a bunch of lame stuff. Having to use a typedef because struct A can only be referred to as struct A, not A. No default function arguments. Having to declare variables at the beginning of the scope. (BTW, I use C89. I don't believe in C99. I'm a Luddite, you already know that, right? I don't think C99 is as portable as C89 here in 2008).

So, yeah. I do miss some C++ features, but it's a small itching here, a tiny scratching there, nothing serious. I can live with that as the price for not having my legs shot off by mysterious compile time explosions and other C++ goodies. OO C is good enough for me. Fanaticism? You be the judge.

43 comments ↓

#1 wsgeek on 06.02.08 at 6:30 pm

If you don't mind installing the runtime (I assume you're on Linux?) and learning a slightly new syntax, then use Objective-C. It's a little esoteric, but then so is doing OO in C.

#2 haberman on 06.02.08 at 7:42 pm

If C is your preferred alternative to C++, I think you'll have to get a better example of "outstandingly complicated grammar" for your C++ FQA. The example you have there now:

AA BB(CC);

But C has exactly the same kind of ambiguity. Is this a pointer declaration or multiplication?

A * B;

#3 Yossi Kreinin on 06.02.08 at 9:50 pm

Regarding Objective-C: I have no problem with its Smalltalkish OO syntax, nor do I mind the "esoteric" part by itself. What I do care about in this particular case is (1) the overhead of OO features - I think OO C is faster than ObjC, at the cost of being way uglier, non-standard and anal-retentive, and (2) portability - C>C++>ObjC in terms of compiler support on less popular platforms. A third problem is interoperability with existing C++ code. If I didn't care about interoperability, I'd use D, and if I didn't care about speed, I'd use a dynamic language. That said, if I ever have to use Objective-C (because of having to use any sort of existing code in it), I'll consider myself lucky in the linguistic sense - it surely beats using C++ by a large margin.

Regarding A * B: right, but wrong. In C, the "context sensitivity" problem can be solved using a single dictionary keeping all typedef names. In C++, you need just a little bit more effort than that, about 10 man years worth of "little bit". The main problem is (guess what?) templates. Check out these two examples I took from a reddit thread:

http://yosefk.com/c++fqa/web-vs-c++.html#misfeature-2
http://yosefk.com/c++fqa/web-vs-c++.html#misfeature-3

I don't like the way it sounds, but I'm right, you're wrong, and I'm tired of rehashing this argument. Just ask comp.lang.c++.moderated about the problems with parsing C++. They are quite a friendly bunch - I'm a C++ hater, so I've seen their harsh side, and even that was pretty soft.

Oh, and C isn't my "preferred alternative" to C++ in the general case, just in this case. In general, I'll try to use the highest level language possible, it's just that in real time embedded software, your language choices are a bit limited.

#4 Yossi Kreinin on 06.02.08 at 9:53 pm

Forgot to tell wsgeek I wasn't on Linux; I cross-compile to a bare metal target.

#5 octopiler on 06.02.08 at 10:48 pm

We use C++ as a better C for an embedded development project => no class hierarchies -just a few classes to make things easier, heavy use of templates for optimized code generation.

I was facing similar compiler time problems with C++ because of the massive template instanciation. Fortunately using the latest g++ compiler helped cutting it down but a huge factor so I left it that way. But it was always tempting though to bundle python with our source code and generate non-templated C/C++ code at build time using a python script.

I don't like C++, but I'm not happy with C either. Since in our group no one has a OO fetish and neither does the project require any huge class hierarchies we are right now using a nice subset of C++. Still I'm bugged with the lack of proper tools that can work with C++ code.

D seems to have a nice chance at this but it might take quite some time for its ecosystem and tools to mature.

#6 ionial on 06.03.08 at 8:17 am

just for you:
http://www.cs.rit.edu/~ats/books/ooc.pdf

#7 Brandon Moore on 06.03.08 at 1:07 pm

It's explained in your links, but it's worth repeating: To parse C++ you have to do template instantiation!

Best to avoid it, but if you must parse C++, try Elsa. It's a library which actually gets it right. It comes with a handy tool called ccparse, which turns code into a line oriented AST which you can actually reliably search.

#8 jemptymethod on 06.03.08 at 4:14 pm

Curious, did you ever consider embedding a Lua interpreter and then implementing your objects as Lua tables with meta-methods?

#9 links for 2008-06-04 on 06.03.08 at 4:38 pm

[...] OO C is passable (tags: c c++ compiler development language performance programming) [...]

#10 haberman on 06.04.08 at 10:01 am

Umm, dude. I am intimately aware of how difficult C++ is to parse, though I have to say that your FQA is a really useful catalog of all these issues.

All I said is that you'd have to find a better example than AA BB(CC); to demonstrate that C++ is worse than C, because C has something that is almost exactly as difficult. And it appears that elsewhere in the FQA you do have better examples.

#11 haberman on 06.04.08 at 10:12 am

Also, I think you overstate C's ease of parsing. It may be a lot (lot) easier than C++, but it's still not easy, at least according to people who have tried to do it:

"When I (George) started to write CIL I thought it was going to take two weeks. Exactly a year has passed since then and I am still fixing bugs in it. This gross underestimate was due to the fact that I thought parsing and making sense of C is simple. You probably think the same. What I did not expect was how many dark corners this language has[.]"

Who says C is simple?

Don't get me wrong, I'm a C guy at heart, I just think your perspective is a little skewed in favor of anything that isn't C++. Other languages have their issues too.

#12 Yossi Kreinin on 06.04.08 at 1:53 pm

"Intimately?" Did you actually go far trying to do this? My condolences.

Maybe I should expand on that example, 'cause it really isn't sufficient to mention AA BB(CC); you ought to know how hard it is to tell a type name from an object name in C++ as opposed to C.

Regarding the ease of parsing C: you can grab a working yacc grammar, and all you'll need to do is (1) run the preprocessor before using the grammar and (2) add a lexer hack looking up typedef names. So much for /parsing/. The difficulties described in "Who says…" are all either semantical or non-standard. Lots of tools won't bump into them.

Of course, today you can get a working gcc/g++ front-end producing the excellently documented LLVM bytecode for free, so porting C++ is as easy as ever. The problem is that C++ will still parse slowly, and that many things are wiped out by the front-end. The LLVM project is working on a new C++ front-end; that could be great. It's amazing that one can still be excited about the future of C++ parsing though, regarding its age. And my hopes are low; an object model representing the complexity of a C++ program will never be fun to hack on.

Of course other languages have issues, they're just 10x smaller. No, really. It happens. It's possible. You can have one popular language that has 10x worse issues than others.

#13 Yossi Kreinin on 06.04.08 at 2:05 pm

Regarding Elsa: many thanks for the pointer. I actually might need a C++ front-end some time soon. OMG.

Regarding Lua: if I had to embed a general-purpose scripting language in my C/C++ app, it would be this. 6KLOC, just like pforth, except it runs Lua, not Forth. Today Lua gets the highest embeddability * linguistic virtue in my book. I wish it had bitwise operators though. And unfortunately all opportunities to use it in my current environment were lost for suckier options. As to the case in the article, I need the crud to run really really fast, so it's plain C.

Regarding the OO C link: they have a preprocessor of their own. In shell/awk. BARF. Otherwise, clean style. I like C. i_like_c().

Regarding using C++ as a worse C: good luck :) Seriously - had lots of fun with using templates for optimizing code. BARF. We already use Python for code generation, on a major scale. Nice. Better, at least.

#14 pbannister on 06.15.08 at 9:56 am

Your problem is not with C++.

For some reason you are generating code that makes massive use of C++ templates, and that usage causes you a lot of pain. Um … so don't.

Templates in C++ are the result of attempting to invent a new language feature while standardizing the language - almost always a bad idea - and we are stuck with the result. In effect templates are a sort of compile-time interpreted embedded mini-language within C++, with awkward and incomplete semantics.

No surprise that templates are a source of grief. Not really a problem - just use templates sparingly.

Auto-generating masses of code with huge use of the weakest part of C++ sounds … dubious.

What you do not get at is the underlying problem that you are trying to solve. Where you went wrong is most likely somewhat upstream of the problem you describe.

#15 Yossi Kreinin on 06.15.08 at 10:45 am

I described two distinct things:

* manually written template code
* generated constructor calls, mostly not relying on templates

So it's not templates. I also described exactly what I don't like about initializing data with constructors as opposed to aggregate initialization.

If you find a subset of C++ that gives you something C doesn't without taking away too much things C does, let me know.

#16 pbannister on 06.15.08 at 11:34 am

Given that C++ is C with additions, so far as I can tell nothing is taken away. I have done the hand-written sort-of OO style in C. It is a pain to write and maintain. (This was in the late 1980's when there was no C++ compiler I could use.) Surely you do not think C is remotely equivalent for this usage?

I am sure you are trying to make some sort of point here. What that point might be I cannot tell. There must be something you are leaving out. Something must be odd in your usage.

Given that C++ compile times are very fast and essentially identical to C compile times, in my usage - you must be doing something very different. What?

The combination of unusually slow compile times and an unusually large compiler memory footprint suggests usage - of some sort - for which the compiler is not suited.

#17 Yossi Kreinin on 06.15.08 at 11:56 am

"Very fast C++ compile times" sounds intriguing, and I bet it would intrigue 8 to 9 out of 10 C++ users out there. Do you use several different compilers? Problems could vary. Also, the system size matters a lot; in C++, you need to enforce very strict module boundaries, otherwise you pay roughly quadratically as your system size grows. Not everybody does that. The price for mediocrity, or even "non-excellence", is tremendously high in C++.

"Given that C++ is C with additions…" - the seven-legged cat picture is just for you: http://yosefk.com/c++fqa/linking.html#link-3

Things which are taken away by adding features: the ability to easily parse your code (and implement poor man's reflection by parsing binaries), short build time (true for just about everybody in the industry except you), the ability to count on your understanding of code obtained by, um, reading it (think overload resolution and implicit conversions), few failure modes (think of classes which accidentally got the same name and now objects of one class are initialized by the constructor of the other class, completely silently), ABI compatibility, the level of standard compliance and and and… "Superset" and "superiority" are too different things.

The point that I'm trying to make here is as simple as it gets: C++ sucks like a vacuum cleaner powered by a nuclear reactor, to the point where using OO C is less disgusting than using C++, even though it's pretty disgusting by my standards.

This opinion is based on my personal experience; nobody else has to agree with it. After all, people come to the world equipped with feet for the single reason of being able to shoot themselves in the feet in the very exact way they see fit, without copying others' shooting habits. However, your disagreement doesn't invalidate my experience, nor does it mean that everybody can or should copy your C++ usage patterns to do their (different) jobs and then they'd live happily ever after.

#18 Entity on 06.20.08 at 10:39 pm

pbannister what Yossi is pointing out, is he very accustome to working very close to the hardware. Not only that but needing full control over the language. C++ his viewpoint that he points out so many times, just simply relys on a small set of the language features uses them everywhere. That coupled with the fact that C++ compilers typicaly take their own assumptions on your C++ code. So even though your program may compile fine in one compiler it would break in another for completly because of an assumption like Template constructor instantiation, or how it lays out multiplie inheritance table and memory.

In one single development enviroment like Visual Studio 8/9 your dealing with one platform and one compiler so you can resolve the problems easily. Though even trying to get code generated with visual studio C++ 6.0 and 8.0 is a challenge in itself, and normaly means droping back down to an C interface.

Where Yossi works where he needs portability to run on many hardware, and many different compilers. Just something that many developers like myself will never experience.

#19 Yossi Kreinin on 06.26.08 at 8:23 am

BTW, I know several people who think they like C++, when in fact they like Microsoft Visual C++. These people would like Microsoft Visual C# even more. Much more. I've seen this happen even to die-hard C++ weenies driven to C# by some circumstance or other.

#20 Pages tagged "passable" on 05.09.09 at 12:18 am

[...] bookmarks tagged passable OO C is passable saved by 5 others     MovieMan2011 bookmarked on 05/09/09 | [...]

#21 A must-read: http://www.yosefk.com/blog/… « ooc devblog on 05.29.09 at 5:34 pm

[...] A must-read: http://www.yosefk.com/blog/oo-c-is-passable.html [...]

#22 Amos Wenger on 05.29.09 at 5:38 pm

It's funny that I stumbled upon this article.

This is my attempt at another hack on C to add object orientation goodness without C++'s clutter in syntax, semantics, implementation, and marketization http://code.google.com/p/ooc-language

Yossi, I would be honoured to have some feedback from you on this humble project =) While it may not be everything you'd expect, I like to think it's slowly getting close.

#23 Yossi Kreinin on 05.30.09 at 1:01 am

@Amos: interesting stuff. Regarding your target audience: I assume that it's not people who're after something like natively-compiled Java, since they already have gcj which gives them exactly that; I thus assume it's those who need integration with C code, and they want OO/other language features such as foreach that they can't get from a C compiler. And: they don't want C++ (understandable), they don't want Objective C (because of - message passing speed? Smalltalk syntax?), they don't want D (because it doesn't compile C?).

So what I was looking for was what would attract someone to ooc as opposed to Objective C or D, and I'm not sure I quite got it (Objective C and D both being languages with an object model lacking the major defects of the C++ object model, namely, the #include/private: based pseudo-encapsulation and manual memory management.)

#24 Amos Wenger on 05.30.09 at 1:41 pm

@Yossi: thanks for the fast review =) Absolutely, compiled Java is taken care of not only by gcj but by Excelsior JET, etc. Moreover, Java's syntax is admittedly verbose, and in the same VM ballpark, I'd rather recommend the excellent Scala.

So why not Objective-C? as for me, it's mostly the Smalltalk syntax I'm put off by. Really, I read again their wikipedia page today and my eyes still hurt. I appreciate the "parameters pseudo-naming" effort, but I have a different conception of "readable".

About not wanting D, it's really a sad, sad reason. IMHO, D got a lot of things right: ditch the preprocessor, integrate garbage collection, throw out C++ nonsense, etc. But.. the main implementation, dmd, is closed-source. Other implementations are mostly lagging behind/incompatible. Also, it looks like the transition between D 1.0 and D 2.0 is never going to happen. The enormous momentum I saw in 2007/2008 has been slowed down much. The only escape I see for D is outstanding community support: http://planet.dsource.org/

Also, while ooc may now look like a stripped-down Java, it's probably because I had to rush the first implementation in 4 months (for a school assignment in C I wanted to use object orientation in). I'm now cleaning up syntax before throwing in nested functions, closures, "safe function pointers", more introspection (for now you can just test A.class == B.class and do A.class.name, but it's a start).

#25 Amos Wenger on 05.30.09 at 1:57 pm

In the end, ooc's goal is not stealing/attracting users away from languages like Objective-C or D. For small projects it boils down to personal taste, for bigger projects there are harder requirements, and I think maybe ooc's translateability to pure C may help (portability = deployment on _tight_ embedded devices?)

Also, direct 'concurrents' of ooc, or rather 'siblings', include Vala: http://live.gnome.org/Vala , Cobra: http://cobra-language.com/ , and probably others I do not yet know of.

As I have absolutely no marketing urge, I prefer to openly inform of other languages, as I do on the recently-created manifesto page: http://code.google.com/p/ooc-language/wiki/Manifesto I do believe in diversity, and I'm always interested in other languages.

#26 Dmitry on 02.11.10 at 4:39 am

>Here’s why not having virtual functions in C sucks:

Why not to define own language syntax (own script) and translate it to oo-c (whatever)? And "write" own translator?
When I think about it, I assumes that syntax of parsing/translating rules is needed. (To easily adjust script syntax or define new one)
Hence parser/code-generator of/from that rules is necessary.
(?FRACTALS?) Кароче фракталы, замкнутый круг… But it can be resolved, worth it?
я intends to do so (но немного застрял).

#27 Yossi Kreinin on 02.11.10 at 6:08 am

If I decided to roll my own language, the two things I'd try to make sure would be (1) that the chances of getting it used in the relevant environment are high and (2) that I wouldn't invest too much in the initial implementation (that is, throw together a parser in a way solid enough to be counted on but without trying to rethink the whole parsing problem inside out, etc.)

#28 Mgr. Šimon Tóth on 03.09.10 at 11:56 pm

OK, well. I did read the whole post. I'm a full time C developer + I teach C and C++ on a university.

I personally consider embedded systems to be pretty much the only area where a choice C over C++ can be reasonable.

I hate when I see C used for project where C++ would be much more appropriate. I think I know enough about both C and C++ to say that most of the FQA is just bullshit. Not because it contains some misinformation, but because it exaggerates every possible flaw in the C++ by giving nonsense examples (that no one would ever write) that always go beyond good C++ coding style and usually even beyond the C++ standard. I always say that everyone should read the FQA to realize where they could end if they don't write proper code (or end up working with idiots).

It's curious that you like to preserve a feature that is pretty much useless in the field you are developing for (embeded systems - deduced from the compiler you use). I would expect you to use static polymorphism, and flat inheritance combined with policy-based design. OK, I don't have the insight, so you might be building huge code base that actually makes such dynamic features useful, but from my standpoint its just curious.

C++ compilation time and memory consumption is a problem. I must say that I'm not aware about the state of the compiler you use, but most embedded compilers I have seen are something like 10 years behind in C++ support. I myself am waiting for the C-Lang to fully support C++, because it will fly and shine like no other compiler before (even more then the current GCC head). But I definitely wouldn't go around and compare the compilation speed with other languages, unless you show me a language that can be compiled into binaries with similar speed (and no, not even C reaches C++ speed http://shootout.alioth.debian.org/).

I highly doubt your conclusion that if implemented in C++ (on the same level of abstraction) the code would compile a lot longer (but I do believe that it would use more memory).

You speak about portability but I do doubt that you follow the C standard and don't break the strict aliasing rule. This is the biggest problem of C code. The C developers think that they know the C language, but they write extremely fragile code that will break under extremely weird circumstances because it is silently depending on things like current stack size, currently optimization implementation in the compiler they are using, etc…

I never had a serious memory problem in C++. But then I write code so tight that if you don't read the documentation, you end up with static and dynamic assertions in 9 of 10 tries. This is a problem in C because you can't just add this type of checking to your code. Yes such checking does increase the build a lot, but (and I'm saying this as a full time C developer) I would rather have my code compile 10 minutes instead of 10 seconds, if I could save myself the endless hours of debugging bugs caused by platform specific code and standard violations.

#29 bfish.xaedalus.net » A tech blog recommendation on 03.25.10 at 2:10 pm

[...] On doing OO with plain old C [...]

#30 blue-slonopotam on 01.03.11 at 1:17 pm

>Face it: C++ just isn’t good enough.
Back in 1990 I used pascal instead of C for exactly the same reason. Turbo Pascal 5.5 was lightning fast compared to Turbo C 2.0, mostly because it did not have to load a bunch of include files from slow HDDs.

#31 Yossi Kreinin on 01.03.11 at 1:35 pm

Interesting, though I don't know enough Pascal to comment on the analogy. I can tell that C++ builds have been lightning slow compared to pretty much everything else for, say, the last decade so it sounds like a good bet to assume it will persist.

#32 Nikolai Kondrashov on 02.04.11 at 1:22 pm

Just FYI:

Lua 5.2 is (soon) going to have bit operations:
http://www.lua.org/work/doc/manual.html#6.7

LuaJIT is sponsored to make ARM port:
http://article.gmane.org/gmane.comp.lang.lua.general/74072

But you probably know this already.

#33 Yossi Kreinin on 02.05.11 at 2:38 am

bit32.band/bit32.bor?! Ahem.

#34 Nikolai Kondrashov on 02.06.11 at 10:35 am

> bit32.band/bit32.bor?! Ahem.
I think it's some kind of overprotective behavior :)
Still it's better than nothing.

#35 Seumas McLeod on 03.12.11 at 11:51 pm

> Virtual function call is a shortcut for obj->vtable->func(obj, args). The OO spelling - obj->func(args) - is of course better.

I guess using macros like
#define foo(obj, args) (obj->vtable->foo(obj, args))
would lessen the problem.

#36 Yossi Kreinin on 03.13.11 at 3:03 am

I don't think it would quite work without variadic macros though, because of the commas between arguments.

#37 Seumas McLeod on 03.13.11 at 10:12 am

Just replace "args" with the appropriate arguments in both sides… :)

#38 Patrick on 02.19.12 at 9:25 am

@Mgr. Šimon Tóth you are a dumb ass idiot, and blind liar, who does write things based on his wishful thinking not actual field work, we would be very pleased if you keep your misinformation to your self, C cannot reach the speed of C++ ??!!!! you are kidding aren't you, and you idiot, it is not just embedded systems you need to forget about C++ in Realtime-systems as well, you are full of bullshit because it is clear that you have not been involved in a large project to see why C++ really sucks, We got tired of people like you who speak from their back of their heads

#39 Alex on 05.18.12 at 7:19 pm

Late to the party, but there's quite a lot that you can express succinctly in C, if you're willing to break out the macros:

https://github.com/CObjectSystem/COS

Courtesy of Laurent Deniau. It was LGPL at the time this article was originally written, but more permissive nowadays.

#40 Boriel on 06.11.12 at 12:40 am

Time to develop your own language (I mean it!).
I agree 100% with you regarding C++ and think a "C+" language should be better.

When you so some C++ code and the problems it brings is when you ask "Isn't supposed high level languages will simplify things for humans?" The fact is that, with C++ you end up simplifying the work for the machine instead.

#41 Yossi Kreinin on 06.11.12 at 2:54 am

We have more than one languages of our own at work, but not general-purpose ones. Developing a general-purpose language is a very high-risk business that I'm not interested in very much. (Also I don't think I have what it takes - I don't sufficiently care about many details which are very important to get right. So I don't think I could ever top, say, C.)

#42 cmccabe on 10.06.12 at 11:17 am

The best guidelines for object oriented C that I've ever seen is the Linux kernel style guide: http://www.kernel.org/doc/Documentation/CodingStyle

Inheritance is hard to do in C, but that's ok, because inheritance is an antipattern: http://berniesumption.com/software/inheritance-is-evil-and-must-be-destroyed/

Composition or implementing abstract interfaces is the way to go.

#43 Mike Manilone on 08.14.13 at 2:39 am

Ah.. OO C, OO C. It reminds me of GObject.

Leave a Comment