9

I'm working on a large project in C, and I want to organize it using interface (.h) and implementation (.c) files, similar to many object-oriented languages such as Objective-C or Java. I am familiar with creating static libraries in C, but I think that doing so for my project is unnecessarily complex. How can I implement an interface/implementation paradigm in ANSI C? I'm primarily using GCC for compilation, but I'm aiming for strict adherence to ANSI C and cross-compiler compatibility. Thanks!

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
user2105505
  • 686
  • 1
  • 9
  • 18
  • Are you actually talking about doing OOP in C? – Oliver Charlesworth Oct 05 '14 at 17:37
  • No, I am basically looking for a way to separate function definitions from the inner workings of said functions and also a method to group related functions and such together (like math.h, for example). I don't want to use a library because I won't be reusing the functions in other programs, and I also may need to make frequent changes to the functionality. – user2105505 Oct 05 '14 at 17:58
  • Make function static in .h file, and it would be visible only in .c file; – Ivan Ivanov Oct 05 '14 at 17:59
  • Isn't that what you already get when you put your function declarations in the .h file and the definitions in the .c file? – Oliver Charlesworth Oct 05 '14 at 18:01
  • extern function is visible outside file, when you include it. static function is visible only in implementation. – Ivan Ivanov Oct 05 '14 at 18:05

3 Answers3

11

It sounds like you are already doing the right thing: good C code also organizes interfaces in .h-files and implementations in .c-files.

Example a.h file:

void f(int a);

Example a.c file:

#include "a.h"
static void helper(void) {...}
void f(int a) {... use helper()...}

Example main.c file:

#include "a.h"
int main(void) { f(123); return 0; }

You get modularity because helper-functions are not declared in headers so other modules dont know about them (you can declare them at the top of the .c file if you want). Having this modularity reduces the number of recompiles needed and reduces how much has to be recompiled. (The linking has to be done every time though). Note that if you are not declaring helper-functions in the header then you are already pretty safe, however having the static in front of them also hides them from other modules during linking so there is no conflict if multiple modules use the same helper-function-names.

If you are working with only primitive types then that is all you need to know and you can stop reading here. However if your module needs to work with a struct then it gets just a little more complicated.

Problematic example header b.h:

typedef struct Obj {
    int data;
}*Obj;
Obj make(void);
void work(Obj o);

Your module wants to pass objects in and out. The problem here is, that internals are leaked to other modules that depend on this header. If the representation is changed to float data then all using modules have to recompile. One way to fix this is to only use void*. That is how many programs do it. However that is cumbersome because every function getting the void* as argument has to cast it to Obj. Another way is to do this:

Header c.h:

typedef struct Obj*Obj;
Obj make(void);
void work(Obj);

Implementation c.c:

#include "c.h"
typedef struct Obj {
    int data;
}*Obj;

The reason why this works is, that Obj is a pointer (as opposed to a struct by value/copy). Other modules that depend on this module only need to know that a pointer is being passed in and out, not what it points to.

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66
  • Thank you for your clear answer. Can this be done with one command in GCC? Simply calling "gcc main.c" yields an undefined symbols error. – user2105505 Oct 05 '14 at 20:01
  • You should call `gcc -c filename.c` for every c-file. Each of them generates an .o-file (module). At the end you call `gcc *.o` which links them all together. If you have changed only `b.c` then only it needs to be recompiled (`b.o`) and you then link again. (the unchanged c-files dont need to be compiled again) – Bernd Elkemann Oct 05 '14 at 20:08
  • @user2105505 also, this is normally automated by a makefile. with that you only need to type "make" at the command line and it will choose which c-files to recompile and it then links the program together. – Bernd Elkemann Oct 05 '14 at 20:13
  • in C, do not use empty parentheses in function declarations (that aren't part of a definition); `Obj make();` tells the compiler that the function takes an unspecified number of arguments subject to default argument promotion; the correct prototype would read `Obj make(void);` – Christoph Oct 05 '14 at 20:14
  • @Christoph this has nothing to do with the question. Also it is a matter of taste whether to write (void), today you dont need it anymore. – Bernd Elkemann Oct 05 '14 at 20:15
  • 1
    @eznme: in C, it's *not* a matter of taste - one is a prototype, the other one isn't; eg `echo "void foo(); int main() { foo(42); }" | clang -fsyntax-only -Weverything -xc -` will parse without complaint; add the `void`, and it won't – Christoph Oct 05 '14 at 20:42
  • I did not even think about not declaring helper functions in `.h` files. I have been doing this kind of function implementation/declaration for a time, but did not think it was a bad architectural idea. Thanks for the tip! – Balázs Börcsök May 03 '22 at 11:22
1

You must read something about OOP with non OOL such like http://www.cs.rit.edu/~ats/books/ooc.pdf. But, doing such you will never have strong OOP typing.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • 1
    Also, doing so will drive you insane ;) – Oliver Charlesworth Oct 05 '14 at 17:52
  • 1
    This is a little bit exaggerated but you will need at least strong discipline (that may drive you crazy). – Jean-Baptiste Yunès Oct 05 '14 at 17:55
  • 3
    "I had fun discovering that ANSI-C is a full-scale object-oriented language" -- stopped reading there. Hilarious. Yes, one may SIMLUATE OOP-patterns with MASSIVE effort but thats pretty much senseless AND inefficient, also : that'd basically be re-inventing a wheel. There is no need for OO-patterns in C. C is pretty much a low-level language, forcing OO into it will only provoke problems - thats why Obj-C has been invented. – specializt Oct 05 '14 at 19:12
  • Thanks for destroying my nights. – gon1332 Nov 18 '16 at 00:18
1

Please do yourself a favor and read C Interfaces and Implementations: Techniques for Creating Reusable Software

Here is a repository of mine that holds some libs written in C using the pattern of interfaces & implementation described in the book.

gon1332
  • 1,930
  • 1
  • 24
  • 30