nesting of .h files and .h files which start with an underscore - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: October 12, 2021, 09:50:51 am
content: I am having problems compiling some code I got from someone else.It isn't recognising time_t. In Cube, a right-click on that shows it declared in _timeval.h but that is not being #included anywhere. Presumably it is defined "somewhere" and probably as a .h inside another .h and possibly inside another .h. I never do that but a lot of ST libs do that. Doing esoteric stuff with .h files seems to be a "unix coder" way of separating men from sheep and I am sure books have been written on itIs there a way to work out the dependency path which the compiler followed?I have another one with uint64_t which is in _stdint.h which I am also not #including. But that one is working... and I can't find how it is finding uint64_t as defined.

author: madires
author_rank: Super Contributor
timestamp: October 12, 2021, 10:50:32 am
content: #include <time.h>?

author: peter-h
author_rank: Super Contributor
timestamp: October 12, 2021, 11:15:45 am
content: Sure, it should be there but it isn't.A right-click on time_t and "open declaration" points to _timeval.h which containstypedef _TIME_T_ time_t;and doing that on _TIME_T_ takes one to _types.h and in there is#define _TIME_T_ __int_least64_twhich takes one to _default_types.h and in there istypedef __INT_LEAST64_TYPE__ __int_least64_t;which is a macro that expands tolong long intBut for some reason, while the IDE is apparently able to track up this ludicrous dependency chain (well, at least it does fish out a .h file where this is defined) the compiler can't.There also appears to be some convention that .h files starting with an underscore have some special usage or meaning.I am writing for a 32F4 ARM which is natively 32 bit so I am using uint8_t...uint64_t, rather than int, long, long long etc.

author: golden_labels
author_rank: Super Contributor
timestamp: October 12, 2021, 12:19:23 pm
content: It doesn’t have to be there (assuming that by “there” you mean thetime.hheader). It has to be defined in the current compilation unit after the preprocessor expandstime.h. That’s the only requirement. It may be in some other header included bytime.h, it may even be magically provided by the compiler without ever being explicitly defined.(1)If you have explicit#include <time.h>in your source code and the compiler complains it still doesn’t know whattime_tis, it’s a bug in the compiler or the standard library it uses. I don’t have that particular toolset, so I can’t test to see what error you are receiving. But it is not up to you, as a programmer, to fix it: the compiler vendor should correct the issue. You may provide a temporary workaround in your code, if needed, but be aware this should be viewed only that way: as a temporary workaround. Tell us the exact error message you are receiving while trying to usetime_t. For suggestions of a workaround, tell how thattime_tis going to be used.The IDE is not performing actual compilation. It may or may not execute a complete preprocessing stage (again, I can’t determine that), but its outcomes may be different from the actual compilation result due to different preprocessor defines. In any case, it’s performing some kind of guesswork and, very often in IDEs, that is finding either all matching definitions or just any of them. Not necessarily those, which would actually be used. Consider your IDE’s “find definition” feature to be glorified grep.(all the above is also true foruint64_t, assuming that this type is available in that compiler(2))____(1)Which I haven’t seen fortime_t, but some other built-in things happen to be provided that way.(2)Presence of anyuintN_tandintN_ttype is always optional in C.

author: Jeroen3
author_rank: Super Contributor
timestamp: October 12, 2021, 12:31:05 pm
content: Yeah, it may very well be that <time.h> was includedabove"module.h" while the module depends on time objects.When you reverse the include order you get an error, while in fact module.h should also have contained an include for time.h to be independent.

author: magic
author_rank: Super Contributor
timestamp: October 12, 2021, 01:11:03 pm
content: Quote from: peter-h on October 12, 2021, 09:50:51 amStart with finding if the error is in the C file or some H file included by the C file (see above).Make sure the file which errors out includes <time.h>.Find where the includes come from.Open <time.h> and see what's inside and why it doesn't define time_t, inculde _timeval.h or whatever other thing it should do.

author: peter-h
author_rank: Super Contributor
timestamp: October 12, 2021, 01:19:39 pm
content: This is the function (this one is found in many places online)Code:time_t mkgmtime(struct tm *ptm){time_t secs=0;// tm_year is years since 1900int year=ptm->tm_year + 1900;for (int y=1970; y < year; ++y){secs+=(IsLeapYear(y) ? 366 : 365) * SecondsPerDay;}// tm_mon is month from 0..11for (int m=0; m < ptm->tm_mon; ++m){secs+=NtpDaysPerMonth * SecondsPerDay;if (m == 1 && IsLeapYear(year))secs+=SecondsPerDay;}secs+=(ptm->tm_mday - 1) * SecondsPerDay;secs+=ptm->tm_hour * SecondsPerHour;secs+=ptm->tm_min * SecondsPerMinute;secs+=ptm->tm_sec;return secs;}It is pretty obvious the intention is that time_t is a uint64_t, but it appears to be ultimately defined as a long long int which is a int64_t on this device. Of course an int64_t is not the same as a uint64_t but it probably does not matter given the vast span of 64 bits.The .h file contains thisCode:time_t mkgmtime(struct tm *ptm);time.h is includedabovethis .h file and contains thisCode:/** time.h** Struct and function declarations for dealing with time.*/#ifndef _TIME_H_#define _TIME_H_#include "_ansi.h"#include <sys/cdefs.h>#include <sys/reent.h>#define __need_size_t#define __need_NULL#include <stddef.h>/* Get _CLOCKS_PER_SEC_ */#include <machine/time.h>#ifndef _CLOCKS_PER_SEC_#define _CLOCKS_PER_SEC_ 1000#endif#define CLOCKS_PER_SEC _CLOCKS_PER_SEC_#define CLK_TCK CLOCKS_PER_SEC#include <sys/types.h>#include <sys/timespec.h>#if __POSIX_VISIBLE >= 200809#include <sys/_locale.h>#endif_BEGIN_STD_Cstruct tm{inttm_sec;inttm_min;inttm_hour;inttm_mday;inttm_mon;inttm_year;inttm_wday;inttm_yday;inttm_isdst;#ifdef __TM_GMTOFFlong__TM_GMTOFF;#endif#ifdef __TM_ZONEconst char *__TM_ZONE;#endif};clock_tclock (void);doubledifftime (time_t _time2, time_t _time1);time_tmktime (struct tm *_timeptr);time_ttime (time_t *_timer);#ifndef _REENT_ONLYchar*asctime (const struct tm *_tblock);char*ctime (const time_t *_time);struct tm *gmtime (const time_t *_timer);struct tm *localtime (const time_t *_timer);#endifsize_tstrftime (char *__restrict _s,size_t _maxsize, const char *__restrict _fmt,const struct tm *__restrict _t);#if __POSIX_VISIBLE >= 200809extern size_t strftime_l (char *__restrict _s, size_t _maxsize,const char *__restrict _fmt,const struct tm *__restrict _t, locale_t _l);#endifchar*asctime_r(const struct tm *__restrict,char *__restrict);char*ctime_r(const time_t *, char *);struct tm *gmtime_r(const time_t *__restrict,struct tm *__restrict);struct tm *localtime_r(const time_t *__restrict,struct tm *__restrict);_END_STD_C#ifdef __cplusplusextern "C" {#endif#if __XSI_VISIBLEchar *strptime (const char *__restrict,const char *__restrict,struct tm *__restrict);#endif#if __GNU_VISIBLEchar *strptime_l (const char *__restrict, const char *__restrict,struct tm *__restrict, locale_t);#endif#if __POSIX_VISIBLEvoid tzset(void);#endifvoid _tzset_r(struct _reent *);typedef struct __tzrule_struct{char ch;int month; /* Month of year if ch=M */int week; /* Week of month if ch=M */int day; /* Day of week if ch=M, day of year if ch=J or ch=D */int secs; /* Time of day in seconds */time_t change;long offset; /* Match type of _timezone. */} __tzrule_type;typedef struct __tzinfo_struct{int __tznorth;int __tzyear;__tzrule_type __tzrule;} __tzinfo_type;__tzinfo_type *__gettzinfo (void);/* getdate functions */#ifdef HAVE_GETDATE#if __XSI_VISIBLE >= 4#ifndef _REENT_ONLY#define getdate_err (*__getdate_err())int *__getdate_err (void);struct tm *getdate (const char *);/* getdate_err is set to one of the following values to indicate the error.1 the DATEMSK environment variable is null or undefined,2 the template file cannot be opened for reading,3 failed to get file status information,4 the template file is not a regular file,5 an error is encountered while reading the template file,6 memory allication failed (not enough memory available),7 there is no line in the template that matches the input,8 invalid input specification */#endif /* !_REENT_ONLY */#endif /* __XSI_VISIBLE >= 4 */#if __GNU_VISIBLE/* getdate_r returns the error code as above */intgetdate_r (const char *, struct tm *);#endif /* __GNU_VISIBLE */#endif /* HAVE_GETDATE *//* defines for the opengroup specifications Derived from Issue 1 of the SVID. */#if __SVID_VISIBLE || __XSI_VISIBLEextern __IMPORT long _timezone;extern __IMPORT int _daylight;#endif#if __POSIX_VISIBLEextern __IMPORT char *_tzname;/* POSIX defines the external tzname being defined in time.h */#ifndef tzname#define tzname _tzname#endif#endif /* __POSIX_VISIBLE */#ifdef __cplusplus}#endif#include <sys/features.h>#ifdef __CYGWIN__#include <cygwin/time.h>#endif /*__CYGWIN__*/#if defined(_POSIX_TIMERS)#include <signal.h>#ifdef __cplusplusextern "C" {#endif/* Clocks, P1003.1b-1993, p. 263 */int clock_settime (clockid_t clock_id, const struct timespec *tp);int clock_gettime (clockid_t clock_id, struct timespec *tp);int clock_getres (clockid_t clock_id, struct timespec *res);/* Create a Per-Process Timer, P1003.1b-1993, p. 264 */int timer_create (clockid_t clock_id,struct sigevent *__restrict evp,timer_t *__restrict timerid);/* Delete a Per_process Timer, P1003.1b-1993, p. 266 */int timer_delete (timer_t timerid);/* Per-Process Timers, P1003.1b-1993, p. 267 */int timer_settime (timer_t timerid, int flags,const struct itimerspec *__restrict value,struct itimerspec *__restrict ovalue);int timer_gettime (timer_t timerid, struct itimerspec *value);int timer_getoverrun (timer_t timerid);/* High Resolution Sleep, P1003.1b-1993, p. 269 */int nanosleep (const struct timespec *rqtp, struct timespec *rmtp);#ifdef __cplusplus}#endif#endif /* _POSIX_TIMERS */#if defined(_POSIX_CLOCK_SELECTION)#ifdef __cplusplusextern "C" {#endifint clock_nanosleep (clockid_t clock_id, int flags,const struct timespec *rqtp, struct timespec *rmtp);#ifdef __cplusplus}#endif#endif /* _POSIX_CLOCK_SELECTION */#ifdef __cplusplusextern "C" {#endif/* CPU-time Clock Attributes, P1003.4b/D8, p. 54 *//* values for the clock enable attribute */#define CLOCK_ENABLED 1 /* clock is enabled, i.e. counting execution time */#define CLOCK_DISABLED 0 /* clock is disabled *//* values for the pthread cputime_clock_allowed attribute */#define CLOCK_ALLOWED 1 /* If a thread is created with this value a *//* CPU-time clock attached to that thread *//* shall be accessible. */#define CLOCK_DISALLOWED 0 /* If a thread is created with this value, the *//* thread shall not have a CPU-time clock *//* accessible. *//* Flag indicating time is "absolute" with respect to the clockassociated with a time. Value 4 is historic. */#define TIMER_ABSTIME4/* Manifest Constants, P1003.1b-1993, p. 262 */#if __GNU_VISIBLE#define CLOCK_REALTIME_COARSE((clockid_t) 0)#endif#define CLOCK_REALTIME((clockid_t) 1)/* Manifest Constants, P1003.4b/D8, p. 55 */#if defined(_POSIX_CPUTIME)/* When used in a clock or timer function call, this is interpreted asthe identifier of the CPU_time clock associated with the PROCESSmaking the function call. */#define CLOCK_PROCESS_CPUTIME_ID ((clockid_t) 2)#endif#if defined(_POSIX_THREAD_CPUTIME)/* When used in a clock or timer function call, this is interpreted asthe identifier of the CPU_time clock associated with the THREADmaking the function call. */#define CLOCK_THREAD_CPUTIME_ID((clockid_t) 3)#endif#if defined(_POSIX_MONOTONIC_CLOCK)/* The identifier for the system-wide monotonic clock, which is defined* as a clock whose value cannot be set via clock_settime() and which* cannot have backward clock jumps. */#define CLOCK_MONOTONIC((clockid_t) 4)#endif#if __GNU_VISIBLE#define CLOCK_MONOTONIC_RAW((clockid_t) 5)#define CLOCK_MONOTONIC_COARSE((clockid_t) 6)#define CLOCK_BOOTTIME((clockid_t) 7)#define CLOCK_REALTIME_ALARM((clockid_t) 8)#define CLOCK_BOOTTIME_ALARM((clockid_t) 9)#endif#if defined(_POSIX_CPUTIME)/* Accessing a Process CPU-time CLock, P1003.4b/D8, p. 55 */int clock_getcpuclockid (pid_t pid, clockid_t *clock_id);#endif /* _POSIX_CPUTIME */#if defined(_POSIX_CPUTIME) || defined(_POSIX_THREAD_CPUTIME)/* CPU-time Clock Attribute Access, P1003.4b/D8, p. 56 */int clock_setenable_attr (clockid_t clock_id, int attr);int clock_getenable_attr (clockid_t clock_id, int *attr);#endif /* _POSIX_CPUTIME or _POSIX_THREAD_CPUTIME */#ifdef __cplusplus}#endif#endif /* _TIME_H_ */which confuses me because a load of the prototypes in there are also using time_t, and no errors are generated even if I edit the file so as to force it to be recompiled.This is the errorCode:In file included from ../src/main.c:46:C:/KDE420/Project1/inc/KDE_NTP.h:15:1: error: unknown type name 'time_t'; did you mean 'size_t'?15 | time_t mkgmtime(struct tm *ptm);| ^~~~~~| size_tmake: *** [src/subdir.mk:145: src/main.o] Error 1which also confuses me because this module is not main.c. Line 46 in main.c isCode:#include "KDE_NTP.h"Probably the reason for the main.c reference is that kde_ntp.h appears in several .c files and the compiler suppresses identical errors after the first one.EDIT: that was it - time.h was not being #included in main.cOne thing I'd still like to know if whether is a way to trace the chain which the compiler has followed to find (or not find) some definition. I've had so many cases, over time (no pun intended), where the IDE finds a definition in some .h file which is #included but the compiler doesn't, but later it magically does.

author: golden_labels
author_rank: Super Contributor
timestamp: October 12, 2021, 02:16:45 pm
content: Quote from: peter-h on October 12, 2021, 01:19:39 pmtime_tis always a signed integer, so can’t beuint64_. The function is, however, making an invalid assumption thattime_tindicates seconds, which it does not.(1)Quote from: peter-h on October 12, 2021, 01:19:39 pmWhich is exactly what is happening: the error message states that the error is in “KDE_NTP.h”, which is being included in that line. And it indeed is included in that line.Quote from: peter-h on October 12, 2021, 01:19:39 pmNo, the inclusion should not be in “main.c”. All neccessary things should be included by the header that depends on them (it should be self-contained), so#include <time.h>must appear in “KDE_NTP.h”, before any reference totime_t. Otherwise you are asking about similar errors in the future — and they will be your own doing.____(1)In general. It may in your particular platform, but I can’t test that.

author: Jeroen3
author_rank: Super Contributor
timestamp: October 12, 2021, 02:22:59 pm
content: Quote from: peter-h on October 12, 2021, 01:19:39 pmThe IDE is cheating, it parses all the files and follows all possible references.The compiler knows nothing, it starts compilation for every C file with the given include paths and if time_t is never declared it in any included header from top to bottom, it fails.Tracing this is easy, find the error, then go up following all includes until your find it. You won't, since the compiler didn't either. So you have to add an include, or forward declare it if it's a pointer.For standard stuff this is easy to fix. It starts to get way more complicated with custom files when lots of #ifdefs are around.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: October 12, 2021, 02:45:32 pm
content: The bit of arcana is:In general, underscore-leading names should be considered "reserved", not to be used, or at least with caution. Single underscore is library-internal, double underscore is compiler-internal, I believe. No-underscore names are user level, typical of program variables and etc. They tend to be platform-dependent so using them hurts portability; and un/re-defining them is a sure plan for disaster!There may be some peculiarities with Cube as well. I inherited a project which was full of multiple-definition errors; at least that's what bare GCC told me. A number of variables were shared between modules, and I had to declare themexternto get correct function without errors. Cube uses GCC so I don't even understand how it built in the first place...Tim

author: peter-h
author_rank: Super Contributor
timestamp: October 12, 2021, 02:51:40 pm
content: "so #include <time.h> must appear in “KDE_NTP.h”"Are you saying that nested .h files is the right way?I would have thought that for long term maintainability each .c file should #include the .h files it actually needs, only.The problem is that a lot of the "standard" .h files (like time.h) contain loads of #includes, among a mass of conditionals."The IDE is cheating, it parses all the files and follows all possible references."That explains alot."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 doextern uint32 g_fred;Cube is Eclipse based so lots of people should know it.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: October 12, 2021, 03:03:23 pm
content: Typically, you want the module-named .h to contain everything needed to use it from another module.So, "public" exports: function declarations, shared variables. The structures, types, etc. needed to use them. Any relevant#defines,enums etc. And any supporting definitions in turn: hence you'll often have#includes at the top of the .h itself. This may cause multiple reads of include files, which is why aCode:#ifndef FILE_H_INCLUDED#define FILE_H_INCLUDED...#endif // FILE_H_INCLUDEDis a common wrapping. All the library headers do this, at least I think so?And anything private to the .c file should be declared there, at the top; or in a file_private.h or .inc or something like that if you prefer keeping the clutter off to the side (of course, there's no guarantee that header doesn't get included elsewhere, whereas if it's local in the .c file, it's truly local).As you can see, there's at least a certain amount of cooperation implied by your typical C environment; trust that interfaces are followed, not stomping on other data, using the includes you're supposed to, etc. Not like those OOP languages that enforce this kind of security semantically, with self-contained packages, hierarchically defined classes (interfaces and inheritance), etc.Relevant:https://www.eevblog.com/forum/programming/header-file-exposure/Tim

author: Siwastaja
author_rank: Super Contributor
timestamp: October 12, 2021, 05:30:18 pm
content: Quote from: peter-h on October 12, 2021, 02:51:40 pmIf KDE_NTP.h, whatever it is, uses resources from time.h, then obviously, yes!You don't need to overthink about it. time.h internally prevents multiple includes. So you can and should include it wherever you need it.QuoteRight way is to include the header exactly where needed.QuoteExactly, that's the point. So if KDE_NTP.h needs it, then include it there. If main.c doesn't need it, don't do it there.QuoteIt's not a problem. Don't worry about it. Just include what you need. You don't usually even need to look at library headers. Don't let them scare you, if you do, though. Compilers are pretty efficient today even if including a lot of headers.

author: peter-h
author_rank: Super Contributor
timestamp: October 12, 2021, 06:04:02 pm
content: "If KDE_NTP.h, whatever it is, uses resources from time.h, then obviously, yes!"Would it not be clearer, a year later, to have#include "time.h"#include "kde_ntp.h"separately?Another .c file may need the kde_ntp.h but if it doesn't need time.h, why supply it?

author: magic
author_rank: Super Contributor
timestamp: October 12, 2021, 07:35:32 pm
content: For your sanity, it is recommended to include all necessary headers in those files which actually need them.In this case, KDE_NTP.h clearly needs time.h, as you found out.But you can do whatever you want and it will work, because C isn't saneQuote from: peter-h on October 12, 2021, 06:04:02 pmWell, I will repeat: this whole thread is about you finding out the reason why

author: ejeffrey
author_rank: Super Contributor
timestamp: October 12, 2021, 09:19:37 pm
content: Quote from: peter-h on October 12, 2021, 02:51:40 pmYes, absolutely.QuoteEvery *file* (including headers) should include exactly the .h files it actually needs.QuoteIf the C, POSIX, or other relevant standard says you need to include time.h to bring in a certain definition, that is what you do. You should *never* #include any libc internal headers directly. You shouldn't even have to look at them although at times it can be helpful.QuoteIDE editors have the unenviable job of doing 99% of their work with incomplete or incorrect code. So their syntax highlighting and definition lookup needs to work even in the case where your code wouldn't compile. It's parsing and lookup functions are therefore quite different than a compiler.

author: ejeffrey
author_rank: Super Contributor
timestamp: October 12, 2021, 09:39:46 pm
content: Quote from: peter-h on October 12, 2021, 06:04:02 pmIf the .c file needs kde_ntp.h, then it *does* indirectly need time.h -- as in, it won't be able to use the definitions from kde_ntp.h if time_t isn't defined.The difficulty here is that if some other file bob.c #includes kde_ntp.h but doesn't realize that imports time.h, bob may use names that conflict with unrelated definitions in time.h. This is just a hazard of using C. This is the reason why C++ has namespace support and most other languages that don't try to be C compatible eliminate header files and include directives altogether in favor of some form of packages + import/use/require directives.

author: peter-h
author_rank: Super Contributor
timestamp: October 13, 2021, 08:03:47 am
content: "Every *file* (including headers) should include exactly the .h files it actually needs."Maybe I didn't explain it well but I think I can see the answer (there isn't one).If you have fred.h andin ityou include time.h, and you have two .c files, both need fred.h but only one needs time.h, then it seems dumb to include time.h in fred.h. IMHO time.h should be included separately in the .c file which needs it. Otherwise, every .c file which is getting fred.h will also be getting time.h and you could get a name conflict with some function you have written, etc.Obviously if fred.hitselfneeds time.h then you have to include tim.h in fred.h. That happens sometimes, but from what I have seen most nested includes are accidental, and since most coders are just trying to get a product to work, if it works, you leave it."IDE editors have the unenviable job of doing 99% of their work with incomplete or incorrect code. So their syntax highlighting and definition lookup needs to work even in the case where your code wouldn't compile. It's parsing and lookup functions are therefore quite different than a compiler."It would still be handy if there was some way to see how the IDE is determining the location of the declaration. I have some variable declared in loads of .c files and somehow the IDE is picking the one it thinks is the right one. Presumably it scans the .c file itself first, including all #included .h files. But that's what the compiler should also be doing. It's probably quite complicated.

author: newbrain
author_rank: Super Contributor
timestamp: October 13, 2021, 08:17:37 am
content: Quote from: peter-h on October 12, 2021, 02:51:40 pmSome 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 andoptimize 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 thelinkage(=visibility of the identifier) is external or internal (e.g. thedefinitionuses "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 theymostly 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.

author: newbrain
author_rank: Super Contributor
timestamp: October 13, 2021, 08:28:44 am
content: Quote from: peter-h on October 13, 2021, 08:03:47 amI think you got it right(Edit: no you in fact did not, rereading yours and Golden Labels posts) - if fred.h uses time.h it should include it. If bob.c needs fred.h it will include it - but itshould notrely on time.h being included: if something from time.h is needed in bob.c, bob.c should explicitly include it.In this way, if fred.h implementation changes so that it does not use time.h any longer, nothing will break.Good IDE either automatically detect the include paths (along with e.g. compiler defines) used by your build system, or allow you to define them manually.If you follow some good rules, you won't have the same variable declared in a number of different places: you should have a single definition and a declaration in an include file to be used by whichever translation units need to access it (see the Google style guide I linked above).Sprinkling "extern" declarations in .c files is often a recipe for unmaintainable and easily broken code.

author: golden_labels
author_rank: Super Contributor
timestamp: October 13, 2021, 01:12:55 pm
content: Quote from: peter-h on October 13, 2021, 08:03:47 amIt’s “fred.h” that requires “time.h”, not some source (.c) files. The source files require it only as a consequence of “fred.h” requiring it. And “fred.h” should be self-contained. If you include “fred.h” in any compilation unit, it must work as it, including providing any dependencies it may require.Quote from: peter-h on October 13, 2021, 08:03:47 amNo. Aside from that being impossible to do in practice (you may not even know, what files have to be included), it is code duplication and it is unnecessarily introducing chances for mistakes. By doing so you are both wasting your time and shooting yourself in the foot at the same time. And if this code will ever be used by someone else, you are also confusing them and making them waste time on tracking down weird errors.Quote from: peter-h on October 13, 2021, 08:03:47 amNo, there can’t be any conflict: those are separate compilation units. And even if that would be a single compilation unit, this is why every header file must have inclusion guards.

author: ejeffrey
author_rank: Super Contributor
timestamp: October 13, 2021, 02:12:23 pm
content: Quote from: peter-h on October 13, 2021, 08:03:47 amMaybe the issue here is your idea of "X needs Y"? In your example kde_ntp.h needs time.h. maybe explain why you think it doesn't?

author: madires
author_rank: Super Contributor
timestamp: October 13, 2021, 02:20:43 pm
content: Example on how to manage header files (from, linux' time.h):Code:#ifndef _TIME_H#define _TIME_H 1#include <features.h>[...]#endif

author: Siwastaja
author_rank: Super Contributor
timestamp: October 13, 2021, 03:08:10 pm
content: It's beyond me how something such obvious can generate this much confusion.asdfg.c:#include <time.h> // needed, because later in this file:time_t thing;qwerty.h#include <time.h> // needed, because later in this file:void do_thing(time_t arg);foo.h// time_t or other time.h things not expressed. #include <time.h> not neededbar.c// time_t or other time.h things not expressed. #include <time.h> not neededIt'scompletely irrelevantto think about what something else includes and if there is nesting or no.

author: newbrain
author_rank: Super Contributor
timestamp: October 13, 2021, 04:46:39 pm
content: Quote from: Siwastaja on October 13, 2021, 03:08:10 pmWell, I agree with you*, and for me - and, I'm sure, many other contributors here - it's quite clear how this works and what good rules are.The same cannot be said for people with less experience in C, so much that Google considered the topic worthy of about 4 style guide rules (they are for C++, to be precise, but there are no differences in this case).I take your last statement as a confirmation that one should never rely on what an header includes: IMO it should be considered a black box, providing only the advertised interfaces and not any side perks grabbed by looking under the hood.So one should do their homework by explicitly including everything needed (Google and I agree) - but I've also seen somewhat experienced designers failing to realize this, and bad habits are easy to get and spread.If discussing about this topic here can help someone to write better code, it's worth the (small) effort.*in fairness, about C, I only remember one minor style thing where I did not.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 14, 2021, 09:28:04 am
content: Quote from: newbrain on October 13, 2021, 04:46:39 pmWas it shift vs. division, my argument being explicit instead of implicit?If it was, I think I have become less strict about the reasoning because if the exact asm op is important, one should carefully look at the listinganyway, because being explicit with C is not always possible, even if it is with the shift. So now I kinda agree with you on that as well.

author: Nominal Animal
author_rank: Super Contributor
timestamp: October 14, 2021, 10:18:09 am
content: Perhaps a simple example might be illustrative here.Because of "stuff", I decided I wanted a simple C/C++ implementation of efficient (hardware-accelerated, if possible) 3- and 4-component vectors of 32-bit integers, and basic arithmetic operations (addition, subtraction, multiplication, division, dot product, and three-component cross product).Note: This requires compiler-specific extensions, but those are available at least in GCC, clang, and Intel CC. This is my design choice here; Ichoseto trade some portability for the efficiency of the generated code on SIMD-capable architectures in an architecture-agnostic manner.Anyway, I started by creating a wrapper header file,ivec.h:Code:#ifndef IVEC_H#define IVEC_H/** Four-component hardware-accelerated vector type support** The r component is first, followed by the x, y, and z components.*/#include <stdint.h>#define ALT4(_1, _2, _3, _4, NAME, ...) NAME#if defined(__GNUC__)/* GCC has vector support built-in as an extension to C. */typedef int32_t ivec __attribute__((vector_size (4 * sizeof (int32_t))));#ifndef PURE_ACCESSOR#define PURE_ACCESSOR __attribute__((unused, const, always_inline)) static inline#endif#include "ivec_internal.h"#elif defined(__clang__)/* Clang has vector support built-in as an extension to C. */typedef int32_t ivec __attribute__((vector_size (4 * sizeof (int32_t))));#ifndef PURE_ACCESSOR#define PURE_ACCESSOR __attribute__((unused, const, always_inline)) static inline#endif#include "ivec_internal.h"#else/* Unsupported compiler. */#error File "ivec.h" does not contain an implementation for this compiler!#endif#endif /* IVEC_H */Because the header file is wrapped in#ifndef IVEC_H,#define IVEC_H, and#endif /* IVEC_H */, it can be included safely more than once. The idea is, that any source or header file that needs the types or functionality, does an#include "ivec.h".To detect the compiler, we can usepre-defined compiler macros. Here,__GNUC__is defined if compiling with GCC, and__clang__if compiling with clang. Recent versions of Intel C++ also defines__GNUC__, so if one wanted to add specific quirks for it, one would need to test for__INTEL_COMPILERfirst. I don't have it installed on this particular machine –– only some versions of gcc and clang ––, so I omitted it here.TheALT4()macro is used to choose macros or functions depending on the number of arguments (2, 3, or 4). It's one of those "tricks" you sometimes see that seem really odd, but they really are just an useful preprocessor trick. The way it is used, is#definegeneric_name(...) ALT4(__VA_ARGS__,four_args,three_args,two_args)(__VA_ARGS__)so thatgeneric_name(a,b)expands totwo_args(a,b);generic_name(a,b,c)expands tothree_args(a,b,c); andgeneric_name(a,b,c,d)expands tofour_args(a,b,c,d). This works in both C and C++.ThePURE_ACCESSORmacro should evaluate tostaticorstatic inlinewith compiler-specific attributes that describe the functions as only operating on its parameters (not accessing any other objects at all), with calls having the same arguments always producing the same results. The intent is to help the compiler maximally optimize these pure accessor type helper functions, without affecting the results.All compilers currently only need the sameivec_internal.h:Code:#ifndef IVEC_H#error Include "ivec.h", never "ivec_internal.h" directly!#endif#ifndef IVEC_INTERNAL_H#define IVEC_INTERNAL_H#include <stdint.h>#define IVEC4(x, y, z, r) ((ivec){ r, x, y, z })#define IVEC3(x, y, z) ((ivec){ 0, x, y, z })#define IVEC2(x, y) ((ivec){ 0, x, y, 0 })#define IVEC(...) ALT4(__VA_ARGS__, IVEC4, IVEC3, IVEC2)(__VA_ARGS__)#define IVEC_R(a) ((a))#define IVEC_X(a) ((a))#define IVEC_Y(a) ((a))#define IVEC_Z(a) ((a))/* Accessors */PURE_ACCESSOR int32_t ivec_r(const ivec a) { return a; }PURE_ACCESSOR int32_t ivec_x(const ivec a) { return a; }PURE_ACCESSOR int32_t ivec_y(const ivec a) { return a; }PURE_ACCESSOR int32_t ivec_z(const ivec a) { return a; }/* Definition (in a function form) */PURE_ACCESSOR ivec ivec4_def(const int32_t x,const int32_t y,const int32_t z,const int32_t r){const ivec result = { r, x, y, z };return result;}/* Definition (in a function form) */PURE_ACCESSOR ivec ivec3_def(const int32_t x,const int32_t y,const int32_t z){const ivec result = { 0, x, y, z };return result;}/* Definition (in a function form) */PURE_ACCESSOR ivec ivec2_def(const int32_t x,const int32_t y){const ivec result = { 0, x, y, 0 };return result;}/* Pick definition function based on number of arguments. */#define ivec_def(...) ALT4(__VA_ARGS__, ivec4_def, ivec3_def, ivec2_def)(__VA_ARGS__)/* Component-wise addition */PURE_ACCESSOR ivec ivec_add(const ivec a, const ivec b) { return a + b; }/* Component-wise subtraction */PURE_ACCESSOR ivec ivec_sub(const ivec a, const ivec b) { return a - b; }/* Component-wise multiplication */PURE_ACCESSOR ivec ivec_mul_ivec(const ivec a, const ivec b) { return a * b; }/* Component-wise division */PURE_ACCESSOR ivec ivec_div_ivec(const ivec a, const ivec b) { return a / b; }/* Component-wise multiplication by a scalar */PURE_ACCESSOR ivec ivec_mul_i32(const ivec a, const int32_t s){const ivec b = { s, s, s, s };return a * b;}/* Component-wise division by a scalar */PURE_ACCESSOR ivec ivec_div_i32(const ivec a, const int32_t s){const ivec b = { s, s, s, s };return a / b;}#ifdef __cplusplusPURE_ACCESSOR ivec ivec_mul(const ivec a, const ivec b) { return ivec_mul_ivec(a, b); }PURE_ACCESSOR ivec ivec_mul(const ivec a, const int32_t b) { return ivec_mul_i32(a, b); }PURE_ACCESSOR ivec ivec_div(const ivec a, const ivec b) { return ivec_div_ivec(a, b); }PURE_ACCESSOR ivec ivec_div(const ivec a, const int32_t b) { return ivec_div_i32(a, b); }#else/* Generic multiplication depends on the second parameter */#define ivec_mul(a, b) _Generic((b), \ivec: ivec_mul_ivec, \default: ivec_mul_i32 )(a, b)/* Generic division depends on the second parameter */#define ivec_div(a, b) _Generic((b), \ivec: ivec_div_ivec, \default: ivec_div_i32 )(a, b)#endif/* Dot product of the three first components */PURE_ACCESSOR int32_t ivec_dot3(const ivec a, const ivec b){const ivec v = a * b;return v + v + v;}/* Dot product of all four components */PURE_ACCESSOR int32_t ivec_dot4(const ivec a, const ivec b){const ivec v = a * b;return v + v + v + v;}/* Cross product of the three first components; fourth component zero */PURE_ACCESSOR ivec ivec_cross3(const ivec a, const ivec b){const ivec apos = { 0, a, a, a },bpos = { 0, b, b, b },aneg = { 0, a, a, a },bneg = { 0, b, b, b };return apos*bpos - aneg*bneg;}#endif /* IVEC_INTERNAL_H */It first verifies thatIVEC_His defined, i.e. that this file was included through ivec.h and not directly. It is also protected against multiple inclusion viaIVEC_INTERNAL_H, which should not happen unless ivec.h has a bug, so theIVEC_Hcheck alone would definitely be enough for now. (The reason I have it, is because I'll expand this with float and double vectors, and functions that convert between those vector types may need to include the internal headers for those types.)The definition of theIVEC()macro uses the aforementionedALT4()macro to pick the correct initialization macro. If only three arguments are specified, thercomponent is initialized to zero, and if only two, bothrandzcomponents are initialized to zero.Since this file expects the compiler to provide the underlying vector (as in SIMD vector, not as in C++ vector) support, there is nothing unusual in the basic arithmetic operations.In C++,ivec_mul()is an overloaded function, which calls eitherivec_mul_ivec()orivec_mul_i32(), depending on the type of the second parameter.Similarly forivec_div(),ivec_div_ivec(), andivec_div_i32().In C,ivec_mul()is a convenience macro based on the C11_Generic()facility, expanding toivec_mul_ivec()if the second parameter is an ivec, and toivec_mul_i32()otherwise.Similarly forivec_div(),ivec_div_ivec(), andivec_div_i32().ivec_cross3()uses four temporary vector constants to reorder the components, so that the difference of the products yields the expected 3D vector cross product. This tends to let the compiler generate faster SIMD code, compared to just defining the result as an array with the component-wise formulae.Here is a simple test program to visually verify the operations do work. (Note that this is not an unit test: for an unit test, I'd use a PRNG, most likely Xorshift64*, to generate random test values, and compare them to the results from naïve versions implemented in the unit test, written in as easy-to-read/verify form as possible.)test.cortest.cpp:Code:#include <stdlib.h>#include <stdio.h>#include "ivec.h"static void describe_ivec(const char *description, const ivec v){printf("%s = (%d, %d, %d)\n", description, (int)IVEC_X(v), (int)IVEC_Y(v), (int)IVEC_Z(v));}int main(void){ivec a = IVEC(1, 2, 3);ivec b = IVEC(4, 5, 6);describe_ivec("a", a);describe_ivec("b", b);describe_ivec("ivec_add(a, b)", ivec_add(a, b));describe_ivec("ivec_sub(a, b)", ivec_sub(a, b));describe_ivec("ivec_mul(a, b)", ivec_mul(a, b));describe_ivec("ivec_div(a, b)", ivec_div(a, b));describe_ivec("ivec_mul(b, 3)", ivec_mul(a, 3));describe_ivec("ivec_div(b, 2)", ivec_div(b, 2));describe_ivec("ivec_cross3(a, b)", ivec_cross3(a, b));printf("ivec_dot3(a, b) = %d\n", (int)ivec_dot(a, b));return EXIT_SUCCESS;}You can compile the above (as eitherexample.corexample.cpp) usinggcc,g++, orclang; I haven't checked other compilers, but at least Intel compiler either works or can be made to work by adding a small snippet of code into ivec.h.If you enable optimization (I tested with -O2), and examine the generated binary, you'll find that the example executable does not actually do any SIMD operations at all, and just prints preset constants! This is exactly as I wanted: a desired side effect of how the PURE_ACCESSOR -annotated helper functions should be implemented by the compiler. (But, if you generate one or both randomly at runtime or based on input, SIMD instructions will be used, of course.)(Additional functions, like component-wise minimum and maximum, and Manhattan distance (sum of components magnitudes), could use <immintrin.h> on x86 and x86-64, or GCC x86/x86-64/ARM vector built-ins, and rely on the compiler to generate sane code from component-wise expressions on others. I omitted those to keep to some semblance of simplicity, but I'd probably put those alternatives into sub-header-filesivec_internal_generic.h,ivec_internal_sse3.h,ivec_internal_avx.h, andivec_internal_neon.h, for example.)If I addfvec(four-component float (32-bit FP) vectors), I'll add anfvec.handfvec_internal.h.Then, I'll also create anvecs.hthat includes bothivec.handfvec.h, and a newvecs_internal.hthat defines the conversion functions betweenivecandfvectypes.This way, files or headers that use either type (or both but not the conversion functions), just include "ivec.h" and/or "fvec.h"; and those that need the conversion functions too, include "vecs.h". Because the features are split into the internal parts, simple include guards as implemented above will take care of the order and re-inclusion absolutely fine. Even including all tree (ivec.h, fvec.h, vecs.h) is absolutely fine, and yields the same generated code as if one only included vecs.h.In this scheme, therefore,#include "header"means "This particular source or header file requires the facilities declared byheader".When tracing the source code, it is common for the compilation unit to have multiple includes of the same header, just in different header files.This sceme also plays perfectly well with dependency tracking. For example, if you usegcc, options-M -MFsourcefile.depsgenerates no object file, but saves the Makefile rule specifying the dependencies of the object file as filesourcefile.deps. These can then be include'd in the Makefile, so that changes (modification timestamps to later than the dependent object file) to any source or header files, including system header files, will cause (only) the affected object files to be recompiled. Usually, the dependency tracking target is calleddeps, so that if you create new files or delete old ones, or change any #include directives, you only need to runmake depsto update the dependency information. It only takes a few lines in the Makefile to automate all this, but it depends very much on how you structure your source trees, so there is no one single recipe to fit everyone.

author: newbrain
author_rank: Super Contributor
timestamp: October 14, 2021, 12:54:41 pm
content: Quote from: Siwastaja on October 14, 2021, 09:28:04 amExactly, I'm honoured you remember or took the time to search for it!As I've a "say what you mean"* approach to C programming, I think it applies also here for include rules: include what you need - do not rely on side effects.*E.g. the mentioned '/' vs '>>' and '%' vs '&', using 'name[]' for a function parameter if it is an array rather than a pointer to a single object, etc..