9

I was reading about multiple inclusions of the same header in one file, and found an interesting statement (link):

There are a couple of tricks with header files were you deliberately include it multiple times (this does actually provide a useful feature).

I understand that those tricks are probably undesired and confusing in real-world projects (especially since people take precautions against multiple inclusions, like include guards and #pragma once). But still, what are those tricks? I came up with a few ideas, but would like to see some actual examples (ideally, safe and tried).

My thoughts:

  • Pseudo-templates in C, where template parameters are substituted with preprocessor definitions. It can be done without inclusions, but functions may be too big or too numerous, so making a separate file would make sense.
  • Block-by-block struct/class construction (concatenation of pieces). It may help emulate inheritance in C and prevent code duplication when defining structs with common members.
  • Look-up tables and other compile-time data structures (again, with the aid of preprocessor definitions).
Community
  • 1
  • 1
ostappus
  • 302
  • 2
  • 11
  • Maybe if you're using `#define` and `#undef` sprinkled throughout a source file... – Fiddling Bits Jul 24 '14 at 20:55
  • Why don't you ask the person that posted that? – juanchopanza Jul 24 '14 at 21:03
  • @juanchopanza Because I don't have enough reputation. Besides, I thought the issue deserves its own question and StackOverflow is more suitable for language-specific questions. – ostappus Jul 24 '14 at 21:12
  • I know I've used this trick for tables in the past, but not recently - pretty sure I haven't tried it since I learned about `#pragma once`. – Mark Ransom Jul 24 '14 at 22:35
  • inline files can be handy for generating code: http://coliru.stacked-crooked.com/a/f39d27793f083fdb – Mooing Duck Jul 25 '14 at 00:01
  • Also, I know that prior to VC++10 or so, `` used to include two other headers a couple hundred times – Mooing Duck Jul 25 '14 at 00:03
  • For more about X-macros, see [Macro to cycle through and allocate data to members of structs](http://stackoverflow.com/questions/15983496/macro-to-cycle-through-and-allocate-data-to-members-of-structs-incorrectly-recog) and [Easy way to use variables of `enum` types as string in C](http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c) -- probably amongst many others. – Jonathan Leffler Jul 25 '14 at 00:04

2 Answers2

8

#include "file" means take the header file and put all of its content instead of the #include line.

We usually used headers for type definitions and for adding a forward declarations to a source files. defining same type twice in a file (a circular include will always cause it) gives compilation error, therefore we use #ifndef or #pragma once. (or both)

But we also can to put a repeating code and macros and include it several times, even in the same file. in such as case, we won't use #ifndef nor #pragma once. If you do so you must be extra careful, and do it only if you know what you are doing.

For example: If in some OS calling a specific system function (or even a c macro like: offsetof) cause a bunch of warnings, and it is bothering you, and you sure your code is good, but you don't want to disable all the warnings you've got on all the project or the file, you just want to disable it when you call the specific function.

//suppress the warnings: 
#if defined(__GNUC__)
  #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wreorder"
    #pragma GCC diagnostic ignored "-Wunused-function"
    #pragma GCC diagnostic ignored "-Wunused-variable"
    #pragma GCC diagnostic ignored "-Wsign-compare"
    #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
    #pragma GCC diagnostic ignored "-Wsequence-point"
  #endif
#endif // __GNUC__

//here you call the function...
func(x,y,z);

//unsupress: bring back the warnings to normal state
#if defined(__GNUC__)
  #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
    #pragma GCC diagnostic pop
  #endif
#endif // __GNUC__

This will make your code to look very dirty, especially if you call the function several times.

One possible solution, (I'm not suggesting it is the best one...) is to make 2 headers, in one to suppress the warnings and in the other to cancel the suppression.

In that case your code may look like this:

#include "suppress.h"
func(x,y,z);
#include "unsuppress.h"

//.... more code come here 
//now when call it again:
#include "suppress.h"
func(x,y,z);
#include "unsuppress.h"
SHR
  • 7,940
  • 9
  • 38
  • 57
2

The 'standard' example is the <assert.h> header. The effect of including it depends on the value of NDEBUG:

#include <assert.h>

void func1(void)
{
    assert(...);
}

#undef NDEBUG
#include <assert.h>

void func2(void)
{
    assert(...);
}

#define NDEBUG
#include <assert.h>

void func3(void)
{
    assert(...);
}

The assertion in func1() is active unless something in the compilation environment has set NDEBUG at the time when <assert.h> was included. The assertion in func2() is active because NDEBUG was undefined when <assert.h> was included. The assertion in func3() is inactive because NDEBUG was defined when <assert.h> was included.

Having said that, I've never used this facility in real life, but the C standard blesses (mandates) the behaviour shown.

Note that this does not happen by accident; it happens because the header is deliberately engineered to be (re)used several times in a single TU.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278