"so #include <time.h> must appear in “KDE_NTP.h”"
Are you saying that nested .h files is the right way?
"Single underscore is library-internal, double underscore is compiler-internal, I believe"
[...]
That is how it appears, which is why I resisted doing a #include with a .h file which starts with one of these. Wasn't sure why though.
"A number of variables were shared between modules, and I had to declare them extern to get correct function without errors."
Yes; what I found works is e.g.
uint32_t g_fred=0;
in one file (perhaps in main.c or whatever module starts running first) and then in each of the other files you do
extern uint32 g_fred;
Cube is Eclipse based so lots of people should know it.
Some random thoughts and references:
Nested includes:Yes, that is the recommended way to do it. Each include file should be self consistent and include all the headers it needs.
The .c file should also include all the header it needs, and not rely on a header to implicitly include something it needs: as Siwastaja said, compilers are efficient enough (some cache the headers and
optimize the standard #ifdef guard, so one deos not need to use the non-standard, but very widely supported, #pragma once)
The recommendation can be found in e.g.
Google styleguide for C++ (no difference with C in case), see also the following rules for declarations (more on that below).
Identifiers (and other) with leading underscore(s):The C standard, in chapter 7.1.3 (C2011) makes a subtle difference between a leading underscore followed by an uppercase letter or another underscore, and a leading underscore followed by anything. In the first case the identifier is reserved for any use (e.g. also extra keyword, like __always_inline), in the second it's reserved for use as a file scope identifier and also for tags.
The point is so fine that the rule here is simply DO NOT USE a leading underscore and you'll be fine.
extern declarations and definitionsThe rules are outlined in chapter "6.9 External Definitions".
They boil down to what you observed: for any object that is actually used there must be a single definition (i.e. a declaration that reserves the object space) and there can be many external declaration (simplifying, just letting the compiler know the type of the identifier).
uint32_t fred = 0; is a definition as it includes an initializer.
uint32_t fred; would have worked the same, in that case it's called a tentative definition, and there can be more than one as long as they agree: if no definition is found the compiler will take it.
Note that this applies regardless if the
linkage (=visibility of the identifier) is external or internal (e.g. the
definition uses "static", though a declaration in the same file uses "extern"), see the examples at the end of chapter 6.9.3.
CubeAll of the above has no relationship whatsoever with Cube.
The compiler enforces the standard (possibly with some extension), the header files (real or "virtual" - they need not exist physically, though they
mostly do) are provided part by the compiler and part by the used standard library.
The source parsing function, as many said above, can throw you off - that happens quite rarely to me with either internal or clang based Visual Studio Intellisense, I don't know what Eclipse 🤮uses.