views:

386

answers:

5

I know some higher level languages, all web based (PHP, javascript, some python). I've finally decided to learn a lower level language, and I've decided to go with C. The problem is that all the languages I use are based heavily on OOP. Seeing as (based on the research I did) C doesn't have classes or inheritance. As a result, I ask you this: How should I organize my code in C in an organized manner like OOP, without having to switch languages or just have files with endless amounts of functions?

+3  A: 

Well, use C++ if you still want to do some OO.

If you really want to use C only, without any OO then divide your logic over multiple files, and consider every source file to be just a single object. Thus, every file would only hold a structure and functions closely related to that single structure.

Workshop Alex
Exactly -- an object is just data plus operations on that data that implement a well defined responsibility.
Steve Gilham
+8  A: 

"Files with endless amounts of functions"

What you really want is files with a well defined and limited set of functions. This is called modular programming. The idea is you group functions based on functionality into a single compilation unit and define the function prototypes in a header:

Foo.h:

int GetFoo();

Foo.c:

int GetFoo()
{
   ...
}

Its somewhat similar to how you would group a set of methods into a single class. The main difference is that there may-or-may-not be a "this" thing you are working on at any given time. That is to say you can still do essentially "object oriented" programming in C. Your object, however, becomes a parameter to your module. Here's one approach:

Bar.h:

typedef int BAR_OBJ

BAR_OBJ MakeBar();

int GetBarSize(BAR_OBJ);

void DoFooToBar(BAR_OBJ, ...)

Bar.c

   struct BarDetails
   {
      int isFree;
      int size;
      ...
      // other info about bar
   };

   static BarDetails arrayOfBars[...]; // 

   BAR_OBJ MakeBar()
   {
       // search BarDetails array, find free Bar    
   }

   int GetBarSize(BAR_OBJ obj)
   {
       return arrayOfBars[obj];
   }

   void DoFooToBar(BAR_OBJ, ...)
   {
      // lookup bar obj and do something
   }
Doug T.
A stylistic concerns about your code: "#define BAR_OBJ int" should be "typedef int BAR_OBJ" as it's clearer what your intent is (to create a new type that's equivalent to int) and there's probably some edge case where the former has unintended side effects. Also you usually don't need to use extern on function declarations, but I guess it doesn't hurt to be explicit.
Falaina
yes, you're correct. update the code.
Doug T.
`extern int GetFoo();` is bogus.
kaizer.se
why is it bogus? I admit its been a long time since I've done straight C so I'd like to improve the answer.
Doug T.
extern would be used by *the caller* to define a function missing prototype (promising that it would be there when actually linked and run). In a Library/Module's public API header, you use normal function declarations without "decoration". David T.'s answer has the right way to show which API is exported.
kaizer.se
The extern isn't bogus, but it is redundant for function declarations. That doesn't keep me from using it as well, though.
John Bode
Yes you're right. I'm thinking of data exported from a compilation unit. For function prototypes it certainly is redundant.
Doug T.
You are right; there are no ill effects. To me it stands out though, it's not needed.
kaizer.se
thanks, answer updated.
Doug T.
Also, "BAR_OBJ MakeBar();" should probably be "BAR_OBJ MakeBar(void);". In C++ there's no difference, but in C the first means you're providing no information about the parameters in the declaration and the second means that the function takes no parameters.
Michael Burr
A: 

There's GObject for OOP-ish C, but well-designed structured program shouldn't be any harder to read/maintain than equivalent OOP one.

PiotrLegnica
+10  A: 

C doesn't offer much in the way of code organization. There's functions and files.

Whenever you want to implement an interface with hidden implementation, or perhaps with private functions, create two files, like "foo.h" and "foo.c".

The interface and/or public functions are function prototypes in "foo.h", and the functions are written in "foo.c". In addition, any hidden implementation or other private functions are in "foo.c", but marked as static, such as static int foo_internal_function(int i);. Functions marked static may only be referenced in the file they exist in, not any other.

There are no formal namespaces, although you can get much the same effect with prefixing function names, such as Foo_discombobulate_developers(). There are no classes, although you can at least get some encapsulation effects with the files. There is no extensible hierarchy, just files and functions.

C doesn't have anywhere near the organization of later languages, but lots of really good programs have been written in it. Be careful, comment anything confusing (and in C there are likely to be confusing things), and keep good notes.

David Thornley
+1 for understanding the right way of public and private API. Use `static` to hide all non-exported functions. And creating your own namespace for the module (common prefix to all exported symbols) is something you *should* do.
kaizer.se
+2  A: 

Here's a pattern that pops up in C frequently that emulates OOP:

Consider a class named MyClass.

/* MyClass.h or myclass.h */

#ifndef MYCLASS_H
#define MYCLASS_H

struct myclass_s;
typedef struct myclass_s myclass_t;

myclass_t * myclass_new();
void delete_myclass(myclass_t *);

    // Method int doMyStuff(int arg1,int arg2)
int myclass_doMyStuff(myclass_t *, int arg1, int arg2);

#endif //MYCLASS_H

The header file defines the type myclass_t, but hides the actual implementation myclass_s. This somewhat ackward requirement to have two names follows from C having structs in a separate namespace, while in C++ structs are in the same namespace as all other types. The code is intended to work in both C and C++. Here is the corresponding .c file:

/* MyClass.c or myclass.c */

#include "myclass.h" // Or MyClass.h

struct myclass_s {
   int exampleField;
};

myclass_t * myclass_new()
{
   myclass_t * o=(myclass_t*)malloc(sizeof(myclass_t));
   // Initialize o here.
   return o;
}

void myclass_delete(myclass_t * o)
{
   // Do any cleanup needed on o here.
   free(o);
}

int myclass_doMyStuff(myclass_t * o,int arg1,int arg2)
{
   // ...
}

Inheritance and dynamic binding are also possible to do in C, but they are somewhat more involved. The above pattern, or any pattern that models OOP, isn't always the best way to do things in C, so try not to get hung up on the class centered way of thinking. Still, this pattern is occasionally useful. For example, libpng uses something close to this (they also do 'exceptions' using setjmp/longjmp, something which I advice against).

TrayMan