Live data from Hacker News

Object-oriented techniques in C

dmitryfrank.com

61–70 of 74 posts

Re: Object-oriented techniques in C

#61
post #48
post #5

Earlier quoted context omitted.

Fully agree. Ended up doing a similar approach when forced to use C instead of C++ for a data structures university project. Goal achieved, but not an experience to repeat ever again.

So how would you have done it the next time if you had to stick with C?

Just plain ADTs instead of trying to mock object semantics.

Edit: Forgot to mention that so far I only used plain C instead of C++ when obliged to do so.

Re: Object-oriented techniques in C

#62

How badly does this impact optimizations? First you add an extra function pointer for any member, even if it's not virtual. Then since you're always calling via a function pointer -- do compilers notice when fps are assigned and never modified and do inlining and so on? Also the first technique doesn't feel like OO in any meaningful way. Defining a state object and passing it to functions is exactly what you'd do in…

Object-oriented programming is a paradigm that can be followed in almost any language. If you're creating a conceptual 'object' that stores state and has associated methods then you're using OOP.

Re: Object-oriented techniques in C

#63

I've found that, in the embedded world at least, the overhead this adds (in terms of flash/rom and ram) can be fatal. That said, there are some more implementations of this here if anyone is curious: https://github.com/ryanbalsdon/libr

> the overhead this adds (in terms of flash/rom and ram) can be fatal.

I know you list the resources, but how does this approach add overhead? (how does it use those resources more than a "C" approach would?) Most of the constructs in C++ only cost anything if you use them, so you only pay if you need the feature; further, the runtime costs of most C++ features are pretty much exactly what's required for that feature… so if you were writing C, and you needed the same functionality, how would you avoid paying the same cost?

Re: Object-oriented techniques in C

#64
post #34

Earlier quoted context omitted.

Again, if we use it correctly, the resulting thing is much more maintainable than the code with switch-cases instead of virtual methods, and so on. By the way, similar approach is used across the Linux Kernel: check, for example, the book "Linux Kernel Development" by Robert Love, especially the discussion on the virtual filesystem.

Every solution feels great and neat in the beginning until you wake up one day and feel sick to your stomach from the mess it has created. Most vtable like implementations in the kernel are nothing more than struct of function pointers, and this is almost exclusively.

As an example, SDL is a large C codebase that employs a very OO paradigm, and I find its APIs very well organized.

When I was (or am) coding C, a lot of the OO principles helped me write better C code, as I understood better what the higher level constructs that I was working with was. (i.e., if there's a base class among essentially hiding among my structs that should perhaps be pulled out into a separate struct; do I need a vtable, or perhaps function pointers on the struct; how is a struct initialized, and how is it destructed, etc.)

Re: Object-oriented techniques in C

#65
post #39

I don't feel like the crc32 example is a good one. Maybe the game entities example would have been better...

IIRC, MD5 (and probably the other hashs) are implemented in exactly this form in OpenSSL.

I can't really think of a more straightforward way to implement CRC32, frankly, especially assuming that maybe you don't want to force the entire buffer to be present. If you did, you could `uint32_t crc32(unsigned char * buffer, size_t len);` — but you can implement that function in terms of the interface in the article, and if you pick up a bit of inlining from an optimizer, I think it would end up being just as efficient, too. (If you don't, you'll end up with some extra pointer dereferences, but again, the streaming interface supports streaming.)

And perhaps to stress what some other commenters are missing: it's not "you should always OO in C"; it's that if you want an interface to compute, while streaming, the CRC32 of a stream — you need to store that state somewhere, and you need to act on that state somehow. That state is an object: it has a setup, and potentially a teardown, and two relevant methods: feed it data, and get the computed hash.

Re: Object-oriented techniques in C

#66
post #7

This is probably a stupid question, but here it goes: Embedded platforms tend to have size limits. Would this increase the size of the source code? Seems a bit verbose to me.

Surely it does have some overhead. And, by the way, passing arguments to function has an overhead as well, comparing to globals. But, how often would you recommend use globals? The more interesting question is: how much overhead? And the answer is: it depends on the MCU. For example, some 8-bit PICs don't have silicon support for indirect function call (i.e. by function pointer), so they work around this problem by s…

> don't have silicon support for indirect function call (i.e. by function pointer), so they work around this problem by saving function address to the stack and execution 'return' instruction.

While no doubt this is true, if you have an indirect function call in C++ OO code, how do you not have it in C? That you had it in C++ implies — I hope — that you needed it. The C code can't simply whisk that need away. (Or, if it can, so can the C++…)

Re: Object-oriented techniques in C

#67
post #10

This seems to just seems to be replicating the functionality of C++ in C. The only reason he gives for not using C++ is lack of C++ compilers for embedded CPUs. I think a better approach for the long term would be to use a C++ -> C transpiler (as much as I hate that word).

I'm saddened by the author going to this trouble. Embedded cpu selection doesn't happen in isolation - or at least, it shouldn't. The available tools are a large part of why you would pick chip A over chip B. If you don't have a C++ compiler, and you end up having to re-invent C++ in C, then maybe you should have picked the chip with decent tools. If this project really did require re-inventing C++ in C, it must be j…

Embedded world is weird. Changing hardware design is difficult and costly by itself and manufacture process is limited by real world constraints, therefore the largest "part of why you would pick chip A over chip B" is availability, both long-term and short-term: will I be able to source the chip in 5 years? how easy it would be to change delivery schedule if my manufacture schedule changes?. Oh, and ARM looks like a solution until you have to battery operate it.

Cost of the software in embedded systems is quite often low compared to hardware costs. I ballpark lower limit for part cost at $10. I ballpark software development cost at $100k/y or $50/h. Let's say it is reasonable to get software done in a man-year. At 1M units of projected sales, software cost is at 1% of hardware cost (not project cost). At such ballpark estimate software engineers' opinion is only treated as a humble request.

Of course this varies from project to project. Increasing portion of embedded systems are mostly software. Portion of projects have hard-realtime requirements. Here software engineers are benevolent dictators. But that is by no means a general case.

Re: Object-oriented techniques in C

#68
post #10

This seems to just seems to be replicating the functionality of C++ in C. The only reason he gives for not using C++ is lack of C++ compilers for embedded CPUs. I think a better approach for the long term would be to use a C++ -> C transpiler (as much as I hate that word).

I'm saddened by the author going to this trouble. Embedded cpu selection doesn't happen in isolation - or at least, it shouldn't. The available tools are a large part of why you would pick chip A over chip B. If you don't have a C++ compiler, and you end up having to re-invent C++ in C, then maybe you should have picked the chip with decent tools. If this project really did require re-inventing C++ in C, it must be j…

I work mainly on embedded systems and i would much rather work with a different CPU but stuck with the hardware we have.

The compiler I use does have an embedded c++ option but it's limited and has some issues working with the RTOS that's supplied with the compiler. Also with a total of 6K of ram available it's usually easier just to stick to writing plain C.

Re: Object-oriented techniques in C

#69
post #39

I don't feel like the crc32 example is a good one. Maybe the game entities example would have been better...

IIRC, MD5 (and probably the other hashs) are implemented in exactly this form in OpenSSL. I can't really think of a more straightforward way to implement CRC32, frankly, especially assuming that maybe you don't want to force the entire buffer to be present. If you did, you could `uint32_t crc32(unsigned char * buffer, size_t len);` — but you can implement that function in terms of the interface in the article, and if…

CRCs or MD5 or other hash sums don't require inheritance or other OO tricks or even constructors or destructors (apart from zero-initialization). The state is just a single integer. For other hashes, the state might be slightly more complicated but there's still no need for fancy OO.

If you want to build a system where you can change the hash function at runtime, you might need some kind of interface or inheritance. But if you end up needing a virtual call per byte (or per a small chunk of bytes), you're killing your performance.

And besides, there's so few hash functions you end up needing in practice that a simple switch-case would do the same with less boilerplate and probably give better performance because compiler optimizations can take place (virtual calls tend to prevent many optimization tricks).

I agree with GP... The choice of CRC was a poor example for an article about OO in C. Something related to filesystems or device drivers would be vastly more practical.

Re: Object-oriented techniques in C

#70
post #69

Earlier quoted context omitted.

IIRC, MD5 (and probably the other hashs) are implemented in exactly this form in OpenSSL. I can't really think of a more straightforward way to implement CRC32, frankly, especially assuming that maybe you don't want to force the entire buffer to be present. If you did, you could `uint32_t crc32(unsigned char * buffer, size_t len);` — but you can implement that function in terms of the interface in the article, and if…

CRCs or MD5 or other hash sums don't require inheritance or other OO tricks or even constructors or destructors (apart from zero-initialization). The state is just a single integer. For other hashes, the state might be slightly more complicated but there's still no need for fancy OO. If you want to build a system where you can change the hash function at runtime, you might need some kind of interface or inheritance.…

That's what I meant - the state is a uint32_t that you pass to the crc32_calc_next_byte function...
Post reply on HN