David Mearsen said:
Hi,
I've recently started programming C after many years using "the other
language"... I just wanted to find out the common practice for
organising source files.
there are many "other languages"...
a simple hueristic ranking would look something like this:
1. C++
2. Java
3. VB
4. C#
5. Python
6. Perl
....
but, one has no idea of knowing which.
also note that it is a common misconception (especially among moderate-use
languages) for the users to somehow think that their language rules the
world (I have seen this attitude especially bad in VB coders).
for high-use languages, such a C, C++, and Java, this view is acceptable,
but after this the drop is sharp (albeit, each needs to acknowlege the
others, and not simply assume that "everybody" uses their language).
for low-use languages (esp in the Scheme, Common Lisp, OCaml, ...
communities), since the illusion of world dominance is unmaintainable, the
more common attitude becomes that of superiority and elitism (because I use
this lang, I am so superior to the ignorant masses who know nothing about
this language, ...).
so, one needs be careful with assumptions.
Specifically, consider a moderately complicated library module, mylib.c.
Obviously its "public interface" (i.e. non-static function
declarations, typedefs, any global variables) need to go in mylib.h.
also, be careful with terms like "obviously" as well.
if one is wrong, then it can make them look arrogant and/or stupid...
now, not all non-static functions are part of the "public interface" either
(this is especially true once the complexity of a library moves much past
"trivial").
as for global variables:
I will personally somewhat recommend against basing any public API on global
variables;
IMO, it is a much better idea to make use of getter and setter functions.
this is especially true if one may need to, for example, attach logic to
some particular variables, or reorganize the libraries' internals.
in a few cases, I have seen examples of where the lib had originally used a
global as part of its API, but later replaced it with some ugly macro
wrapping a function call:
#define foo (*(int *)(mylib_getFooPtr()))
though, maybe acceptable, it is not very nice either...
in fact, this is a common practice in implementing 'errno' as well.
The question is: what about private (i.e. static) functions and struct
declarations and typedefs only used in the private implementation?
Is it more usual to put these in the mylib.h file, or to put them at the
top of the mylib.c file, or to create a separate mylib_private.h file?
this is a personal preference, but in my case, usually any structs or
typedefs go into the headers.
whether I maintain seperate public and private headers (or, use the same
headers and an ifdef to handle private contents), depends on the specifics
of what I am writing.
example:
#include <mylib.h> //includes header, and gets public declarations
but:
#define MYLIB_INTERNAL
#include <mylib.h> //includes header, and gets private declarations as
well
in cases where I distinguish them, personally I usually use an '_i' suffix.
And a similar question for #includes: let's suppose that one of the
public functions declared in mylib.h takes a FILE* parameter.
Obviously, I'll need to #include<stdio.h> at the top of mylib.h to get
the FILE structure defined.
be careful with 'obviously'...
it may well be the case that one can require any such headers to be included
prior to the API header...
But say in the implementation, in mylib.c, I need to use (for example)
malloc. Then I need to #include<stdlib.h> as well. Should I put the
#include at the top of mylib.h or at the top of mylib.c?
arguments can be made in both cases, but, generally:
if the code needed for the library (internally) is unneeded by the client of
the app, it may not be a good idea to include the header from within the
header (will slow compilation time, ... for no real gain).
however, it may also provide a means to centrally control allowable
dependencies (for example, when the compiler is set to treat missing
prototypes as an error condition).
in this way (disallowing including other headers from within source files),
we can be certain if and where there is any accidental deviation from our
allowed list of dependencies (IMO, as a project scales much, controlling
allowed interdependencies can become an important factor, and it is much
easier to create dependencies, than to eliminate them).
as such, it may make sense to include system headers, for the internal
headers/sections.
#ifdef MYLIB_INTERNAL
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
....
#endif