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.
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.
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;
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.
Forgot to tell wsgeek I wasn't on Linux; I cross-compile to a bare
metal target.
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.
just for you:
http://www.cs.rit.edu/~ats/books/ooc.pdf
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.
Curious, did you ever consider embedding a Lua interpreter and then
implementing your objects as Lua tables with meta-methods?
[...] OO C is passable (tags: c c++ compiler development language
performance programming) [...]
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.
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.
"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.
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.
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.
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.
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.
"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.
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.
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.
[...] bookmarks tagged passable OO C is passable saved by 5 others
MovieMan2011 bookmarked on 05/09/09 | [...]
[...] A must-read: http://www.yosefk.com/blog/oo-c-is-passable.html
[...]
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.
@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.)
@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).
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.
>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 (но немного застрял).
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.)
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.
[...] On doing OO with plain old C [...]
>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.
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.
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.
bit32.band/bit32.bor?! Ahem.
> bit32.band/bit32.bor?! Ahem.
I think it's some kind of overprotective behavior :)
Still it's better than nothing.
> 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.
I don't think it would quite work without variadic macros though,
because of the commas between arguments.
Just replace "args" with the appropriate arguments in both sides...
:)
@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
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.
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.
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.)
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.
Ah.. OO C, OO C. It reminds me of GObject.
Since it become a discussion table, I'll share my thoughts. I like
Ada programming language, but I'm not about Ada now. Trying to use Ada
forced me to think about interoperability. Eventually I stopped thinking
about C++-to-Ada solutions like yet another module for SWIG or GNAT's
recent C++-to-Ada bindings generator in favour of more general
solutions, e. g. MS COM, VirtualBox XPCOM, UNO, GObject, libobjc and
eventually discovered IBM SOMobjects.
Objective-C was also helpful in my mind evolution, I must admit. It
is difficult to spot things that are everywhere. For instance, we all
know what is English language because we are aware of non-English ones.
But if on another planet people used just one language, it would be hard
for them to spot this despite it being everywhere. They would just talk,
just write. What is the language? It's hard to think about language, and
harder to decide to name it.
Objective-C helped me spot one thing that makes C unequal to other
programming languages without taking special considerations. This thing
is the dynamic linker present in every modern OS. And it's not just
linker, it's C linker. If you compile with Ada or C++, you compile with
Ada or C++, and then link with C linker, and that's where inequality
stems from. In other words, if we want to upgrade from C to something
better, we must upgrade not only compiler, but also linker, and that's
what Objective-C does. It's not quite modifying linker, but what it does
is similar. Objective-C works together with runtime library libobjc
complementing OS dynamic linker. And if we look at Java and C#, we
discover that they are also backed by runtime. Compared to Delphi, Ada
and C++, not backed by object-oriented linker or runtime, Objective-C,
Java and C# are more natural, more direct, more equal.
I think, IBM did a big damage to the world by shutting down
SOMobjects. Both OS/2 and Copland gone in favour of Windows (.NET) and
OPENSTEP (Objective-C), and most developers don't ever know about this
page of history. IBM SOM is considered to be something OS/2-specific,
not interesting to non-OS/2 programmers. That's not. It's a breakthrough
project, just read "Release-to-Release Binary Compatibility". In 1990s
there were projects like Sun OBI and SGI Delta/C++. There was CLOS, and
SOM engineers knew about them. They managed to outperform others. And
they still remain unbeaten, and their progress not likely to be
spontaneously reinvented.
SOM aimed to be language agnostic, but there could also exist
Direct-to-SOM compilers. I think, Direct-to-SOM C++ comes most close to
what would be nice to see. It is supposed to have a syntax of C++, but
semantics of SOM. That's cloudy definition, I know, and DTS C++ never
left beta stage, there is no standard DTS C++. There are actually 3 DTS
C++: VisualAge C++ 3.6.5 (but not 4.0); MetaWare High C++ (OS/2 version
had DTS for sure, but I haven't seen Win version), and the third one is
described in the legendary "Putting Metaclasses to Work" book. They take
different decisions in how to map C++ to SOM. Being able to compile
something like wxWidgets in DTS mode is nice (I guess, IBM planned to
compile OCL into cross-platform SOM library eventually, otherwise I
don't understand why one IBM department was working hard on improving
SOM, and another IBM department produced OCL that had nothing in common
with SOM; I can't imagine Borland making new object system with TObject
and TClass, and producing VCL using Borland Pascal with Objects' object
keyword at the same time), but if compatiblity with C++ could be
dropped, we could have a powerful OO language, with normal,
non-SmallTalk syntax. And other programming languages could eventually
be bridged with SOM. If more libraries had SOM interfaces, programmers
were less stuck at using C++. If less programmers were stuck at using
C++, or if libraries in a particular language did not cause programmer
to be stuck using the same language, we could see more interesting
languages.
Check this project: http://somfree.sf.net/ This is almost complete SOM
clone, in some places more complete than IBM SOM 3.0 (somref from Apple
SOM, somem from IBM SOM 2.1), and in some places less complete (due to
bootstrapping problem C++ is being used now in SOM compiler as opposed
to SOM in the original IBM SOM Emitter Framework). Author did a good job
porting implementation to a variety of platforms.
There are not so much programmers aroung the world able to understand
what is this all about. I think, this blog entry is a good place.
Actually the power of C++ today lays in templates. I have programmed
on embedded systems and i would have really liked several things from
C++.
The constexpr to calculate a bunch of stuff, like a crc form a fixed
message, at compile time and a limited version of templates (maybe more
like java generics).
Namespaces and function overloading wouldn't be that bad ether.
All the virtual functions and other dynamic things can easily be
emulated with function pointers. So i think you would get much further
by borrowing tricks from the functional paradigm than you get from
OOP.
For example a strategy pattern and observer pattern can be replaced
by higher order functions.
How relevant is this blog now (17 Dec 2017) ?
C11 is better with _Generic. No more virtual table methods since we
could select the method using the type of data. Single Inheritance is
Easy. It's Multiple Inheritance that stays hard.
Post a comment