extern undefined reference error when declaring variable - c++

I have 2 header files and 1 source file to work with. Respectively: Utility.h, Game.h and main.cpp. I have declared extern variables in Utility.h and am trying to define them in main.cpp. This gives me an undefined reference error, which I don't understand. I use the variable AFTER I give it a value, so that should be fine?
Utility.h:
#pragma once
#include <string>
#include <fstream>
#include <SDL.h>
namespace Utility {
extern std::string BIN_PATH;
extern std::string ROOT_PATH;
extern std::string EXE_PATH;
// Omitted rest of namespace.
}
main.cpp
#include "Game.h"
int main(int argc, char * argv[]) {
// Get the necessary paths
std::string path = SDL_GetBasePath();
#ifdef _WIN32
// TODO check if working on different windows systems
// exePath = path;
// binPath = path.substr(0, path.find_last_of('\\'));
// rootPath = binPath.substr(0, binPath.find_last_of('\\'));
#elif __LINUX__
Utility::BIN_PATH = path.substr(0, path.find_last_of('/'));
Utility::ROOT_PATH = Utility::BIN_PATH.substr(0, binPath.find_last_of('/'));
// TODO check if working on different linux systems
#endif
std::cout << "BinPath: " + Utility::BIN_PATH << std::endl;
std::cout << "RootPath: " + Utility::ROOT_PATH << std::endl;
// Omitted rest of source.
}
I am including Utility.h in Game.h, and Game.h in main.cpp. Shouldn't that put the extern definitions above my main.cpp source when linking?

To simplify (rather complicated) rules a bit, each variable (as well as other entities) must be defined once and only once in the whole program. It can be declared multiple times. It is important to understand what is a declaration and what is a definition.
extern std::string var; // in namespace scope
Is a declaration of string variable var. var is not yet defined. You need to define it somewhere else - only once - by using
std::string var; // in namespace scope!
In your code, you do not define the variable in function main - instead, you are assigning the value to it. But the variable needs to be defined.

The lines
Utility::BIN_PATH = path.substr(0, path.find_last_of('/'));
Utility::ROOT_PATH = Utility::BIN_PATH.substr(0, binPath.find_last_of('/'));
don't define the variables. They just assign values to them. To define them, use:
std::string Utility::BIN_PATH;
std::string Utility::ROOT_PATH;
in the global scope. Add similar line for EXE_PATH.
std::string Utility::EXE_PATH;
You can also define them using
namespace Utility
{
std::string BIN_PATH;
std::string ROOT_PATH;
std::string EXE_PATH;
}
Make sure that those lines appear in a .cpp file, not in a .h file.

Related

How can I declare a global compile-time constant for std::array?

I am trying to implement a global variable that will be able to be used by different files and at the same time with std::array, but I get the following compiler error:
error: the value of ‘constants::HEIGHT’ is not usable in a constant expression
note: ‘constants::HEIGHT’ was not initialized with a constant expression
My code is split in the following files at the moment:
main.cpp
#include <iostream>
#include "classA.h"
#include "globals.h"
namespace constants {
extern const int WIDTH = 800;
extern const int HEIGHT = 600;
}
int main()
{
ClassA classA;
printf("Hello World");
std::cout << constants::WIDTH << " " << constants::HEIGHT << std::endl;
return 0;
}
classA.h
#include <array>
#include "globals.h"
class ClassA {
public:
std::array<int, constants::HEIGHT> m_arrayFixedRGBA;
ClassA();
};
classA.cpp
#include "classA.h"
ClassA::ClassA() {
}
globals.h
#ifndef CONSTANTS_H
#define CONSTANTS_H
namespace constants {
extern const int WIDTH;
extern const int HEIGHT;
}
#endif
I know that by removing extern, declaring the values in globals.h like this
#ifndef CONSTANTS_H
#define CONSTANTS_H
namespace constants {
const int WIDTH = 800;
const int HEIGHT = 600;
}
#endif
and removing the relevant lines in main.cpp, then the program is able to compile.
While this is simple (and fine for smaller programs), every time globals.h gets #included into a different code file, each of these variables is copied into the including code file. Therefore, if globals.h gets included into 20 different code files, each of these variables is duplicated 20 times. Header guards won’t stop this from happening, as they only prevent a header from being included more than once into a single including file, not from being included one time into multiple different code files. This duplication of variables isn’t really that much of a problem (since constants aren’t likely to be huge), but changing a single constant value would also require recompiling every file that includes the constants header, which can lead to lengthy rebuild times for larger projects.
What could be a work-around for this scenario?
You could define your constants as static constexpr members
// in some header file:
struct constants {
constexpr static int width = 800;
constexpr static int height = 600;
};
and use them like
std::array<int, constants::height> arr;
------ EDIT ------
Note that this approach only declares these compile-time constants, but does not define any variable. Thus there is no problem with multiple definitions confusing the linker (as with your implementation using extern variables).
However, before C++17 the opposite problem can occur: if you odr-use these constants, a link-time error will arise, as the linker cannot find a definition. For example the following code will fail
std::cout << constants::width << std::endl;
since operator(std::ostream&, something const&) takes the object to be written by reference. You can avoid this by either providing a definition somewhere (in a source file), or by avoiding such use, e.g.
std::cout << int(constants::width) << std::endl;

Why do I have a muliply defined ofstream object? [duplicate]

I have been trying to use extern in order to use variable that is previously defined.
I have not used extern before and now I need to use it in order to define variable just once and use them across multiple files
I have written minimized version of code for this question. I have four files
lib.h
#ifndef LIB_H
#define LIB_H
#include <iostream>
namespace lib {
extern bool initialized;
bool initialized = false;
static void isInit(char* parent) {
std::cout << "Library for [" << parent << "] initialized? " << (::lib::initialized ? "yes" : "no") << "\n";
}
} // namespace lib
#endif
vehicle.h
#ifndef _VEHICLE_H
#define _VEHICLE_H
#include <string>
class Vehicle {
public:
Vehicle(const std::string& manufacturer,
const std::string& model,
int year);
std::string manufacturer;
std::string model;
int year;
};
#endif
Following is implementation of vehicle.h file called vehicle.cpp
#include "vehicle.h"
#include "lib.h"
Vehicle::Vehicle(const std::string& manufacturer,
const std::string& model,
int year) :
manufacturer(manufacturer),
model(model),
year(year) {
::lib::isInit("Vehicle");
}
main.cpp
#include "vehicle.h"
#include "lib.h"
int main(int argc, char** argv) {
::lib::isInit("main");
::lib::initialized = true;
::lib::isInit("main");
Vehicle vehicle("Toyota", "Corolla", 2013);
return 0;
}
I am using g++
g++ -Wno-write-strings main.cpp vehicle.cpp -o bin/main.cpp.bin
I get following errors:
/tmp/cclVpsgT.o:(.bss+0x0): multiple definition of `lib::initialized'
/tmp/ccmJKImL.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
I have checked the output of:
g++ -Wno-write-strings main.cpp vehicle.cpp -E
multiple definition occurs every time lib.h is included.
My questions are:
Why is lib.h included multiple times when define guard is there
How would I define 'extern' variable and initialize it in the same file (since it's used in the same file later)
Why is lib.h included multiple times when define guard is there
You need to remove the definition:
bool initialized = false;
And put it in one and only one source file.
Include guards prevent the same header file from getting included more than once in the same translation unit(TU) not in different translation units.
You define the variable initialized in header file which gets included across different translation units and then each TU has a symbol named initialized which breaks the one definition rule.
How would I define 'extern' variable and initialize it in the same file (since it's used in the same file later)
If you want the variable to be used in the same file, why make it extern? You need to use extern when you want to share the same variable accross different TUs.
If you need to use it at global scope in only single TU, You should simple put it inside a unnamed namespace.

Global const object shared between compilation units

When I declared and initialized a const object.
// ConstClass.h
class ConstClass
{
};
const ConstClass g_Const;
And two cpp files include this header.
// Unit1.cpp
#include "ConstClass.h"
#include "stdio.h"
void PrintInUnit1( )
{
printf( "g_Const in Unit1 is %d.\r\n", &g_Const );
}
and
// Unit2.cpp
#include "ConstClass.h"
#include "stdio.h"
void PrintInUnit2( )
{
printf( "g_Const in Unit2 is %d.\r\n", &g_Const );
}
When i build the solution, there was no link error, what you will get If g_Const is a non-const fundamental type!
And PrintInUnit1() and PrintInUnit2() show that there are two independent "g_Const"s with different address in two compilation units, Why?
==============
I know how to fix it.(use extern keyword to declaration, and define it in one cpp file.)
I wonder to know why I did't get redfined link error in this sample.
https://stackoverflow.com/a/6173889/1508519
const variable at namespace scope has internal linkage. So they're
basically two different variables. There is no redefinition.
3.5/3 [basic.link]:
A name having namespace scope (3.3.5) has internal linkage if it is
the name of
— an object, reference, function or function template that is
explicitly declared static or,
— an object or reference that is explicitly declared const and neither
explicitly declared extern nor previously declared to have external
linkage; or
— a data member of an anonymous union.
Use extern if you want it to have external linkage.
As stated in the other answer, header files are just pasted in cpp files. The same header file is included in both cpp files, but they are separate translation units. That means that one instance of a variable is different from the other instance. In other to let the compiler know that you have defined the variable elsewhere, use the extern keyword. This ensures only one instance is shared across translation units. However extern const Test test is just a declaration. You need a definition. It doesn't matter where you define it as long as it is defined once in some cpp file. You can declare it as many times as you want (which is convenient for placing it in a header file.)
So for example:
Constant.h
class Test
{
};
extern const Test test;
Unit1.cpp
#include "Constant.h"
#include <iostream>
void print_one()
{ std::cout << &test << std::endl; }
Unit2.cpp
#include "Constant.h"
#include <iostream>
void print_two()
{ std::cout << &test << std::endl; }
main.cpp
extern void print_one();
extern void print_two();
int main()
{
print_one();
print_two();
}
Constant.cpp
#include "Constant.h"
const Test test = Test();
Makefile
.PHONY: all
all:
g++ -std=c++11 -o test Constant.cpp Unit1.cpp Unit2.cpp main.cpp
Because you put variable definition in header file. Including header file is just like replacing it with the content of the file. So, the first file:
// Unit1.cpp
#include "ConstClass.h" // this will be replace with the content of ConstClass.h
#include "stdio.h"
void PrintInUnit1( )
{
printf( "g_Const in Unit1 is %d.\r\n", &g_Const );
}
will become (after preprocessing phase before compiling):
// Unit1.cpp
// ConstClass.h
class ConstClass
{
};
const ConstClass g_Const;
//this line is replaced with the content of "stdio.h"
void PrintInUnit1( )
{
printf( "g_Const in Unit1 is %d.\r\n", &g_Const );
}
And the second file will be:
// Unit2.cpp
// ConstClass.h
class ConstClass
{
};
const ConstClass g_Const;
//this line is replaced with the content of "stdio.h"
void PrintInUnit2( )
{
printf( "g_Const in Unit2 is %d.\r\n", &g_Const );
}
As you can see, each file has separate variable g_Const (this is just for the case of your code in here, there maybe no variable at all just like macro, see explanation in my last paragraph).
If what you want is not the definition of the variable just the declaration in the header file, you should use extern keyword in the header file:
extern const ConstClass g_Const;
Then you can put the definition of g_Const variable in ConstClass.c
There is some catch in your code:
there is no constant value assigned in your g_Const definition, you must assign it a constant value in the definition unless you want the default value (0).
inside printf, you take the address of const variable of C++. This actually force the compiler to create the variable in stack. If you don't take the address it may be able to infer a compile time number behaving like macro in C (you can get the magic number directly put in the code where you use the const variable).

extern variable causes multiple definition error

I have been trying to use extern in order to use variable that is previously defined.
I have not used extern before and now I need to use it in order to define variable just once and use them across multiple files
I have written minimized version of code for this question. I have four files
lib.h
#ifndef LIB_H
#define LIB_H
#include <iostream>
namespace lib {
extern bool initialized;
bool initialized = false;
static void isInit(char* parent) {
std::cout << "Library for [" << parent << "] initialized? " << (::lib::initialized ? "yes" : "no") << "\n";
}
} // namespace lib
#endif
vehicle.h
#ifndef _VEHICLE_H
#define _VEHICLE_H
#include <string>
class Vehicle {
public:
Vehicle(const std::string& manufacturer,
const std::string& model,
int year);
std::string manufacturer;
std::string model;
int year;
};
#endif
Following is implementation of vehicle.h file called vehicle.cpp
#include "vehicle.h"
#include "lib.h"
Vehicle::Vehicle(const std::string& manufacturer,
const std::string& model,
int year) :
manufacturer(manufacturer),
model(model),
year(year) {
::lib::isInit("Vehicle");
}
main.cpp
#include "vehicle.h"
#include "lib.h"
int main(int argc, char** argv) {
::lib::isInit("main");
::lib::initialized = true;
::lib::isInit("main");
Vehicle vehicle("Toyota", "Corolla", 2013);
return 0;
}
I am using g++
g++ -Wno-write-strings main.cpp vehicle.cpp -o bin/main.cpp.bin
I get following errors:
/tmp/cclVpsgT.o:(.bss+0x0): multiple definition of `lib::initialized'
/tmp/ccmJKImL.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
I have checked the output of:
g++ -Wno-write-strings main.cpp vehicle.cpp -E
multiple definition occurs every time lib.h is included.
My questions are:
Why is lib.h included multiple times when define guard is there
How would I define 'extern' variable and initialize it in the same file (since it's used in the same file later)
Why is lib.h included multiple times when define guard is there
You need to remove the definition:
bool initialized = false;
And put it in one and only one source file.
Include guards prevent the same header file from getting included more than once in the same translation unit(TU) not in different translation units.
You define the variable initialized in header file which gets included across different translation units and then each TU has a symbol named initialized which breaks the one definition rule.
How would I define 'extern' variable and initialize it in the same file (since it's used in the same file later)
If you want the variable to be used in the same file, why make it extern? You need to use extern when you want to share the same variable accross different TUs.
If you need to use it at global scope in only single TU, You should simple put it inside a unnamed namespace.

I can access a static fstream from one file only

I need to use a static fstream in multiple source files. However I can only use it from a single file and not from the others. Furthermore, its use in the other files does not give any error, it just does nothing.
This is the code:
/// log.h
#ifndef LOG_H
#define LOG_H
#include <fstream>
static std::ofstream ofs;
#define LOG(level) ofs << level << ": "
#endif
/// test.cpp
#include "log.h"
#include "other.h"
int main()
{
ofs.open("file.log");
LOG(5) << "Test log 1" << std::endl; // OK
OtherFunc();
}
/// other.h
#ifndef OTHER_H
#define OTHER_H
extern int OtherFunc();
#endif
/// other.cpp
#include "other.h"
#include "log.h"
int OtherFunc()
{
LOG(5) << "Test log 2" << std::endl; // Nothing
}
This is the generated file:
5: Test log 1
Thank you!
Platform:
Linux
g++ 4.5.1
Static global variables: variables declared as static at the top level of a source file (outside any function definitions) are only visible throughout that file ("file scope", also known as "internal linkage").
The default storage class for global variable is extern which has external or whole program linkage. So if you want to use the global variable in multiple files it must be extern.
static here means you're explicitly asking the compiler to give the file-scope variable std::ofstream ofs static linkage, which means it is visibly only in the current file.
The twist is that you're doing this in a header, which means every .cpp file including the header gets its own distinct instance of std::ofstream ofs. Only because you've given it static linkage can they all have distinct file-scope variables with the same name - otherwise there would be a name clash.
So, in main.cpp, you open your local ofs and write to it.
In other.cpp, you have your own local copy of ofs, but never open it ... so the output doesn't go anywhere, and certainly not to file.log.
The other answers are correct, that changing the header declaration to extern std::ofstream ofs; will allow all the .cpp files to share a single object called ofs, and then you just need to put the instance in exactly one place (main.cpp would be fine).
It might be simpler, and cleaner, to make the LOG(level) an out-of-line function call though; then the output stream could live in a log.cpp with the function definition, and nobody else needs to see it.
I need to use a static fstream in multiple source files.
If you need to reference the same variable from multiple source files, that isn't static linkage, that is extern linkage.
Put this in your header:
extern std::ofstream ofs;
Put this in exactly one of your CPP files:
std::ofstream ofs;