I want to create and share a new C user library (using best practices). It is a collection of C modules that a user program can compile and link with. Each module has a default configuration, but the user program must be able to override the default configuration (only if it wants to). Also, the user program must not be forced to provide configurations for C modules that it does not use.
I want to group the C module files together in the same library directory (e.g. module.h, module_cfg.h & module.c) to minimize the learning curve for a user of my library.
Here is my solution to the problem, but I was wondering if this is bad form and if there is a better solution.
Let's call the new C library FancyLib and use the prefix "fl" to prevent namespace clashes with other libraries or user code. Inside the library is a timer
module. It has a default configuration file, but is overridden by a user supplied configuration file. Also inside the library is a module called foo
that is not used by the user program.
The whole user project, including the library, consists of the following files:
- libs/fl/fl_lib.h
- libs/fl/utils/fl_tmr.h
- libs/fl/utils/fl_tmr_cfg.h <-- DEFAULT
- libs/fl/utils/fl_tmr.c
- libs/fl/utils/fl_foo.h
- libs/fl/utils/fl_foo_cfg.h <-- DEFAULT
- libs/fl/utils/fl_foo.c
- cfg/fl_tmr_cfg.h <-- OVERRIDE
- src/main.c
The content of src/main.c is:
#include <fl_lib.h>
int main(int argc, char *argv[])
{
fl_tmr_init();
}
The content of libs/fl/fl_lib.h is:
#ifndef __FL_LIB_H__
#define __FL_LIB_H__
#include <utils/fl_tmr.h>
#include <utils/fl_foo.h>
#endif
The content of libs/fl/utils/fl_tmr.h is:
#ifndef __FL_TMR_H__
#define __FL_TMR_H__
// Include configuration (default or user supplied)
#include <fl_tmr_cfg.h>
void fl_tmr_init(void);
#endif
The content of libs/fl/utils/fl_tmr.c is:
#include "fl_tmr.h" // <-- Need to be quotes
void fl_tmr_init(void)
{
// Initialise timer
}
The content of libs/fl/utils/fl_foo.h is:
#ifndef __FL_FOO_H__
#define __FL_FOO_H__
// Include configuration (default or user supplied)
#include <fl_foo_cfg.h>
#endif
The search path for the compiler is specified as "-Icfg -Isrc -Ilibs/fl". This ensures that the user supplied configuration file is found first (cfg/fl_tmr_cfg.h) and overrides the default file in the library (libs/fl/utils/fl_tmr_cfg.h).
Is this the best way to go about it?
This solution only works if #include <>
(with angle brackets) are used, because it "forces" the compiler to follow the specified include directory precedence and not use the file it found first in the same directory as the *.c file (e.g. when libs/fl/utils/fl_tmr.c is compiled).
The use of #include ""
(quotes) versus #include <>
(angle brackets) have been discussed in depth here, but I was still confused by this GCC documentation page that made me think that #include <>
(angle brackets) should be used for system files only, e.g. #include <stdio.h>
One reference example is the boost library that uses #include <filename>
extensively, but makes an exception. Here is an example:
boost\libs\math\src\tr1\acosh.cpp:
#include <boost/math/tr1.hpp>
#include <boost/math/special_functions/acosh.hpp> // <-- Angle brackets
#include "c_policy.hpp" // <-- Quotes
Note: c_policy.hpp
is in the same directory as acosh.cpp
One pitfall mentioned in that discussion is that dependencies are generated for #include ""
files only and not for #include <>
files. This means that if a file is changed in the library then the whole project needs to be rebuilt from scratch.
Thanks in advance,
Pieter
P.S. Any other pointers/hints/links/examples to create a good (embedded) C library would be greatly appreciated.