I have an header file that i put some global const variables. Now I need another project wide modifiable variable. But it gives linker error as expected. I am trying to solve the issue without using inline keyword. My codes:
constants.h:
#ifndef CONSTANTS_H
#define CONSTANTS_H
namespace constants {
bool myVar;
}
#endif // CONSTANTS_H
I am using this variable in my classes with constants::myVar. And I got a linker error. Any solution without using inline (for backward compiler compability)?
I completely agree with the comments above about looking at alternatives for this design.
Also as you seems to be aware, c++17 inline variables offer a better solution.
Having said that, if you must keep the current design - you can do the following:
Change the header file to declare myVar as extern:
namespace constants {
//--vvvvvv------------
extern bool myVar;
}
Add constants.cpp file (if it doesn't exists yet), with a definition for myVar (preferably with some initialization):
namespace constants {
bool myVar;
// or better: something like:
bool myVar = false;
}
Related
I try to use a global variable as elements for an array.
The problem is that the compiler want to know the integer constant value while declaring an array. I thought that my global variable is allready a const int???
global header file "constants.h"
#ifndef CONSTANT_H
#define CONSTANT_H
namespace constants{
extern const int MY_ROW;
extern const int MY_COLUMN;
}
#endif // CONSTANT_H
define global "constants.cpp"
#include "constants.h"
namespace constants{
const int MY_ROW{55};
}
main function "main.cpp"
#include "constants.h"
#include <iostream>
#include <array>
int main()
{
std::cout<<constants::MY_ROW<<std::endl;
int my_int_array[constants::MY_ROW];
return 0;
}
So far everything is going well, I can declare an array in main using globals as elements.
But if I try "the same thing" in another header the compiler complains.
"test.h"
#ifndef TEST_H
#define TEST_H
#include "constants.h"
class Test
{
public:
Test();
~Test();
void display_array();
private:
int test_array[constants::MY_ROW]; //error here???
};
#endif // TEST_H
error message:
**error: array bound is not an integer constant before ']' token|**
I appreciate it if someone can bring light to darkness.
In main(), for int my_int_array[constants::MY_ROW]; the value of MY_ROW is not known to the compiler, as it is not resolved until the linker stage, so my_int_array cannot be allocated at compile-time, only at runtime, and only if you are using a compiler that supports Variable Length Arrays (see Why aren't variable-length arrays part of the C++ standard?).
But for class Test, that is simply not an option. You can't use a VLA in a class, since the compiler needs to know up front the full size of all members in order to setup memory storage for instances of the class. But the size of the array is not know to the compiler, so the class cannot be compiled.
To do what you are attempting, you need to initialize the constants directly in their declarations, not split them up with extern, eg:
#ifndef CONSTANT_H
#define CONSTANT_H
namespace constants{
const int MY_ROW = 55;
const int MY_COLUMN = ...;
}
#endif // CONSTANT_H
See Define constant variables in C++ header
As noted by Remy, the issue is that the compiler can't see the value of MY_ROW when main.cpp or any file
#includeing test.h is compiled.
If you're using C++17, there's a fairly elegant fix for this. The goal is to make the value MY_ROW visible when main.cpp is compiled, so we need to move the definition of MY_ROW to constants.h:
#ifndef CONSTANT_H
#define CONSTANT_H
namespace constants{
constexpr int MY_ROW{55};
// ...
}
#endif // CONSTANT_H
Note that I swapped const with constexpr to ensure that MY_ROW is a compile time constant rather than a runtime constant.
This version of constants.h will allow your program to compile, but its still not ideal. The issue is that every file that includes constants.h will receive its own MY_ROW definition with static linkage. This means that we may end up with multiple MY_ROWS being stored in the final executable, all with different addresses.
Luckily, in C++17, inline variables were introduced, which essentially let us provide multiple definitions for a single variable with external linkage:
#ifndef CONSTANT_H
#define CONSTANT_H
namespace constants{
inline constexpr int MY_ROW{55};
// ...
}
#endif // CONSTANT_H
With this implementation, you are free to include constants.h in multiple translation units and the linker will ensure that only one MY_ROW makes it to the final executable. You are also now free to use MY_ROW wherever a compile time constant is needed, so your declarations of my_int_array in main.cpp and test_array in test.h would now be valid.
I know global is bad but just as a practice, is this the correct way to initialize a global class used between multiple object files?
Header 1.h
class test {
int id;
public:
test(int in){
id = in;
}
int getId(){
return id;
}
};
extern test t;
File 1.cc:
#include <iostream>
#include "1.h"
int main(){
std::cout << t.getId() << std::endl;
return 0;
}
File 2.cc:
#include "1.h"
test t(5);
Now what if instead of extern I use the static approach globally static test t(0); in the header?
Correct me if I'm wrong but that would compile fine however I would have 2 different unrelated copies of the same t in both object files and the final binary? Is that bad? Or does the linker sort it out to eliminate multiple copies?
There are global instances, not global classes.
What you have is a global instance. And yes, this sounds about right, until you get to multiple global instances which depend upon each other. Then the real fun will start.
Defining a variable as 'static' at global level means the variable will be defined in the compilation unit only (i.e. the '.o' file) and the symbol won't be exported by the compiler.
In other words: yes, there will be multiple variables with the same name but only visible to functions on the same compilation unit.
Besides, 'invisible' doesn't mean 'inaccessible'. You still can provide access to the variable. For example:
1.h
struct Test { int value; }; // Class definition
Test& get_t(); // Function declaration
1.cc
#include "1.h"
static Test t; // Variable declared as 'static'
Test& get_t() { return t; };
2.cc
#include "1.h"
#include <iostream>
int main()
{
std::cout << get_t().value << std::endl; // Static variable accessed
}
I use the static approach globally static test t;?
But your test class needs an int in parameter for the constructor, so you want:
static test t(0); // or whatever int you want
If you turn the extern to static in the header, you would define a static variable in each compilation unit in which the header is imported. So classes in different cpp files would no longer "communicate" via t, since each would have theirs. This is very error prone.
In addition, adding the definition of a static in a header is an extremely bad practice. When someone includes a header, one does not expect that it will create variables.
Including the declaration of t as extern is an acceptable practice. But be aware that if the header has a general purpose, this might reduce its reusability other projects.
More information of interest for you:
Must read: C++ Core Guidelines about source files
Must read: Guidelines for writing headers
StackOverflow: When are global variables recommended
Why global variables should be avoided if possible and when are they ok
If you put a variable declaration outside of any function, you're declaring the variable as 'global'. Ex:
1.cc
int this_is_global;
From here on you can use the variable in any function of '1.cc'.
For using the same variable in any other file, the compiler will need to know about it:
2.cc
extern int this_is_global;
Here, the keyword extern tells the compiler that the variable is declare somewhere else, letting the task of finding it to the linker.
If you miss to add the extern keyword here, the compiler will treat it as a new variable, and the linker will have two variables with the same name and will emit an error. All of your source files of your project except the first one will need the extern keyword to avoid duplicate symbols.
So common practice is to add the 'extern' declaration in an include file:
1.cc
int this_is_global;
1.h
extern int this_is_global;
2.cc
#include "1.h"
On the other side, the static keyword tells the compiler not to export the symbol. In other words: the variable will exists only in the source file it is declared. You could declare it once per source file and there will be different variables with the same name. Ex:
1.h
static int my_var;
1.cc
#include "1.h"
2.cc
#include "1.h"
This way, you'll end having two variables 'my_var' and changes to any of them won't affect the other.
What I want to do is just to define a variable in a header file and use it on two different cpp files without redefinition that variable each time I include that header
Here is how I tried :
Variables.h
#ifndef VARIABLES_H // header guards
#define VARIABLES_H
static bool bShouldRegister;
#endif
(I also tried extern but nothing changed)
And in a cpp file I give it a value ::bShouldRegister = true or bShouldRegister = true;
In my another cpp file I check it's value by creating a thread and check its value in a loop (and yes my thread function works well)
while (true)
{
if (::bShouldRegister) // Or if (bShouldRegister)
{
MessageBox(NULL,"Value Changed","Done",MB_OK|MB_ICONINFORMATION);
}
Sleep(100);
}
And yes, that MessageBox never appears (bShouldRegister never gets true :/)
You should use extern otherwise you will have separated bShouldRegister variables in each translation unit with probably different values.
Put this in a header file (.h):
extern bool bShouldRegister;
Put this in one of implementation files (.cpp):
bool bShouldRegister;
Another way which is simpler is to use inline keyword. Put you variable in a header file as below:
inline bool bShouldRegister;
If you can use C++17, consider using an inline variable:
// in a header file
inline bool bShouldRegister = true;
See How do inline variables work? for more information.
A more C++-like way would be using a class member, syntactically indicated by the static keyword. Class member variables have implicit external linkage.
#ifndef VARIABLES_H
#define VARIABLES_H
class RegUtil {
public:
static bool bShouldRegister;
};
#endif
in one of your cpp files (maybe variables.cpp), you have to define this class member:
#include "variables.h"
bool RegUtil::bShouldRegister;
You need to define the variable in one of the modules:
bool bShouldRegister;
Then declare it extern (not static!) in the header:
extern bool bShouldRegister;
Here you need to define bool bShouldRegister in one class. Your header file should like this.
#ifndef VARIABLES_H // header guards
#define VARIABLES_H
class abc{
public:
bool bShouldRegister;
abc();
#endif
Now initialize bShouldRegister variable in cpp file in constructor of abc class and then use this variable in your second cpp file using object of class. You will get your message box.
Still relatively new to C++
I have a header variable with a namespace with a few constants that looks something like this
namespace blah {
const std::string x="foo";
}
I have no problems accessing the variables this way - dosomething(blah::x); etc. Now I want to change the variable so it can be modified. If I just take out the const I get a linker error "duplicate symbol blah::x". Adding an extern here doesn't help:
namespace blah {
extern std::string x;
}
It says that extern is enabled by default and I get the same duplicate symbol error. Whats the right way to do this?
(EDIT in the latter case, I don't want to set the variable value yet. I want to derive it elsewhere and share the value. To clarify - I want to get rid of the const so I can change the value (e.g. using command line arguments. When I get rid of the const is when I get the errors about duplicate symbols.)
In the header file, you should declare the variable:
//blah.h
namespace blah {
extern std::string x; //declaration
}
And in the source file, you should define it as:
//blah.cpp
#include "blah.h"
namespace blah {
std::string x="foo"; //definition
}
Now include blah.h in as many file as you want. You wouldn't see any redefinition error.
If you want to get rid of const (as you said in the comment), then simply remove it, as I did.
Declare the extern in a header file:
code.h
namespace blah {
extern std::string x;
};
...and define it in a single CPP file...
code.cpp
std::string blah::x = "foo";
Create a header file that declares your extern:
namespace blah {
extern std::string x;
}
Then create a source file that contains the definition:
namespace blah {
std::string x="foo";
}
This way the definition gets compiled once in a lib (which you will link with), and won't be defined multiple times (everywhere you include the header).
Is declaring extern const or just extern the same thing in a header file? Also will both give external linkage?
globals.cpp
#include <string>
extern const std::string foo = "bar";
globals.h
#ifndef GLOBALS_H
#define GLOBALS_H
#include <iostream>
extern const std::string foo;
#endif /* GLOBALS_H */
OR
globals.h
#ifndef GLOBALS_H
#define GLOBALS_H
#include <iostream>
extern std::string foo;
#endif /* GLOBALS_H */
Both compiles and run fine, both give same address when used in multiple files, which one is more correct?
This one is correct,
//globals.h
extern const std::string foo; //Consistent
Consistent!
The correct one is
extern const std::string foo;
foo is a constant string in the source file it is defined in, so the extern declaration should indicate this too and allow the compiler to catch instances where code within source file where that header is included may try to modify foo.
The only reason they both compile fine is that you are not actually using foo, so it doesn't matter whether the linker can find foo or not (it never tries to). (Or your compiler/linker is rubbish. :) )
The consistent header is fine. The one missing the const is not and will/should result in errors if something tries to use foo.
If you make some file (not globals.cpp) which includes globals.h and tries to use foo (e.g. just add a line with "foo.c_str();" into a function) then you'll get a link error because no const std::string foo could be found.
This is how it has to be, too. The header is telling anything that wants to use foo that it can only read it, not modify it. Things which include the globals.h header have no idea of what is inside globals.cpp; if you want them to treat an object as const you have to put that information in a file which they include.
You can give const external linkage, as in your example, but it's still non-modifiable (constant).
P.S. you need to include <string>, not <iostream>
P.P.S. If I wasn't clear, you can't redefine const-ness. This will not compile:
extern std::string foo;
extern const std::string foo = "bar";
For C language, declare actual const in a .c file and use 'extern' for it in a header (.h) file. This should be as usual as we do when declaring a global variable.