Author Topic: nesting of .h files and .h files which start with an underscore  (Read 1607 times)

0 Members and 1 Guest are viewing this topic.

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 1331
  • Country: gb
  • Doing electronics since the 1960s...
nesting of .h files and .h files which start with an underscore
« on: October 12, 2021, 09:50:51 am »
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 it :)

Is 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.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 90S1200 32F417
 

Offline madires

  • Super Contributor
  • ***
  • Posts: 6378
  • Country: de
  • A qualified hobbyist ;)
Re: nesting of .h files and .h files which start with an underscore
« Reply #1 on: October 12, 2021, 10:50:32 am »
#include <time.h>?
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 1331
  • Country: gb
  • Doing electronics since the 1960s...
Re: nesting of .h files and .h files which start with an underscore
« Reply #2 on: October 12, 2021, 11:15:45 am »
Sure, it should be there but it isn't.

A right-click on time_t and "open declaration" points to _timeval.h which contains

typedef   _TIME_T_   time_t;

and doing that on _TIME_T_ takes one to _types.h and in there is

#define   _TIME_T_ __int_least64_t

which takes one to _default_types.h and in there is

typedef __INT_LEAST64_TYPE__ __int_least64_t;

which is a macro that expands to

long long int

But 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.
« Last Edit: October 12, 2021, 11:33:23 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 90S1200 32F417
 

Online golden_labels

  • Frequent Contributor
  • **
  • Posts: 473
  • Country: pl
Re: nesting of .h files and .h files which start with an underscore
« Reply #3 on: October 12, 2021, 12:19:23 pm »
It doesn’t have to be there (assuming that by “there” you mean the time.h header). It has to be defined in the current compilation unit after the preprocessor expands time.h. That’s the only requirement. It may be in some other header included by time.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 what time_t is, 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 use time_t. For suggestions of a workaround, tell how that time_t is 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 for uint64_t, assuming that this type is available in that compiler(2))
____
(1) Which I haven’t seen for time_t, but some other built-in things happen to be provided that way.
(2) Presence of any uintN_t and intN_t type is always optional in C.
« Last Edit: October 12, 2021, 12:45:05 pm by golden_labels »
Dihydrogen monoxide was responsible for Fukushima, Chernobyl and TMI disasters
Worth watching: Calling Bullshit — protect your friends and yourself from bullshit!
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 3721
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: nesting of .h files and .h files which start with an underscore
« Reply #4 on: October 12, 2021, 12:31:05 pm »
Yeah, it may very well be that <time.h> was included above "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.
 

Online magic

  • Super Contributor
  • ***
  • Posts: 4090
  • Country: pl
Re: nesting of .h files and .h files which start with an underscore
« Reply #5 on: October 12, 2021, 01:11:03 pm »
Is there a way to work out the dependency path which the compiler followed?
Start 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.
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 1331
  • Country: gb
  • Doing electronics since the 1960s...
Re: nesting of .h files and .h files which start with an underscore
« Reply #6 on: October 12, 2021, 01:19:39 pm »
This is the function (this one is found in many places online)

Code: [Select]

time_t mkgmtime(struct tm *ptm)
{
  time_t secs=0;
  // tm_year is years since 1900
  int year=ptm->tm_year + 1900;
  for (int y=1970; y < year; ++y)
    {
    secs+=(IsLeapYear(y) ? 366 : 365) * SecondsPerDay;
    }
  // tm_mon is month from 0..11
  for (int m=0; m < ptm->tm_mon; ++m)
    {
    secs+=NtpDaysPerMonth[m] * 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 this

Code: [Select]
time_t mkgmtime(struct tm *ptm);


time.h is included above this .h file and contains this

Code: [Select]
/*
 * 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_C

struct tm
{
  int tm_sec;
  int tm_min;
  int tm_hour;
  int tm_mday;
  int tm_mon;
  int tm_year;
  int tm_wday;
  int tm_yday;
  int tm_isdst;
#ifdef __TM_GMTOFF
  long __TM_GMTOFF;
#endif
#ifdef __TM_ZONE
  const char *__TM_ZONE;
#endif
};

clock_t    clock (void);
double    difftime (time_t _time2, time_t _time1);
time_t    mktime (struct tm *_timeptr);
time_t    time (time_t *_timer);
#ifndef _REENT_ONLY
char   *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);
#endif
size_t    strftime (char *__restrict _s,
     size_t _maxsize, const char *__restrict _fmt,
     const struct tm *__restrict _t);

#if __POSIX_VISIBLE >= 200809
extern size_t strftime_l (char *__restrict _s, size_t _maxsize,
  const char *__restrict _fmt,
  const struct tm *__restrict _t, locale_t _l);
#endif

char   *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 __cplusplus
extern "C" {
#endif

#if __XSI_VISIBLE
char      *strptime (const char *__restrict,
const char *__restrict,
struct tm *__restrict);
#endif
#if __GNU_VISIBLE
char *strptime_l (const char *__restrict, const char *__restrict,
  struct tm *__restrict, locale_t);
#endif

#if __POSIX_VISIBLE
void      tzset (void);
#endif
void      _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[2];
} __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 */
int getdate_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_VISIBLE
extern __IMPORT long _timezone;
extern __IMPORT int _daylight;
#endif
#if __POSIX_VISIBLE
extern __IMPORT char *_tzname[2];

/* 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 __cplusplus
extern "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 __cplusplus
extern "C" {
#endif

int clock_nanosleep (clockid_t clock_id, int flags,
const struct timespec *rqtp, struct timespec *rmtp);

#ifdef __cplusplus
}
#endif

#endif /* _POSIX_CLOCK_SELECTION */

#ifdef __cplusplus
extern "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 clock
   associated with a time.  Value 4 is historic. */

#define TIMER_ABSTIME 4

/* 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 as
   the identifier of the CPU_time clock associated with the PROCESS
   making 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 as
    the identifier of the CPU_time clock associated with the THREAD
    making 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 error

Code: [Select]
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_t
make[1]: *** [src/subdir.mk:145: src/main.o] Error 1


which also confuses me because this module is not main.c. Line 46 in main.c is

Code: [Select]
#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.c :)

One 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.
« Last Edit: October 12, 2021, 01:33:31 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 90S1200 32F417
 

Online golden_labels

  • Frequent Contributor
  • **
  • Posts: 473
  • Country: pl
Re: nesting of .h files and .h files which start with an underscore
« Reply #7 on: October 12, 2021, 02:16:45 pm »
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.
time_t is always a signed integer, so can’t be uint64_. The function is, however, making an invalid assumption that time_t indicates seconds, which it does not.(1)

which also confuses me because this module is not main.c. Line 46 in main.c is (…)
Which 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.

that was it - time.h was not being #included in main.c
No, 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 to time_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.



Dihydrogen monoxide was responsible for Fukushima, Chernobyl and TMI disasters
Worth watching: Calling Bullshit — protect your friends and yourself from bullshit!
 
The following users thanked this post: newbrain, thinkfat

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 3721
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: nesting of .h files and .h files which start with an underscore
« Reply #8 on: October 12, 2021, 02:22:59 pm »
One 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.
The 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.
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 18487
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: nesting of .h files and .h files which start with an underscore
« Reply #9 on: October 12, 2021, 02:45:32 pm »
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 them extern to get correct function without errors.  Cube uses GCC so I don't even understand how it built in the first place...

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 
The following users thanked this post: SiliconWizard

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 1331
  • Country: gb
  • Doing electronics since the 1960s...
Re: nesting of .h files and .h files which start with an underscore
« Reply #10 on: October 12, 2021, 02:51:40 pm »
"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 a lot.

"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.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 90S1200 32F417
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 18487
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: nesting of .h files and .h files which start with an underscore
« Reply #11 on: October 12, 2021, 03:03:23 pm »
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 a
Code: [Select]
#ifndef FILE_H_INCLUDED
#define FILE_H_INCLUDED
...
#endif // FILE_H_INCLUDED
is 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
« Last Edit: October 12, 2021, 03:07:59 pm by T3sl4co1l »
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 4885
  • Country: fi
Re: nesting of .h files and .h files which start with an underscore
« Reply #12 on: October 12, 2021, 05:30:18 pm »
"so #include <time.h> must appear in “KDE_NTP.h”"

If 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.

Quote
Are you saying that nested .h files is the right way?

Right way is to include the header exactly where needed.

Quote
I would have thought that for long term maintainability each .c file should #include the .h files it actually needs, only.

Exactly, 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.

Quote
The problem is that a lot of the "standard" .h files (like time.h) contain loads of #includes, among a mass of conditionals.

It'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.
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 1331
  • Country: gb
  • Doing electronics since the 1960s...
Re: nesting of .h files and .h files which start with an underscore
« Reply #13 on: October 12, 2021, 06:04:02 pm »
"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?
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 90S1200 32F417
 

Online magic

  • Super Contributor
  • ***
  • Posts: 4090
  • Country: pl
Re: nesting of .h files and .h files which start with an underscore
« Reply #14 on: October 12, 2021, 07:35:32 pm »
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 sane 8)

Another .c file may need the kde_ntp.h but if it doesn't need time.h, why supply it?
Well, I will repeat: this whole thread is about you finding out the reason why ;)
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 2735
  • Country: us
Re: nesting of .h files and .h files which start with an underscore
« Reply #15 on: October 12, 2021, 09:19:37 pm »
"so #include <time.h> must appear in “KDE_NTP.h”"

Are you saying that nested .h files is the right way?

Yes, absolutely.

Quote
I would have thought that for long term maintainability each .c file should #include the .h files it actually needs, only.

Every *file* (including headers) should include exactly the .h files it actually needs.

Quote
The problem is that a lot of the "standard" .h files (like time.h) contain loads of #includes, among a mass of conditionals.

If 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.

Quote
Quote
"The IDE is cheating, it parses all the files and follows all possible references."
That explains a lot.

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.
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 2735
  • Country: us
Re: nesting of .h files and .h files which start with an underscore
« Reply #16 on: October 12, 2021, 09:39:46 pm »
Another .c file may need the kde_ntp.h but if it doesn't need time.h, why supply it?

If 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.
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 1331
  • Country: gb
  • Doing electronics since the 1960s...
Re: nesting of .h files and .h files which start with an underscore
« Reply #17 on: October 13, 2021, 08:03:47 am »
"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 and in it you 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.h itself needs 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.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 90S1200 32F417
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1269
  • Country: se
Re: nesting of .h files and .h files which start with an underscore
« Reply #18 on: October 13, 2021, 08:17:37 am »
"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 definitions
The 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.

Cube
All 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.
« Last Edit: October 13, 2021, 09:08:38 am by newbrain »
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1269
  • Country: se
Re: nesting of .h files and .h files which start with an underscore
« Reply #19 on: October 13, 2021, 08:28:44 am »
"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 and in it you 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.h itself needs 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.
I 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 it should not rely 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.
« Last Edit: October 13, 2021, 01:41:40 pm by newbrain »
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Online golden_labels

  • Frequent Contributor
  • **
  • Posts: 473
  • Country: pl
Re: nesting of .h files and .h files which start with an underscore
« Reply #20 on: October 13, 2021, 01:12:55 pm »
If you have fred.h and in it you 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.
It’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.

IMHO time.h should be included separately in the .c file which needs it.
No. 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.

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.
No, 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.
Dihydrogen monoxide was responsible for Fukushima, Chernobyl and TMI disasters
Worth watching: Calling Bullshit — protect your friends and yourself from bullshit!
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 2735
  • Country: us
Re: nesting of .h files and .h files which start with an underscore
« Reply #21 on: October 13, 2021, 02:12:23 pm »
"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 and in it you 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.h itself needs 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.

Maybe 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?
 

Offline madires

  • Super Contributor
  • ***
  • Posts: 6378
  • Country: de
  • A qualified hobbyist ;)
Re: nesting of .h files and .h files which start with an underscore
« Reply #22 on: October 13, 2021, 02:20:43 pm »
Example on how to manage header files (from, linux' time.h):

Code: [Select]
#ifndef _TIME_H
  #define _TIME_H 1

  #include <features.h>
  [...]
#endif
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 4885
  • Country: fi
Re: nesting of .h files and .h files which start with an underscore
« Reply #23 on: October 13, 2021, 03:08:10 pm »
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 needed

bar.c
// time_t or other time.h things not expressed. #include <time.h> not needed


It's completely irrelevant to think about what something else includes and if there is nesting or no.
 
The following users thanked this post: newbrain

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1269
  • Country: se
Re: nesting of .h files and .h files which start with an underscore
« Reply #24 on: October 13, 2021, 04:46:39 pm »
It's beyond me how something such obvious can generate this much confusion.
[...]
It's completely irrelevant to think about what something else includes and if there is nesting or no.
Well, 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.
Nandemo wa shiranai wa yo, shitteru koto dake.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf