10 ms·
C++ patterns using plain C
- torrance 12y agoI couldn't care less about trying to reimplement classes in C — structs and functions will do just fine thank you very much. What about something actually useful, like RAII to simplify memory management?
- stinos 12y agoRAII 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.
- invernomut0 12y agoI remember something implemented as GCC extension (the cleanup variable attribute) but I never used it so I can't judge if it's really what are you searching for.
- guillaumec 12y agoGcc cleanup extension works very well for RAII, I used it extensively to write a simple profiler for a video game project (just add 'PROFILE' on top of a function to have it automatically profiled). The only problem is that this is not supported by MSVC.
- mitchty 12y agoIt is supported by clang though. Maybe you could use clang-cl to get it into msvc no?
- lelf 12y ago> Is there a clean way to implement something like this in C? __attribute__((cleanup(…)))
- thinkpad20 12y agoPerhaps you might be interested in Rust?
- rlpb 12y agoI really like talloc as a means of simplifying memory management. It's not, RAII, though.
- pjmlp 12y agoAka as "lots of hacks using non standard C, while forcing the developer to do the work of the compiler".
- jacquesm 12y agoYes, but sometimes for whatever reason C is all you have. And then tricks like these just might push down the complexity far enough to give you an edge. (Do it wrong and you'll lose a lot of hair.)
- pjmlp 12y agoIn 2014 I fail to see any scenario besides *BSD and GNU/Linux kernels, where C is available and C++ is not.
- jacquesm 12y agoThat's a limit in you, not in the scenarios. 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).
- kabouseng 12y agoEmbedded development...
- pjmlp 12y agoI always used a mix of C++ or/and pure Assembly.
- AnimalMuppet 12y agoI doubt that there are many embedded environments where you can't get C++ these days.
- itsadok 12y agoYon mean "non idiomatic", right? I don't think he used anything non standard.
- Suro 12y agoWelcome back to the 80's, where C++ used to play with macro to make templates and inheritance was only a basic compiler trick. 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.
- jacquesm 12y agoAs C preprocessor macros come these are pretty mild.
- Suro 12y agoMind this is only the simplest functions and thing can already go wild if you apply classic bad C++ coding practices, like cascade inheritance or god object. Add to that, you will (oh yes, you will, at least once) make type errors with macros, and you will fail to maintain all the class constructors and virtual functions.
- kzrdude 12y agoThe code in the article is just a plaything, a C programmer wouldn't use any of that and wouldn't use C++ as a starting point for their C implementation. Don't let it reflect negatively on C.
- scott_s 12y agoIn what way? I have seen several examples of C programs that implemented a form of interfaces using structs of function pointers. The GIMP code base does it, as does the Linux kernel's Virtual File System layer. This is a good overview: https://www.kernel.org/doc/Documentation/filesystems/vfs.txt https://www.kernel.org/doc/Documentation/filesystems/vfs.txt
- rejschaap 12y agoI could probably live with most of this, but the macros make me want to gouge my eyes out. C macros just have too many issues
- cageface 12y agoI'd miss the STL more than anything. It's so much nicer than any of the remotely comparable C data structure + algorithm libraries. That plus RAII are enough to keep me on the C++ side.
- sillysaurus3 12y agoAlternatively, the STL is so bloated and there are so many ways to mess up RAII that it's enough to keep me on the C side. Where I sell C shells.
- cageface 12y agoThe STL is actually pretty lean compared to the standard libraries that ship with most popular programming languages these days and if you leave the iostream stuff out it's fast and doesn't take up much space in the binary. I've used it with great results on iOS devices. My first step in creating any new class is subclassing a no-copy parent, which in my experience eliminates most of the nasty gotchas with accidental copies. I have yet to get any RAII weirdness. 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.
- sillysaurus3 12y agoMy first step in creating any new class is subclassing a no-copy parent 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.
- hamidr 12y agoIt's fun to know how we implement these stuffs in C but not fun enough to use them :)
- sparkie 12y agoA complaint about all of the examples is the use of free() directly in main(). I dislike this because it implies that the "create", or "new" used a single call to (m/c)alloc to allocate the returned pointer. While this is the case for the examples, it may not be the case where you are only given a prototype for "base_new" in a header file, and you don't know of its implementation detail. 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?
- stinos 12y agoLuckily we have modern C++ where such problems hardly exist :P
- guillaumec 12y agoThat's a valid point. This is the reason why some C API (like the chipmunk physic engine), offer two versions of constructor functions: one that allocates the memory, and one that takes an already allocated pointer.
- sparkie 12y agoI also think that asking the user to allocate memory is bad (for OOP) design, because it is leaking information which should be encapsulated into the class. How does the user even know how, or what to allocate in order to work with your "methods"? 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.
- oneofthose 12y agoI think this proofs Scott Meyers point about the most important feature we have in C++: the destructor.
- userbinator 12y agoI've always liked to keep in mind that C++ was developed as an extension to C ( http://en.wikipedia.org/wiki/Cfront http://en.wikipedia.org/wiki/Cfront ), so most of the constructs that it has can be directly mapped to C --- I think the only exception is... exceptions, although there's probably a way to do it using setjmp/longjmp too. 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.
- mtdewcmu 12y ago>>I think the only exception is... exceptions, although there's probably a way to do it using setjmp/longjmp too. 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.
- TickleSteve 12y agoNever do this! 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??
- zvrba 12y agoBasic template: how do you single-step through a multiple-statement macro? Advanced template: where's specialization? Also, where's RAII? Nothing to see there, move on.
- bluedino 12y agoThree other ways to do OOP in C http://www6.uniovi.es/cscene/CS1/CS1-02.html http://www6.uniovi.es/cscene/CS1/CS1-02.html
- zwieback 12y agoThis is a nice article for anyone who wants to understand typical implementation of basic C++ features but in the end I'd say it makes the case for actually using C++ instead of reimplementing it in C. 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.
- rajeevk 12y agoI have written related article http://www.avabodh.com/cxxin/cxx.html http://www.avabodh.com/cxxin/cxx.html
- ternaryoperator 12y agoThe problem with many of these OO in C workarounds is how well they work at scale. For small projects, they're fine. But in larger projects, the compromises in the design begin to show up and require additional patching. This same problem--increasingly messy code to emulate C++ features--led to the abandonment of cfront and the development of true C++ compilers.
- dottrap 12y agoThe scale argument is dogma. We already know huge, successful code bases exist in C and many employ similar techniques to this. (These are pretty mild tricks.) Additionally, the scaling argument is often used to belittle other languages like Python, Ruby, Lua, JavaScript, etc. All propaganda BS.
- ternaryoperator 12y agoThe problem with many of these OO in C workarounds is how well they work at scale. For small projects, they're fine. But in larger projects, the compromises in the design begin to show up and require additional patching. This same problem--increasingly messy code to emulate C++ features--led to the abandonment of cfront and the development of true C++ compilers.
- abimaelmartell 12y agoI hate when people try to do this, it makes the code almost unreadable, if you want to use classes, use C++.