Interface/Implementation in ANSI C
Asked Answered
G

3

10

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!

Georgy answered 5/10, 2014 at 17:34 Comment(5)
Are you actually talking about doing OOP in C?Condensable
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.Georgy
Make function static in .h file, and it would be visible only in .c file;Lucid
Isn't that what you already get when you put your function declarations in the .h file and the definitions in the .c file?Condensable
extern function is visible outside file, when you include it. static function is visible only in implementation.Lucid
M
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.

Mariko answered 5/10, 2014 at 18:25 Comment(7)
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.Georgy
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)Mariko
@Georgy 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.Mariko
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);Montserrat
@Montserrat 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.Mariko
@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'tMontserrat
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!Reticulum
C
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.

Carrington answered 5/10, 2014 at 17:50 Comment(3)
Also, doing so will drive you insane ;)Condensable
This is a little bit exaggerated but you will need at least strong discipline (that may drive you crazy).Kinaesthesia
"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.Ona
T
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.

Testes answered 18/11, 2016 at 0:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.