1

I want to know if I can rely on the particular definitions of the include guards in standard headers. If I look on my system with Visual Studio I can see, for example, that stdint.h has the _STDINT defined. My question is if I can rely on this #define for other compilers too. Basically, is it safe for me to do something like this:

#ifndef _STDINT
typedef char int8_t;
#endif

I know this might look stupid. Why not just include stdint.h you say? Well I'm writing code which may be deployed embedded targets, which might not be happy about including certain standard c headers. However, I want to be able to use them when they are available. Therefore, I want to be able to choose in the file that has int main() whether I want to include stdint.h or not.

xaviersjs
  • 1,579
  • 1
  • 15
  • 25
  • 8
    short answer is no, and long answer is dear-god-don't-do-this. underscore #defines are reserved for the file scope only, so using them outside that file is risky at best. The correct way to handle this (imho) would be to provide your own "stdint.h" iff it is not found on the target machine. I'd do an optional include directory, which is included if the compiler is not c99/posix compatible. then include stdint with `#include "stdint.h"`. – IdeaHat Oct 27 '14 at 14:29
  • "an optional include directory, which is included if the compiler is not c99/posix compatible". Forgive my ignorance. How do I do that? Via compiler flag? Or another header which configures my system according to c99 defines? – xaviersjs Oct 27 '14 at 14:32
  • @xaviersjs It's compiler dependent, but yes, it usually boils down to giving one extra path to compiler. – user694733 Oct 27 '14 at 14:36
  • 1
    @xaviersjs This shouldn't be handled in the header file but rather as part of whatever build system you are using, I would think. I personally use CMake so `CHECK_INCLUDE_FILE_CONCAT ("stdint.h" H4H5_HAVE_STDINT_H)` takes care of that, but you'd have to set it up how you want it. – IdeaHat Oct 27 '14 at 15:10
  • @MadScienceDreams Your answer would _break_ in several scenarios without some kind of `configure` process to go beforehand. On GCC at least (and perhaps some other compilers), user-defined include paths (i.e. those passed as `-I`) are included _before_ system include directories; therefore this "optional" include will always become included, even if the system _does_ have its own `stdint.h`. Sometimes this may be sufficient (for example, if it's known that the entire application and all its compile-time deps only use a small well defined subset). – Mark Nunberg Oct 27 '14 at 15:22
  • @mnunberg that is why I encouraged him to make it as part of the build system, NOT as include files. Also why its in comments and not in an answer...making downgradable C/C++ code is always like playing with fire. – IdeaHat Oct 27 '14 at 15:25
  • Having all the #defines contained in some system header file is not a risk AND does not bloat the code. So is not a problem in embedded systems. The linker will only link code that is actually referenced in the body of the code. – user3629249 Oct 28 '14 at 00:12

2 Answers2

1

Unfortunately no :). This is why things like autoconf and friends exist, to be able to actually compile small programs and test in advance to determine if the system contains specific symbols and headers; something which cannot be done in a single pass compilation of your program.

Case in point here, the "Visual Studio stdint.h" doesn't actually exist on older visual studio versions.

The correct solution, as pointed out above, is to use some kind of out-of-source configure system to determine what to do.

Sometimes, you might be able to get away with something simpler, depending on how large a subset you use from stdint.h:

  • Use project-prefixed fixed with types (my_I32 for example), and define them as being int; if it's known beforehand that all target platforms will have int as being a 32-bit integer.

  • Use a project-supplied stdint.h and place the path of this file in your preprocessor search path. Note that this requires that your project and any dependencies of this project, including system headers will only use the subset of declarations defined in this file.

Mark Nunberg
  • 3,551
  • 15
  • 18
0

You can rely on a standard library header to declare exactly (and no more than) what it is specified to declare. This means that:

  • If a feature is optional, the standard will specify a feature-test macro that you can check for with #if or #ifdef, to tell you if it is available on your platform. e.g. threads.h is optional in C11, so the standard specifies that __STDC_NO_THREADS__ is defined to 1 if the header is not available.

  • If a feature is not optional, you should be able to assume it is present! If it isn't present, the compiler isn't conforming to the language version, and you'd better hope that this stuff is explicitly spelled out in the documentation, because you're into implementation-defined territory.

  • Many features are provided as macros, or with associated macro definitions; you can therefore test for the existence of such features independent of the whole header.

So for the case of stdint.h, there is no feature test macro provided because the header is not optional. If it isn't present, the compiler really ought to be documenting that, and not claiming standard compliance. However, certain features within stdint.h are optional, and are therefore testable individually with their associated macros.

For instance, the exact-width integer types described in 7.20 (int8_t and so on) require associated macro definitions describing their maximum and minimum values (and that these macros specifically not be defined if the types are not available), so you can test for the specific availability of int8_t by testing whether INT8_MAX is defined after including the header.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89