4

I am reusing some C/C++ source files part of an autotools project within a CMake project and I see many source files littered with lines like:

#ifdef HAVE_UNISTD_H
#include <unistd.h>  // for getpid()
#endif

I would understand the purpose of this construct if getpid() was optional and its call was surrounded by equivalent HAVE_UNISTD_H directives. However, without HAVE_UNISTD_H the source file does not compile, complaining that getpid() is not defined. This feels a lot more cryptic than the compiler letting me know that unistd.h was not found.

Of course, this is only an example. Other popular macros include HAVE_STDINT_H, HAVE_INTTYPES_H, etc. whose presence is mandatory to compile the source file.

Why are HAVE_* guards included at all? I feel they only bring disadvantages:

  • Reusing such source files requires making sure the right header files are present and the right HAVE_* macros are defined.
  • In case of a mistake, the developer gets a more cryptic message, i.e., the compiler does not report the root cause (header not found) but an ancillary error (type/function not found).
  • The source files are a bit longer and a bit more tedious to read, i.e., #includes mixed with #ifdefs.
user1202136
  • 11,171
  • 4
  • 41
  • 62
  • 1
    Is this C or C++? The default mode of C for most current compilers would *not* be to issue an error message about `getpid()` not being declared. Which is both a reason for and against the macros, depending on whom you ask. –  Jul 09 '17 at 11:06
  • See also https://stackoverflow.com/questions/1653958. – Tom Zych Jul 09 '17 at 11:16
  • @hvd: Note that GCC from version 5 upwards defaults to C11 mode and will generate at minimum a warning message for functions that are declared implicitly. Only old versions of GCC default to C90. – Jonathan Leffler Jul 09 '17 at 15:50
  • @JonathanLeffler I'm well aware of that and that doesn't contradict my comment. It's still only a warning in GCC 5, 6 and 7. –  Jul 09 '17 at 16:04
  • Note that code that only does `#ifdef HAVE_UNISTD_H / #include / #endif` without providing any fallback is going against the intent of the `HAVE_UNISTD_H` check. The code _should_ provide a generic declaration for what's missing — `int getpid(void);`, for example. Otherwise the check is pointless. That was the original intention. In the days of C90, you could get away with implicit function declaration — but C90 ought to be long-forgotten if only MS had actually implemented C99 or C11 or both. – Jonathan Leffler Jul 09 '17 at 16:18
  • That's because no one on this planet really understands autotools, so configure scripts are made by copying pasting snippets from similar projects. There's no reason beyond "it seems to work". At least, that's my experience. – el.pescado - нет войне Jul 10 '17 at 07:18

2 Answers2

12

Most of the HAVE_xxx_h guards are remnants from an era before POSIX came along and standardized the header files. In early 90s you could easily come across a system that did have getpid(), but without a working unistd.h - the function would simply be declared in another header file, or it wouldn't be declared at all, but it would still work (as long as its return value was int-sized) due to declarations being optional in K&R and C89 C.

There were even stranger issues between the multitude of systems in use at the time. For example there were systems that shipped time.h, those that shipped sys/time.h, and those that shipped both - except that among the last category there was a subset where attempting to actually include both would result in a compilation error! Supporting a vast array of such systems, where possible without listing them all in advance, was one of the explicit design goals of Autoconf, and some of the long-irrelevant hacks are still carefully documented.

In addition to the above concerns, decoupling the header names from support for functions can come in useful when porting the code to non-POSIX systems such as windows. On such systems the posix headers might be missing or broken, and the actual function definition comes from a portability library such as gnulib.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • "due to declarations being optional in pre-ANSI C" -- And C89/C90 as well. –  Jul 09 '17 at 11:08
  • @hvd Thanks, corrected. What about C99 and C11? Gcc at least seems to allow use of undeclared functions with only a warning. – user4815162342 Jul 09 '17 at 12:01
  • 1
    Formally, in C99 and C11, it's a syntax error (see Primary expressions, the footnote is explicit about it), but compilers are allowed to accept syntactically invalid code as an extension, and GCC does so for backwards compatibility (and I don't believe GCC is alone in that). –  Jul 09 '17 at 12:24
  • @hvd When you mention it being a syntax error, are you perhaps referring to pre-ANSI declarations? In the context of the answer I was referring to the use of a function [without it having been declared *at all*](http://www.tutorialspoint.com/compile_c_online.php?PID=0Bw_CjBb95KQMZVhXMF9yVHpSbGc) (but defined in a separate compilation unit). Such a function call cannot be a syntax error because the syntax of the call is correct, but would still be rejected by e.g. C++. – user4815162342 Jul 09 '17 at 14:30
  • 1
    No, that really is classified as a syntax error, I already provided a reference. The function call syntax requires an expression to be called, and undeclared identifiers are not considered as expressions, even if they look like them. –  Jul 09 '17 at 14:40
  • Referencing TutorialsPoint is not strengthening anyone's case. For example, they have an example of recursion with 'Factorial of 15 is 2004310016'. The correct answer is 1307674368000, of course, but that doesn't fit into a 32-bit value, and 2004310016 is 1307674368000 % (2^31). So they didn't notice the signed integer arithmetic overflow — aka undefined behaviour. Not good! – Jonathan Leffler Jul 09 '17 at 16:11
  • @JonathanLeffler I see that my link in the comment links to TutorialsPoint, but it was not meant to refer to or endorse the contents of the site (which I'm not familiar with), but to provide a convenient online link to modern gcc apparently accepting calls to undeclared functions. I could have used any online compiler, TutorialsPoint just happened to be the first one returned by google that supported multiple-file sources. – user4815162342 Jul 09 '17 at 16:41
1

Why are HAVE_* guards included at all? I feel they only bring disadvantages: ...

A source could approach an alternative implementation. In that case you certainly don't want to get errors for missing includes.

Silly1 example:

#ifdef HAVE_UNISTD_H
#include <unistd.h>  // for getpid()
#endif
#ifdef HAVE_WINDoWS_H
#include <windows.h> // for GetProcessId()
#endif

1I know that windows supports getpid().

user0042
  • 7,917
  • 3
  • 24
  • 39