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)
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.
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.
Related
I would like to make an array of integers via the malloc method. I want this array to be global and be used anywhere in my program. I put code in a header file that looked like this:
static int *pieces;
Then I have a function that fills it with numbers that I want in there. The function is in a namespace and the namespace is implemented in its own .cpp file. However, I import the header file into main.c and call the function from the namespace that creates the array like:
pieces = malloc(sizeof(int) * 128);
But when I try to access numbers in the array in main (after calling the function that creates my array), it crashes and says that pieces wasn't initialized. But in the function I have I can create it and manipulate the numbers in it just fine. I was under the impression that by making pieces a static variable, whenever some function anywhere changes (or sets it) then that will affect the usage of the variable anywhere. Basically what I'm trying to say is why does pieces appear unset in main, even though I set it in a function that I called?
Static is a keyword with many meanings, and in this particular case, it means not global (paraphrasing)
It means that each .cpp file has its own copy of the variable. Thus, when you initialize in main.cpp, it is initialized ONLY in main.cpp. The other files have it still uninitialized.
First thing to fix this would be to remove the keyword static. That would cause the "Multiple definitions issue". To fix this you should define the variable in a .cpp file and just extern declare it in a header file.
Edit: You are just allocating memory to it, doesnt count as initialization. You need to initialize the memory to 0 after allocation.
You can use new int[128]() instead of your more verbose malloc syntax, and this would perform initialization as well? Or you could take the easy road (thats what its there for) and use std::vector
The key is this:
static int *pieces;
You said you put that in your header. This is not the way to export a symbol. Any file that includes the header will get its own static version of an uninitialised pointer called pieces.
Instead, you put this in your header:
extern int *pieces;
extern int init_pieces();
And in the source file, you do this:
static const size_t num_pieces = 128;
int *pieces = 0;
int init_pieces()
{
pieces = malloc( num_pieces * sizeof(int) );
return pieces != NULL;
}
Now when you include your header, your source file will know to get pieces from somewhere else, and will wait for the linker to work out where. I also suggested an 'init' function for the array. I did not put a 'release' function in, however.
Note this is all C, not C++. If you're using C++ you should really use new or better still, use a vector.
Also, when using statics in C++, be mindful of this: C++ static initialization order
In C++17 standard, you can use inline specifier instead of static. For variables this means every object unit will have a copy of the variable, but linker will choose only one of them.
Or, as stated on cppreference:
An inline function or inline variable (since C++17) has the following
properties:
1) There may be more than one definition of an inline
function or variable (since C++17) in the program as long as each
definition appears in a different translation unit and (for non-static
inline functions and variables (since C++17)) all definitions are
identical. For example, an inline function or an inline variable
(since C++17) may be defined in a header file that is #include'd in
multiple source files.
2) The definition of an inline function or
variable (since C++17) must be present in the translation unit where
it is accessed (not necessarily before the point of access).
3) An inline function or variable (since C++17) with external linkage (e.g.
not declared static) has the following additional properties:
1) It must be declared inline in every translation unit.
2) It has the same address in every translation unit.
Supported in (source):
MSVC since version 19.12 (VS 2017 15.5)
GCC 7
Clang 3.9
ICC 18.0
In this case, it means you can replace
static int *pieces;
with
inline int *pieces;
For high performance code on various architectures, you may want a malloc-y allocation rather than generic new. That is because you would wrap it with something like mymalloc() and then use architecture dependent functions, such as ones that implement the proper alignment to avoid cache misses and do other nifty things provided by the hardware manufacturer, such as IBM (Bluegene) or Intel (MIC). All of these optimized allocation routines have the malloc type framework.
Learning from this: By default, non-const variables declared outside of a block are assumed to be external. However, const variables declared outside of a block are assumed to be internal.
But if I write this inside MyTools.h:
#ifndef _TOOLSIPLUG_
#define _TOOLSIPLUG_
typedef struct {
double LN20;
} S;
S tool;
#endif // !_TOOLSIPLUG_
and I include MyTools.h 3 times (from 3 different .cpp) it says that tool is already defined (at linker phase). Only if I change in:
extern S tool;
works. Isn't external default?
The declaration S tool; at namespace scope, does declare an extern linkage variable. It also defines it. And that's the problem, since you do that in three different translation units: you're saying to the linker that you have accidentally named three different global variables, of the same type, the same.
One way to achieve the effect you seem to desire, a single global shared variable declared in the header, is to do this:
inline auto tool_instance()
-> S&
{
static S the_tool; // One single instance shared in all units.
return the_tool;
}
static S& tool = tool_instance();
The function-accessing-a-local-static is called a Meyers' singleton, after Scott Meyers.
In C++17 and later you can also just declare an inline variable.
Note that global variables are considered Evil™. Quoting Wikipedia on that issue:
” The use of global variables makes software harder to read and understand. Since any code anywhere in the program can change the value of the variable at any time, understanding the use of the variable may entail understanding a large portion of the program. Global variables make separating code into reusable libraries more difficult. They can lead to problems of naming because a global variable defined in one file may conflict with the same name used for a global variable in another file (thus causing linking to fail). A local variable of the same name can shield the global variable from access, again leading to harder-to-understand code. The setting of a global variable can create side effects that are hard to locate and predict. The use of global variables makes it more difficult to isolate units of code for purposes of unit testing; thus they can directly contribute to lowering the quality of the code.
Disclaimer: code not touched by compiler's hands.
There's a difference between a declaration and a definition. extern int i; is a declaration: it says that there's a variable named i whose type is int, and extern implies that it will be defined somewhere else. int i;, on the other hand, is a definition of the variable i. When you write a definition, it tells the compiler to create that variable. If you have definitions of the same variable in more than one source file, you've got multiple definitions, and the compiler (well, in practice, the linker) should complain. That's why putting the definition S tool; in the header creates problems: each source file that #include's that header ends up defining tool, and the compiler rightly complains.
The difference between a const and a not-const definition is, as you say, that const int i = 3; defines a variable named i that is local to the file being compiled. There's no problem having the same definition in more than one source file, because those guys all have internal linkage, that is, they aren't visible outside the source file. When you don't have the const, for example, with int i = 3;, that also defines a variable named i, but it has external linkage, that it, it's visible outside the source file, and having the same definition in multiple files gives you that error. (technically, it's definitions of the same name that lead to problems; int i = 3; and double i = 3.0; in two different source files still are duplicate definitions).
By default, non-const variables declared outside of a block are assumed to be external. However, const variables declared outside of a block are assumed to be internal.
That statement is still correct in your case.
In your MyTools.h, tool is external.
S tool; // define tool, external scope
In your cpp file, however, the extern keyword merely means that S is defined else where.
extern S tool; // declare tool
I would like to make an array of integers via the malloc method. I want this array to be global and be used anywhere in my program. I put code in a header file that looked like this:
static int *pieces;
Then I have a function that fills it with numbers that I want in there. The function is in a namespace and the namespace is implemented in its own .cpp file. However, I import the header file into main.c and call the function from the namespace that creates the array like:
pieces = malloc(sizeof(int) * 128);
But when I try to access numbers in the array in main (after calling the function that creates my array), it crashes and says that pieces wasn't initialized. But in the function I have I can create it and manipulate the numbers in it just fine. I was under the impression that by making pieces a static variable, whenever some function anywhere changes (or sets it) then that will affect the usage of the variable anywhere. Basically what I'm trying to say is why does pieces appear unset in main, even though I set it in a function that I called?
Static is a keyword with many meanings, and in this particular case, it means not global (paraphrasing)
It means that each .cpp file has its own copy of the variable. Thus, when you initialize in main.cpp, it is initialized ONLY in main.cpp. The other files have it still uninitialized.
First thing to fix this would be to remove the keyword static. That would cause the "Multiple definitions issue". To fix this you should define the variable in a .cpp file and just extern declare it in a header file.
Edit: You are just allocating memory to it, doesnt count as initialization. You need to initialize the memory to 0 after allocation.
You can use new int[128]() instead of your more verbose malloc syntax, and this would perform initialization as well? Or you could take the easy road (thats what its there for) and use std::vector
The key is this:
static int *pieces;
You said you put that in your header. This is not the way to export a symbol. Any file that includes the header will get its own static version of an uninitialised pointer called pieces.
Instead, you put this in your header:
extern int *pieces;
extern int init_pieces();
And in the source file, you do this:
static const size_t num_pieces = 128;
int *pieces = 0;
int init_pieces()
{
pieces = malloc( num_pieces * sizeof(int) );
return pieces != NULL;
}
Now when you include your header, your source file will know to get pieces from somewhere else, and will wait for the linker to work out where. I also suggested an 'init' function for the array. I did not put a 'release' function in, however.
Note this is all C, not C++. If you're using C++ you should really use new or better still, use a vector.
Also, when using statics in C++, be mindful of this: C++ static initialization order
In C++17 standard, you can use inline specifier instead of static. For variables this means every object unit will have a copy of the variable, but linker will choose only one of them.
Or, as stated on cppreference:
An inline function or inline variable (since C++17) has the following
properties:
1) There may be more than one definition of an inline
function or variable (since C++17) in the program as long as each
definition appears in a different translation unit and (for non-static
inline functions and variables (since C++17)) all definitions are
identical. For example, an inline function or an inline variable
(since C++17) may be defined in a header file that is #include'd in
multiple source files.
2) The definition of an inline function or
variable (since C++17) must be present in the translation unit where
it is accessed (not necessarily before the point of access).
3) An inline function or variable (since C++17) with external linkage (e.g.
not declared static) has the following additional properties:
1) It must be declared inline in every translation unit.
2) It has the same address in every translation unit.
Supported in (source):
MSVC since version 19.12 (VS 2017 15.5)
GCC 7
Clang 3.9
ICC 18.0
In this case, it means you can replace
static int *pieces;
with
inline int *pieces;
For high performance code on various architectures, you may want a malloc-y allocation rather than generic new. That is because you would wrap it with something like mymalloc() and then use architecture dependent functions, such as ones that implement the proper alignment to avoid cache misses and do other nifty things provided by the hardware manufacturer, such as IBM (Bluegene) or Intel (MIC). All of these optimized allocation routines have the malloc type framework.
I have a header file that has a number of declarations like this:
extern ID3D10Device* device;
I can't make these static because of a problem with multiple translation units, and I can't have them as normal pointers for a similar reason. However when I try to build the program I get unresolved external symbol linker errors. I'm assuming that this is because I'm attempting to use the pointers without defining them first. This is the problem however, as the way you initialise these DirectX objects is by passing the address of the pointers as parameters into specialist methods. - I may be wrong but I am assuming this is the problem as the compiler / linker / whatever can't see the definitions.
All I'm trying to do is have these pointers (for the graphics device, depth buffer etc) visible to multiple classes. How can this be achieved?
You need the pointers to be defined in some translation unit. The linker is complaining because it seems you haven't done that anywhere. You should declare them at file scope as
ID3D10Device* device = NULL;
in the source file where you call the DirectX function that initializes them. Just make sure the declaration is only made in one source file, then the extern statement should be placed in the associated header file which is included by all translation units that need to use these pointers.
When you externally define a variable like this, the compiler is not reserving any memory for that variable until it sees a definition inside a code module itself. So if you are going to be passing these pointers by-reference to a function for initializing their values, they must be defined in a code module somewhere.
You only need to define them in a single code module ... then place the extern declarations inside a header file you include in the rest of your code modules that require access to the pointer variables. That shouldn't create any linker errors due to duplicate definitions.
I can't make these static because of a problem with multiple translation units
If you need a different variable in different TU (translation units), make it static: this way the variable will be specific to each TU.
A declaration of a variable is also definition unless the extern is used.
You must have one (and only one) definition of a variable in the program.
To have a global variable:
Declare it with the extern keyword in some header file.
Include this header in every TU that needs to use the variable. Never declare the variable directly, never bypass the header inclusion.
Define the variable: a declaration without the extern keyword will define the variable in one TU. You need to include the header file in the same TU to guaranty consistency between the extern declaration and the definition.
the way you initialise these DirectX objects is by passing the address
of the pointers as parameters into specialist methods
To solve this part of the problem, you could do something like this (in a .cpp file):
ID3D10Device* device;
struct Foo {
Foo(ID3D10Device **pdevice) { specialist_method(pdevice); }
};
Foo f(&device);
Beware of the "static initialization order fiasco", though -- it's safe to use device from main, or code called from main, because it will definitely be initialized before that code executes. It's not necessarily safe to use it from other initializers executed before main, because the order of initialization of statics in different translation units is unspecified (within a TU, they're initialized in order of either declaration or definition, I forget which). So device might still be a null pointer in that code. Likewise, specialist_method can't necessarily rely on other statics having been initialized.
There are extra tricks you can use if you need to enforce initialization orders, I'd guess that all the common ones are on SO already in other questions.
I have a number of C++ classes, alot of them (not all) share two "static size variables" e.g.
share.h
/*Other variables in this header used by all classes*/
static size width=10;//Used by about 60%
static size height = 12;//used by about 60%
So I placed them in a header file along with other objects that all classes share.
when I compile the project I get alot of warnings (from the classes which dont use these), which complain about them being defined and not used. But I need them there!
So I ask, is there a way to hash these to prevent such warnings?
Hashing them so that they can be defined! preventing warnings from classes calling this header file which dont require these last two variables, but they call header because they need everything else init
You should place them in an individual header file. So you can include it only in the classes they need it. This avoids the warning in the other classes. So in the end you will have two header files. One where the stuff for all classes is included and another where the variables which are not used in all are defined.
However try to avoid global variables.
Edit reading tune2fs' answer, I realized I may have interpreted the question wrong.
Perhaps you forgot to use extern in the header file? If you just include static definitions in the header file all compilation units will have unique copies, not shared. See also this explanation of static/extern
Edit Disambiguated in comments
static SomeClass NotUnusedInstance;
static void unused_vars_helper()
{
static SomeClass* take_address = &NotUnusedInstance;
}
This approach is design to have minimal impact (not invoking any actual code; take_address isn't actually initialized unless you call unused_vars_helper).
This should work pretty well for your case. You can make unused_vars_helper() static and/or move it inside an anonymous namespace to prevent external visibility of the helper.