13

I'm writing a header-only logger library and I need global variables to store current logger settings (output flags, log file descriptor etc.). My thoughts:

  • I can't declare variables as extern, as i don't have access to the translation units to define them
  • I can't just define global variables in header as it will lead to multiple definitions
  • static variables in header's functions may seem good at the first glance, but the fact is that every translation unit will have it's own copy of 'global' variable, which is awkward and definitely wrong
  • I don't also think it is possible to avoid global variables in my case (even though that's what i'd like to do) as i obviously have to store settings somehow between log function calls

Is there any variants i didn't consider yet? Is there any other way to have global variables using headers only.

p.s. I'm looking for both c99/c++11 compatible solution with possible gcc hacks (gcc >= 4.8)

  • 4
    If you program in C++ then please only tag C++. – Some programmer dude Jul 31 '18 at 11:59
  • As for your problem, have you considered the singleton pattern? It's very common for loggers, and can easily be adapted to hold "global" data for a header-only library. – Some programmer dude Jul 31 '18 at 12:00
  • @Someprogrammerdude i added special note to say that i'm looking for both c/c++ solution –  Jul 31 '18 at 12:01
  • Lastly,, why are you making your own logging library? There are plenty out there already? What use-case will your library solve that can't be solved by others? Or is it just an exercise or learning experience? – Some programmer dude Jul 31 '18 at 12:01
  • At some point I also used the `static` trick to set constant values visible from the header file. They are duplicated in each module which include the header file. This is quite convenient because you only need to define it once. And, as they are constant, you do not have any problem at duplicating it. But, I guess that you are speaking about real variables (meaning that you want to modify their values). – perror Jul 31 '18 at 12:01
  • @perror I do want to modify them, yes –  Jul 31 '18 at 12:02
  • 3
    You do know that C and C++ are two *very* different languages? Solutions using C++ might not be possible to use in C, and the opposite (though that's more unlikely). – Some programmer dude Jul 31 '18 at 12:02
  • @Someprogrammerdude Sure, as well as some C realizations may not be used in C++ due to broken backward compability at some place. That's why this note was required –  Jul 31 '18 at 12:04
  • @Indev: So, the best way is to do as for the `errno` module, you will define a variable present in the implementation (`.c`) and flagged as `extern` in the header file. – perror Jul 31 '18 at 12:05
  • @perror so there is no actual way to avoid implementation in source file? –  Jul 31 '18 at 12:08
  • @Someprogrammerdude i didn't hear about any singleton implementations in pure c, is it even possible? :) –  Jul 31 '18 at 12:10
  • @Indev29: Well, the header files do not really exist in memory. They are just used to connect modules together. So, I do not exactly see how we could store a value in thin air... you obviously need to have it stored in a module of your program. Or, I might have misunderstood what you meant... – perror Jul 31 '18 at 12:31
  • There is no "broken backwrds compatibility. C and C++ are not intended to be compatible. Your question does not make sense, it's most likely an XY problem steming from missconceptions what headers are or some nonsense artificial constraint. – too honest for this site Jul 31 '18 at 14:43

2 Answers2

11

One approach is to hide options behind a function that returns a reference to a local static option. As long as the ODR is not violated (e.g. by some macro-dependent changes of the functions), it is guaranteed that the local static variables are unique across your program. As a simple example, this can be in the header file:

inline bool& someOption()
{
   static bool opt = false;

   return opt;
}

and in a translation unit:

someOption() = true;

It would probably be useful to group your options into a struct and apply the above technique to an instance to this struct.

Note that this approach is limited to C++ (thanks to @rici for the hint), and might only accidently work out in C using gcc.

lubgr
  • 37,368
  • 3
  • 66
  • 117
  • I wrote about this approach in my question. I can't add code to translation units as i have header only library –  Jul 31 '18 at 12:03
  • @Indev29 Only the first snippet is for the header file. The one-liner to demonstrate the usage in a translation unit is client code, not your library. – lubgr Jul 31 '18 at 12:06
  • i get it, this is Meyer's singleton which will work for c++ and what about c realization? –  Jul 31 '18 at 12:07
  • @Indev29 Return a pointer, and use a macro that dereferences the value returned by the function? Like is usually done for `errno`. – Some programmer dude Jul 31 '18 at 12:10
  • @Someprogrammerdude i'll return pointer to a static variable, sure, but it (variable) will have different value for each translation unit –  Jul 31 '18 at 12:12
  • @Indev29 No, it will have the same value (from my answer: "it is guaranteed that the local static variables are unique across your program"). – lubgr Jul 31 '18 at 12:13
  • 1
    @lubgr: C++ makes that guarantee but C specifies the opposite: "Since an inline definition is distinct from the corresponding external definition and from any other corresponding inline definitions in other translation units, all corresponding objects with static storage duration are also distinct in each of the definitions." (Footnote 140 in section 6.7.4). If you have been using this approach in C without problems, it might be because GCC by default implements non-standard semantics. – rici Jul 31 '18 at 14:21
  • It can even simply fail to compile due to multiple definitions of `someOption()` when linking. To be compatible any `inline` needs to be `static` as well defeating the purpose. – Goswin von Brederlow Jul 31 '18 at 15:18
  • @goswin: That's not quite correct either. In standard C, `inline` with neither `static` nor `extern` does not define a function at all; it *declares* a function with external linkage and provides an optional inline substitution. Since there are no definitions at all, the link step could indeed fail. But it will not fail because it finds multiple definitions. It will fail if the compiler chooses not to do the inline substitution at some callsite, since the linker will then not find any definition of `someOption`. (GCC does not inline unless some optimisation level is set, so it's easy to test.) – rici Jul 31 '18 at 16:31
  • @rici Thanks for the correction, I added this hint to the answer. – lubgr Jul 31 '18 at 18:00
  • @rici It might not be what the standard says but historically just specifying inline gets you all kinds of behavior depending on the compiler used. And failing with multiple definitions is a likely outcome. static inline on the other hand has a very strict definition in the standard and one compilers honor. It's more the static part, the inline still might get ignored or not. The static part makes it work either way. – Goswin von Brederlow Aug 06 '18 at 12:48
5

Structure your library like follows:

MyLibrary.h:

extern int foo;
extern int bar;
...
#ifdef MY_LIBRARY_IMPL
int foo;
int bar;
...
#endif

Then, in the library documentation, specify that in exactly one translation unit, the user of the library should #define MY_LIBRARY_IMPL before including the header file.

Paul Belanger
  • 2,354
  • 14
  • 23