0

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.

  • What's better: `log2(x)` or `ln(x)`? Doesn't make much sense as both are similar, but do different things, yet with some trickery one can be used instead of the other. – ForceBru Jan 10 '18 at 13:24
  • @underscore_d - The link provided is not a duplicate. The discussion there, is very specifically delineating the difference between `#include <...>` and `#include "..."` notation. This question is asking for advice on best practices of when to use them. Agreed the topics overlap, but disagree that one completely eclipses the other. – ryyker Jan 10 '18 at 13:41
  • @ryyker To me, the other thread explains what the typical implementation-defined differences between the two are, and "best practices" flow naturally from knowing those. – underscore_d Jan 10 '18 at 13:51
  • Using header blocks, together with using headers located in the calling files directory, or header files located in the _default search_ location are all tools you can choose to use in order to accomplish your design. For ideas on best practices in embedded programming, there are some good discussions _[HERE](https://embeddedgurus.com/barr-code/2010/11/what-belongs-in-a-c-h-header-file/)_ and _[HERE](https://www.embedded.com/electronics-blogs/barr-code/4215934/What-belongs-in-a-header-file)_. – ryyker Jan 10 '18 at 13:58
  • Sorry for hammering this "closed" as I see this question being somewhat different in approach; but the two answers already posted were of so poor quality compared to what is presented in the duplicate that I saw not much benefit in waiting (for better answers or the existing ones being improved). The wording of the standard is elaborated in the duplicate, and "best practice" might change with project, environment, coworkers... not enough to let it stand on its own. – DevSolar Jan 10 '18 at 15:30
  • Note the answers by piCookie and aib in the duplicate (#2 and #3 at the time of this comment) in addition to the top one by quest49 (which focuses on how current implementations do it). – DevSolar Jan 10 '18 at 15:35
  • I have expanded my example by adding an unused module called 'foo' as well as a universal #include . I accept that the question should be closed, but just needed confirmation that this was the way that other good (embedded) C libraries do it. – Pieter Conradie Jan 11 '18 at 07:41
  • I've added the Boost library as a reference example. – Pieter Conradie Jan 11 '18 at 09:59
  • @PieterConradie: I have come across many customs over the years. 1) Use `<>` only for the standard library, and `""` for everything else. 2) Use `<>` for anything external (i.e. stuff that you take from the system) and `""` for anything from your own project. 3) Use `<>` for "public" headers (i.e., system, and headers your project provides to clients) and `""` for "private" headers (i.e. those only used by your project internally). As virtually no compiler actually exploits the "permission" to have `<>` *not* being actual files, the practical differences are minimal. – DevSolar Jan 11 '18 at 14:52
  • Personally I use 1). And reopened your question, as it is much more clearly a non-dupe now. – DevSolar Jan 11 '18 at 14:54
  • @DevSolar. Thanks for reopening my question. I have changed the title and revised the content to better reflect what I am actually asking. I hope that it's acceptable. – Pieter Conradie Jan 12 '18 at 11:56

2 Answers2

0

Using angle brackets, #include <mylib.h> requires the path to find mylib.h to be known to the IDE (compiler).

Using quotes does not require this.

What to use may depend on how you organize your files. If they are pretty local and relative to your main program, it is better to use quotes, which makes your development directory tree easier transportable.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • It requires that file to be locatable by the compiler in the given search directories *in practice*. By word of the language, all that happens in either case is that files are _"searched for in an implementation-defined manner"_. – underscore_d Jan 10 '18 at 13:49
0

It's nothing too complicated.
When you use #include <someFile.h> GCC will search through it's include paths (default one like the system headers paths (for ex - /usr/include/), or specified explicitly using the -I<path> option on the command line.
On the other hand, when you use #include "someFile.h", GCC will search for said file relative to the current file. So, if the current file is include/test.h and it has the line #include "test2.h" in it, then GCC will check for the existence of the file include/test2.h and include it.

So, in your case, since you want to distribute a library, use the "" style for referencing header files that belong to your library. If you use some other library, then it's best to have <>.

Shahe Ansar
  • 314
  • 2
  • 12
  • That's the behaviour of GCC. Other compilers might differ, as behaviour is *implementation-defined*. – DevSolar Jan 10 '18 at 15:32
  • 1
    @DevSolar True, but in general, this is the convention I've seen being followed by most compilers, and since the guy seems to be using GCC, i don't see any problems – Shahe Ansar Jan 10 '18 at 16:08