19

"If you really want OO sugar - go use C++" -- was the immediate response I got from one of my friends when I asked this. I know two things are dead wrong here. First OO is NOT 'sugar', and second, C++ has NOT absorbed C.

We need to write a server in C (the front-end to which will be in Python), and so I am exploring better ways to manage large C programs.

Modeling a large system in terms of objects, and object interactions makes it more manageable, maintainable and extensible. But when you try to translate this model into C which does not bear objects (and everything else thereof), you're challenged with some important decisions.

Do you create a custom library to provide the OO abstractions your system needs? Things like objects, encapsulation, inheritance, polymorphism, exceptions, pub/sub (events/signals), namespaces, introspection, etc. (for example GObject or COS).

Or, you just use the basic C constructs (struct and functions) to approximate all your object classes (and other abstractions) in ad-hoc ways. (for example, some of the answers to this question on SO)

The first approach gives you a structured way to implement your entire model in C. But it also adds a layer of complexity that you have to maintain. (Remember, complexity was what we wanted to reduce by using objects in the first place).

I don't know about the second approach, and how effective it is at approximating all the abstractions you might require.

So, my simple questions is: What are the best practices in realizing an object oriented design in C. Remember I am not asking HOW to do it. This and this questions talk about it, and there's even a book on this. What I am more interested in are some realistic advice/examples that address the real issues that show up when dong this.

Note: please don't advice why C shouldn't be used in favor of C++. We've gone well past that stage.

treecoder
  • 9,475
  • 10
  • 47
  • 84
  • 3
    You can write C++ server so that it's external interface is `extern "C"` and can be used from python. You can do it manually or you can have [SWIG](http://www.swig.org/) help you with that. So desire for python frontend is no reason not to use C++. That's not so say there are no valid reasons to want to stay with C. – Jan Hudec Nov 07 '11 at 08:14
  • 1
    This question needs clarification. Currently, paragraphs 4 and 5 ask basically what approach one should take, but then you say you're "not asking HOW to do it" and instead want (a list?) of best practices. If you're not looking for HOW to do it in C, are you then asking for a list of "best practices" related to OOP in general? If so, say that, but be aware that the question will likely be closed for being [subjective](http://blog.stackoverflow.com/2010/09/good-subjective-bad-subjective/). – Caleb Nov 07 '11 at 08:28
  • :) I am asking for real examples (code or otherwise) where it's been done -- and the issues they encountered when doing it. – treecoder Nov 07 '11 at 08:30
  • 4
    Your requirements seem confusing. You insist on using object orientation for no reason I can see (in some languages, it's a way to make programs more maintainable, but not in C), and insist on using C. Object orientation is a means, not an end or a panacea. Moreover, it's greatly benefited by language support. If you actually did want O-O, you should have considered that during language selection. A question on how to make a large software system with C would make much more sense. – David Thornley Nov 07 '11 at 17:30
  • You might want to take a look at "Object-Oriented Modeling and Design." (Rumbaugh et al.): there is section on mapping OO designs to languages like C. – Giorgio Jul 23 '14 at 07:58

7 Answers7

16

I think you need to differentiate OO and C++ in this discussion.

Implementing objects is possible in C, and it's pretty easy - just create structures with function pointers. That's your "second approach", and I would go with it. Another option would be to not use function pointers in the struct, but rather pass the struct of data to the functions in direct calls, as a "context" pointer. That's better, IMHO, as it is more readable, more easily traceable, and allows adding functions without changing the struct (easy on inheritance if no data is added). That's in fact how C++ usually implements the this pointer.

Polymorphism becomes more complicated because there's no built-in inheritance and abstraction support, so you'll have to either include the parent struct in your child class, or do a lot of copy pastes, either of those options is frankly horrible, even though technically simple. Enormous amount of bugs waiting to happen.

Virtual functions can easily be achieved through function pointers pointing to different functions as required, again - very bug prone when done manually, a lot of tedious work on initializing those pointers correctly.

As to namespaces, exceptions, templates, etc - I think that if you're limited to C - you should just give up on those. I've worked on writing OO in C, wouldn't do it if I have a choice (at that place of work I literally fought my way into introducing C++, and the managers were "surprised" to how easy it was to integrate it with the rest of the C modules at the end.).

Goes without saying that if you can use C++ - use C++. There's no real reason not to.

littleadv
  • 4,704
  • 27
  • 26
  • Actually, you can inherit from a struct and add data: just declare the first item of the child struct as a variable whose type is parent struct. Then cast as you need. – mouviciel Nov 07 '11 at 09:04
  • 1
    @mouviciel - yes. I said that. "*... so you'll have to either include the parent struct in your child class, or ...*" – littleadv Nov 07 '11 at 09:37
  • 5
    No reason to try to implement inheritance. As a means to achieve code reuse, it's a flawed idea to begin with. Object composition is easier and better. – KaptajnKold Nov 07 '11 at 12:35
  • @KaptajnKold - agree. – littleadv Nov 07 '11 at 18:19
16

From my answer to How should I structure complex projects in C (not OO but about managing complexity in C):

The key is modularity. This is easier to design, implement, compile and maintain.

  • Identify modules in your app, like classes in an OO app.
  • Separate interface and implementation for each module, put in interface only what is needed by other modules. Remember that there is no namespace in C, so you have to make everything in your interfaces unique (e.g., with a prefix).
  • Hide global variables in implementation and use accessor functions for read/write.
  • Don't think in terms of inheritance, but in terms of composition. As a general rule, don't try to mimic C++ in C, this would be very difficult to read and maintain.

From my answer to What are the typical naming conventions for OO C public and private functions (I think this is a best practice):

The convention I use is:

  • Public function (in header file):

    struct Classname;
    Classname_functionname(struct Classname * me, other args...);
    
  • Private function (static in implementation file)

    static functionname(struct Classname * me, other args...)
    

Moreover, many UML tools are able to generate C code from UML diagrams. An open source one is Topcased.

mouviciel
  • 15,473
  • 1
  • 37
  • 64
  • +1 for link [How should I structure complex projects in C](http://stackoverflow.com/q/661307/45249) – treecoder Nov 07 '11 at 11:55
  • 1
    Great answer. Aim for modularity. OO is supposed to provide it, but 1) in practice it's all too common to end up with OO spaghetti and 2) it's not the only way. For some examples in real life, look at the linux kernel (C-style modularity) and projects which use glib (C-style OO). I've had the occasion to work with both styles, and IMO C-style modularity wins. – Joh Nov 07 '11 at 12:04
  • And why exactly composition is a better approach than inheritance? Rationale and supporting references are welcome. Or you were referring to C programs only? – Aleksandr Blekh Oct 24 '14 at 21:05
  • 1
    @AleksandrBlekh - Yes I am referring to C only. – mouviciel Oct 24 '14 at 21:52
8

Here are the basics of how to create object orientation in C

1. Creating Objects and Encapsulation

Usually - one creates an object like

object_instance = create_object_typex(parameter);

Methods can defined in one of the two ways here.

object_type_method_function(object_instance,parameter1)
OR
object_instance->method_function(object_instance_private_data,parameter1)

Note that in most cases, object_instance (or object_instance_private_data) is returned is of type void *. The application cannot be reference individual members or functions of this.

Further to this each method uses these object_instance for subsequent method.

2. Polymorphism

We can use many functions and function pointers to override certain functionality in run time.

for example - all object_methods are defined as a function pointer which can be extended to public as well as private methods.

We can also apply function overloading in a limited sense, by way of using var_args which is very similar to how variable number of arguments defined in printf. yes, this is not pretty as flexible in C++ - but this is closest way.

3. Defining inheritance

Defining inheritance is a bit tricky but one can do the following with structures.

typedef struct { 
     int age,
     int sex,
} person; 

typedef struct { 
     person p,
     enum specialty s;
} doctor;

typedef struct { 
     person p,
     enum subject s;
} engineer;

// use it like
engineer e1 = create_engineer(); 
get_person_age( (person *)e1); 

here the doctor and engineer is derived from person and it is possible to typecast it to higher level, say person.

Best example of this is used in GObject and derived objects from it.

4. Creating virtual classes I am quoting a real life example by a library called libjpeg used by all browsers for jpeg decoding. It creates a virtual class called error_manager which the application can create concrete instance and supply back -

struct djpeg_dest_struct {
  /* start_output is called after jpeg_start_decompress finishes.
   * The color map will be ready at this time, if one is needed.
   */
  JMETHOD(void, start_output, (j_decompress_ptr cinfo,
                               djpeg_dest_ptr dinfo));
  /* Emit the specified number of pixel rows from the buffer. */
  JMETHOD(void, put_pixel_rows, (j_decompress_ptr cinfo,
                                 djpeg_dest_ptr dinfo,
                                 JDIMENSION rows_supplied));
  /* Finish up at the end of the image. */
  JMETHOD(void, finish_output, (j_decompress_ptr cinfo,
                                djpeg_dest_ptr dinfo));

  /* Target file spec; filled in by djpeg.c after object is created. */
  FILE * output_file;

  /* Output pixel-row buffer.  Created by module init or start_output.
   * Width is cinfo->output_width * cinfo->output_components;
   * height is buffer_height.
   */
  JSAMPARRAY buffer;
  JDIMENSION buffer_height;
};

Note here that JMETHOD expands in a function pointer through a macro which needs to be loaded with right methods respectively.


I have tried to say so many things without too much individual explanations. But i hope people can try their own things. However, my intention is just to show how things map.

Also, there will be many arguments that this won't be exactly true property of the C++ equivalent. I know that OO in C -wont be that strict to its definition. But working like that one would understand some of the core principles.

Important thing is not that OO is as strict as in C++ and JAVA. It is that one can structurally organize the code with OO thinking in mind and operate it that way.

I would strongly recommend people to see the real design of libjpeg and following resources

a. Object oriented programming in C
b. this is a good place where people exchange ideas
c. and here is the full book

gnat
  • 21,442
  • 29
  • 112
  • 288
Dipan Mehta
  • 10,542
  • 2
  • 33
  • 67
3

Object-orientation boils down to three things:

1) Modular program design with autonomous classes.

2) Protection of data with private encapsulation.

3) Inheritance/polymorphism and various other helpful syntax like constructors/destructors, templates etc.

1 is most important by far, and it is also completely language-independent, it is all about program design. In C you do this by creating autonomous "code modules" consisting of one .h file and one .c file. Regard this as the equivalent of an OO class. You can decide what should be placed inside this module through common sense, UML or whatever method of OO design you are using for C++ programs.

2 is also quite important, not only to protect againt intentional access to private data, but also to protect against unintentional access, ie "namespace clutter". C++ does this in more elegant ways than C, but it can still be achieved in C by using the static keyword. All variables you would have declared as private in a C++ class, should be declared as static in C and placed at file scope. They are only accessible from within their own code module (class). You can write "setters/getters" just as you would in C++.

3 is helpful but not necessary. You can write OO programs without inheritance or without constructors/destructors. These things are nice to have, they can certainly make the programs more elegant and perhaps also safer (or the opposite if used carelessly). But they are not necessary. Since C supports neither of these helpful features, you'll simply have to do without them. Constructors can be replaced with init/destruct functions.

Inheritance can be done through various struct tricks, but I would advice against it, since it will likely just make your program more complex without any gain (inheritance in general should be carefully applied, not only in C but in any language).

Finally, every OO trick can be done in C. Axel-Tobias Schreiner's book "Object-oriented programming with ANSI C" from the early 90s proves this. However, I would not recommend that book to anyone: it adds an unpleasant, strange complexity to your C programs that is just not worth the fuss. (The book is available for free here for those still interested despite my warning.)

So my advise is to implement 1) and 2) above and skip the rest. It is a way of writing C programs that has proven successful for well over 20 years.

2

Borrowing some experience from the various Objective-C runtimes, writing a dynamic, polymorphic OO capability in C is not too hard (making it fast and easy to use, on the other hand, appears to still be ongoing after 25 years). However, if you implement an Objective-C style object capability without extending the language syntax then the code you end up with is quite messy:

  • every class is defined by a structure declaring its superclass, the interfaces it conforms to, the messages it implements (as a map of "selector", the message name, to "implementation", the function that provides the behaviour) and the class's instance variable layout.
  • every instance is defined by a structure which contains a pointer to its class, then its instance variables.
  • message sending is implemented (give or take some special cases) using a function that looks like objc_msgSend(object, selector, …). By knowing what class the object is an instance of, it can find the implementation matching the selector and thus execute the correct function.

That's all part of a general-purpose OO library designed to allow multiple developers to use and extend each other's classes, so could be overkill for your own project. I have often designed C projects as "static" class-oriented projects using structures and functions: - each class is the definition of a C structure specifying the ivar layout - each instance is just an instance of the corresponding structure - objects can't be "messaged", but method-like functions looking like MyClass_doSomething(struct MyClass *object, …) are defined. This makes things clearer in the code than the ObjC approach but has less flexibility.

Where to trade-off lies depends on your own project: it sounds though like other programmers won't be using your C interfaces so the choice comes down to internal preferences. Of course, if you decide you want something like the objc runtime library, then there are cross-platform objc runtime libraries that will service.

1

First off, unless this is homework or there is no C++ compiler for the target device I don't think you do have to use C, you can provide a C interface with C linkage from C++ easily enough.

Secondly, I'd look at how much you will rely on polymorphism and exceptions and the other features the frameworks can provide, if it's not much simple structs with related functions will be much easier than a fully featured framework, if a significant portion of your design needs them then bite the bullet and use a framework so you don't have to implement the features yourself.

If you don't really have a design yet to make the decision from then do a spike and see what the code tells you.

lastly it doesn't have to be a one or other choice (though if you go framework from the start you porbably may as well stick with it), it should be possible to start with simple structs for the simple parts and only add in libraries as needed.

EDIT: So its a decision by management right lets ignore the first point then.

jk.
  • 10,216
  • 1
  • 33
  • 43
1

GObject does not really hide any complexity and introduces some complexity of it's own. I would say that the ad-hoc thing is easier than GObject unless you need the advanced GObject stuff like signals or the interface machinery.

The thing is slightly different with COS since that comes with a preprocessor that extends the C syntax with some OO constructs. There is a similar preprocessor for GObject, the G Object Builder.

You could also try the Vala programming language, which is full high level language that compiles to C and in a way that allows using Vala libraries from plain C code. It can use either GObject, it's own object framework or the ad-hoc way (with limited features).

Jan Hudec
  • 18,250
  • 1
  • 39
  • 62