C++ patterns using plain C
blog.noctua-software.com
blog.noctua-software.com
Such example might be a jagged array where you allocate several chunks of memory, and a final chunk containing the pointers to each one. Freeing the pointer returned for the memory containing these pointers will not free the memory pointed to by each pointer.
The person who wrote the _new function should write the maching _free, since he's really the only one who knows exactly what and how the "object" was allocated and how it needs to be deallocated.
If you find yourself ever writing such _new function without the matching _free, you're doing it wrong.
Even worse is the last list example, as it not clear where, or if memory is even freed at all from the code given. Does one of those macros free up the memory?
A constructor taking a pointer serves a useful purpose - it's the equivalent of a copy constructor.
However, one thing you missed from the examples (quite possibly for simplification of the blog post), is that "proper" OOP design in C is done by having opaque data types in headers, with their fields only in the implementation file - the "methods" you expose in a header all take a pointer to the opaque struct as their first argument, other than the constructor, which returns one. This is how you provide the encapsulation of classes, equivalent to private fields, if you like - which should generally be the case for non-PODS.
What? Gods no!
The user knows more about their runtime environment than your objects--indeed, consider this a form of DI for memory management.
Libraries that don't let me override allocations, file access, and logging are very, very bad.
Agreed. This comes up with some frequency in cross-platform code where somebody coming from Unix (like myself) assumes that there is one `malloc` and it corresponds to one `free`. On Windows, this is not true - when a module is built as debug, the debugging allocators are used which instrument for failures like double-free. So if your debug EXE links a release DLL and tries to `free` something that the DLL has `malloc`ed, your program will quickly abort.
I've worked in a large company with a legacy codebase that attempted to do this in the 90's when C++ & inheritance was the latest-thing. It has turned out to be a real maintenance nightmare for them, it obfuscates normal code scaring off anyone who looks at the it. Lack of tooling is a major issue, try getting eclipse to find the definition of one of your virtual methods...
This is before you consider that all it gives you is inheritance. Wide use of inheritance is an anti-pattern and this gives you the mechanism without the ease-of-use that makes it maintainable.
In summary... AVOID DOING THIS AT ALL COSTS (It will cost you when you need to re-write in a sane way in a few years time).
Anyway... this has been done hundreds of times before, ever heard of CFront??
I can only emphasizes the need to move to real C++ if you want to use C++ idioms, to do it safely and performances wise: here you just opened the gate of Macro error's hell which will be more horrible than anything the C++ could throw at you.
I've considered reverting to plain C to make it easier to interface with other languages like pure ObjC and now Swift but when I think about how much extra work this is going to entail for absolutely zero benefit to my users I always give up the idea.
That's exactly why, when presented with a choice between C or C++, I'll choose C every time.
First, because it's often inexperience with other paradigms that forces programmers into an "everything must be an object" mindset. Second, because the fact that you need to learn a gargantuan amount of trivia to use C++ inheritance non-dangerously is a sign of underlying derangement. Third, because through experience I've learned that C is far less scary once you embrace standard patterns for accomplishing various tasks. The total linecount isn't significantly increased by using C over C++. The codebase complexity isn't increased.
What I like about C is that it gives you a set of inalienable commandments: thou shalt not go completely nuts with {inheritance, template metaprogramming, flavor du jour}, because you can't. In the hands of an experienced programmer, C gives you more reliability than C++ precisely because there are fewer gotchas. The dangers are straightforward: free memory after you use it; have a single place that a pointer is "owned"; check your lengths before copying between buffers.
The total linecount isn't significantly increased by using C over C++. The codebase complexity isn't increased.
This I can't agree with at all. If I had to rewrite my current codebase in C it would be substantially larger and buggier and much less flexible. I know because I've tried this experiment already. This is even more the case with the C++11 syntax enhancements, which reduce the verbosity of C++ a lot.
My only real complaint with C++ is that it lacks a proper ABI so I have to wrap it in C if I want to use it from another language. This wasn't a big deal when I could rely on Obj-C++ but Apple's new Swift language has thrown a bit of a wrench in this scheme.
Maybe true. I bailed from the C++ world before C++11 became pervasive. But if you use C++11 you risk leaving VS2010 users (and below) in a situation where they can't use your code at all: http://blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-...
And C++14? No idea what version of VS supports it. But using C++14 will definitely mess with people who have older compilers. And those people don't always have the luxury of just buying the latest version of VS.
Who cares about Windows? Well, anyone who's into the open source movement should be sympathetic to people who are forced to use a certain operating system but still want to get into programming. The spark for people's desire to become programmers often starts by wanting to customize their environment. That leads them to shell scripting, which is a stepping off point into more advanced programming.
The main point is simply that the C++ ecosystem has so many "gotchas" and timesinks that when forced to choose between C++ and C, an experienced programmer will save time with C, mainly because their years-old patterns will work regardless of what environment they're in. There are still some gotchas, of course; you can't use unix signals and expect them to be cross-platform. But it's far more straightforward than trying to get your codebase compiling on five different compilers.
Of course, choosing Python or some higher level language is almost always the best cost/benefit tradeoff.
Sure, use the highest level language you can afford to use. But I can't agree that experienced programmers will in general choose C or be more productive in it. The counter-examples are almost too numerous to even know where to begin. Surely the people behind Photoshop, Chrome, Renderman and Cubase etc. are aware of C and made a conscious decision not to use it.
When you're a single programmer, you shouldn't look at what large groups of people are doing. Large groups often have no idea what they're doing, too, so that mindset is unproductive in general. Many of the reasons are political, not technical: suggesting that everyone be forced to program in Python to the exclusion of C++ denies your company the expertise of all C++ programmers. For some industries, like Renderman's or Photoshop's, that would be suicide.
There are many reasons Tarsnap is written in C and not C++. Colin was aware of C++, yet chose not to use it. If one of the smartest and most experienced programmers choose not to do something, it's worth understanding why. Perhaps they were avoiding pitfalls that you may be in danger of falling into.
On the other hand, if Colin were forced to program in C++, I'm sure it would be wonderful C++. So the point of C vs C++ may be moot anyway. What matters is that a programmer is experienced and competent, not necessarily what tools they're forced to use.
If you're happy coding in C then by all means continue. I'm just a little tired of all the C++ critics that insist that only naive or inexperienced programmers use it and they'd all be equally productive if they'd just man up and use C for a while.
Down votes are not coming from me, by the way.
I would never say such a thing, because I don't believe it.
Who cares about downvotes? Our conversation was fun. Thank you for it.
I'm not sure where you run into all those C++ critics that prefer C. Programmers that like to write C seem pretty rare to me. The criticism flows both ways: for example the C++ FAQ (http://isocpp.org/wiki/faq) labels every feature that's in C primitive, dangerous, and obsolete. I don't see C++ programmers as naive and inexperienced at all. The biggest problem with C++ is how complex and difficult it is. It might have been considered friendly to newbies at one time, but nowadays I'd be surprised to hear that an inexperienced programmer was using C++. These days it's associated with native code and high performance (C is either lumped in or ignored).
I have yet to be convinced that such a thing exists.
If it does exist, I've never seen it.
But C++ is also much nicer to use on Windows than C.
And embedded developers! Don't forget us.
C++2003 if we're lucky.
The book I learned C++ from must have weighed 5 pounds, and about 4 of them were devoted to inheritance. I assumed that inheritance was the central point of OOP. I never quite finished that C++ book; the details became overwhelmingly tedious, and I'd already learned to write programs. When the Gang of Four book came out, and in its opening pages declared that inheritance was a bad practice, and you should prefer composition wherever possible, I kind of laughed, thinking it had finally dawned on people that this was a bad idea, and OOP would go away soon. I could not have been more wrong about that.
I still don't quite get how a person can be for OOP and profess a dislike for class inheritance; or believe software engineering metrics that consider things like call-tree depth and class counts indicators of excessive complexity and still believe objects are the right model. It seems like all the features of OOP are tied up with inheritance, and all of the recommended practices around classes and objects imply convoluted code paths and architectural complexity by their very nature. OOP seems to thrive on contradictions.
Most projects are not the kernel, and for most normal-size projects, you'll probably get better results by picking a language you are experienced with and familiar with, rather than one that's in some sense universally optimal, but you have little experience in. But to sum up the advantage of C: C is for when you don't want any surprises. It's a very un-surprising language.
Actually, judging by the quantity of unspecified/undefined/implementation-defined behaviors in the C spec, I'd say C is full of surprises.
The situation was better before 64-bit became popular, when int == long == 32 bits on any system that mattered (when 16-bit gave way to 32-bits, you couldn't be sorry about that, it was a relief). Somehow the transition to 64 bits got screwed up, and you can hardly trust the basic integer types anymore. I have started using the new stdint.h types wherever size is likely to be an issue.
It does, but that wasn't the point. I wasn't defending C++.
Part of being unsurprising is being one of the oldest and most popular programming languages. There's no excuse for being surprised by what's well known.
Buffer overflows aren't the only problem C has. For an interesting discussion and examples of bad behavior that C allows, please take a look at [1] (especially part 2 of that series) and [2] (yes, some of these are compiler shenanigans; the problem is that the C standard allows the compiler to go crazy).
1 - http://blog.llvm.org/2011/05/what-every-c-programmer-should-...
It also does a whole lot of unnecessary dynamic allocations due to the non-intrusive style used for all of its containers.
I think the STL might be overhyped.
Like anything in decades of heavy commercial use it has some warts but at least C++ gives you the control you need to write fast code at a reasonable level of abstraction.
std::vector is completely reasonable implementation for a dynamic vector.
std::map is somewhat unreasonable, because its non-intrusiveness requires dynamic allocations, more indirections, and more expensive deletions.
std::list is useless, so when you do want linked lists, you end up using the wrong data structure, because list is out of the lexicon.
For example, the Linux kernel's list.h is far superior to std::list.
Plenty of environments rule out C++ by policy imposed from above. Not all limitations are technical in nature (or even informed).
And there are plenty of technical ones as well (such as: predictability).
Please, yes. It happens I just spent the morning writing some C with files, views of those files etc, and I kept on whishing there was RAII, instead of having to build flow around the fact each of the operations might fail.
Is there a clean way to implement something like this in C?
I couldn't care less about trying to reimplement classes
indeed - the premise the OP starts with, i.e. 'here's the simplest class: BANG two subclasses' is also wrong. That is inheritance, not just 'the simplest class' as that wouldn't have anything to do with inheritcance.
__attribute__((cleanup(…)))If you do go the OOP-C route, one thing that's nice is that you can compose objects of different super types. In C++ an object belongs to one class of a hierarchy but in OOP-C it's conceivable that the vtable dispatches to a different mixt of virtual functions per object.
Also, nice trick with the #define T/#include __FILE__. Hadn't seen that before.
Personally I prefer C because of all the added flexibility it allows, but I can definitely see the value of a lot of the features added by C++ - C programmers were using structures and function pointers before, now they can use classes. RAII - a nice benefit of the semantics of constructors and destructors - is a more implicit, easier way of doing the same thing manually by calling the appropriate functions. Templates make it easy to generate a lot of otherwise very similarly structured code. Operator overloading can replace function calls and make some expressions look clearer.
However, the biggest disadvantage of all these features is that they add complexity and cognitive overhead to understanding the code - abstractions are only truly useful to those who know what the abstractions are of. That's why I believe that anyone who wants to learn C++ should already have strong knowledge of C (including structures, pointers, and memory allocation), and preferably know some Asm; then the transition to using the features of C++ feel straightforward and natural, without the "I don't know how it works, it's all magic" feeling to them. It's for this same reason that I recommend reading through the code of the STL - a good one to start with is std::vector.
I'm pretty sure that C++ exceptions could be simulated that way.
>>Templates make it easy to generate a lot of otherwise very similarly structured code
Templates are a mixed bag. There are a lot of things that look like they should be possible with templates, but once you start trying to implement them, it suddenly becomes much more difficult. Most of the practical value of templates is in getting around data type limitations and in inlining, IMO. Luckily, C has simpler data types than C++, so there is less need to climb over barriers, but templates do provide some value over C in this respect. The value with respect to inlining may be decreasing as C compilers become more sophisticated, but I'm not certain. std::sort() is said to be faster than qsort(). I've wondered if there is any way to coax a C compiler into inlining the sort routine, callback and all, but I haven't tried it yet.
No it doesn't.
And how is the compiler supposed to inline the callback in qsort() when all it has is a pointer that will be provided only at runtime ? With JIT yes that would be possible. This requires a runtime optimization solution, not a compile time. This is also the reason why templates come so handy, sans the error messages (although GCC has improved by leagues, now to the point I find GCC's error messages easier to debug STL code with than Clang's). The error message scenario would a be a lot better once they get concepts sorted out.
Also, if the compiler gets the qsort prototype by including it from the system header <stdlib.h>, I think the compiler may assume that it is the standard qsort function. So, it could emit a custom qsort function that inlines the comparison function.
The comparison function usually will be. The qsort function generally won't be available.
>Also, if the compiler gets the qsort prototype by including it from the system header <stdlib.h>, I think the compiler may assume that it is the standard qsort function. So, it could emit a custom qsort function that inlines the comparison function.
It could do something like that, but gcc doesn't (https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Other-Builtins....).
If you look at the kinds of API functions implemented as builtins, they are generally simple.
Indeed they can, may I introduce you to the preprocessor/macro extravaganza that is libextc: https://github.com/jspahrsummers/libextc/blob/master/src/exc...
I used it for a bit mostly to learn the chicanery of the madness. But I'm not a huge exception fan in general it just doesn't fit C well in my opinion.
I gave it a quick try but honestly its simpler to just use straightforward C.
Short answer, harder than you'd initially think. So many edge cases, the horror.
Nothing to see there, move on.