C++ : const declrations duplicate symbols issue - c++

I want to avoid #defines in my code. Accordingly, I have the following in a header file :
#ifndef __GATENAMES__
#define __GATENAMES__
namespace GateNames
{
const char* CBSD_GATE_TO_SAS = "CbsdGate_SAS_INOUT";
const char* CBSD_GATE_TO_SAS_OUT = "CbsdGate_SAS_INOUT$o";
const char* CBSD_GATE_TO_SAS_IN = "CbsdGate_SAS_INOUT$i";
const char* SAS_GATE_TO_CBSD = "SasGate_CBSD_INOUT";
const char* SAS_GATE_TO_ESC = "SasGate_ESC_INOUT";
const char* SAS_GATE_TO_ESC_OUT = "SasGate_ESC_INOUT$o";
const char* SAS_GATE_TO_ESC_IN = "SasGate_ESC_INOUT$i";
};
#endif
This header file is included in various places in my code. However, the linker complains that the symbols are multiply defined:
../out/gcc-debug/src/CbsdSim.o:(.data.rel.local+0x0): multiple definition of `GateNames::CBSD_GATE_TO_SAS'
How can I get around this problem? Thanks.

Firstly, names like this __GATENAMES__, (any name starting with an underscore and an uppercase letter or containing two consecutive underscores) is reserved for the C++ implementation - you shgould not be creating such names in your own code.
Secondly, your constness is a bit mixed up, instead of things like:
const char* CBSD_GATE_TO_SAS = "CbsdGate_SAS_INOUT";
you want:
const char * const CBSD_GATE_TO_SAS = "CbsdGate_SAS_INOUT";
In other words, it's the pointer that's got to be const to limit the linkage of the pointer, not the thing pointed to (although in this case that also has to be const).

The reason for your linker errors is that non const variables have external linkage, and since you defined them in multiple translation units (by including a header), linker complains.
There are three ways to fix your code. First is to change linkage of your const char* string literals, by making them static. This way they have internal linkage, and each translation unit which includes them will not share them with other translation units - so also linker will not complain:
static const char* CBSD_GATE_TO_SAS = "CbsdGate_SAS_INOUT";
^^^^^^
Second one is to make them const:
const char* const CBSD_GATE_TO_SAS = "CbsdGate_SAS_INOUT";
^^^^^
which is actually quite similar to making them static, they have now internal linkage.
Third way is to declare them in header file and define in one single translation unit.:
// in header file
namespace GateNames
{
extern const char* CBSD_GATE_TO_SAS ;
};
in some .cpp file:
namespace GateNames
{
const char* CBSD_GATE_TO_SAS = "CbsdGate_SAS_INOUT";
};

Related

How to use info passed as parameter to build a string defined in other CPP file

I have a C++ program that has several .hpp files with declarations of variables (most of them paths to a NFS filesystem) and .cpp files with the definitions of those variables.
In some of those variables, whose type is std::string, I need to build its content by appending some content passed as parameter in the main program. For instance:
File constants.hpp:
namespace constants {
extern std::string cudnn_version;
extern const std::string path_caffe_cuda;
extern const std::string path_caffe_cuda_cudnn;
}
File constants.cpp:
const std::string constants::path_caffe_cuda = "/nfs/apps/caffe/cuda";
const std::string constants::path_caffe_cuda_cudnn = constants::path_caffe_cuda + "/cudnn" + constants::cudnn_version;
The content of constants::cudnn_version is asked to the user in the main program as parameter and updated there. The problem is, when constants::path_caffe_cuda_cudnn variable must be built with the content of constants::cudnn_version variable, its content is still empty, so in some way the variable path_caffe_cuda_cudnn is evaluated before the constants::cudnn_version has the content passed by user.
How do you think I could fix the issue?
Thank you very much to everybody.
It could easily be done by using a function instead:
namespace constants {
extern std::string cudnn_version;
extern const std::string path_caffe_cuda;
inline std::string path_caffe_cuda_cudnn()
{
return constants::path_caffe_cuda + "/cudnn" + constants::cudnn_version;
}
}
As long as path_caffe_cuda_cudnn is not called until constants::cudnn_version have been initialized, then it will be okay.

Problems with pointer to a class

I have defined my own class in a Qt project that gives me problems, but I think this could be general C++.
When I try to declare a pointer to my class in any include file in the project myHashMap like
myHashMap* map;
the compiler gives the error and say "multiple definition of map". What is this? I didn't even define a pointer even, just declared one.
However, putting myHashMap* map in a source file doesn't give any errors.
Here is the class definition and declaration, sorry that it looks a bit messy.
#include QObject
#include QHash
class myHashMap : public QObject
{
Q_OBJECT
public:
explicit myHashMap(QObject *parent = 0);
float Get(const QString& key) const;
void setValue(const char key, float value)
{
// only emit value changed if the value has actually changed
//if( hash->value(key) != value)
//{
hash->insert(key,value);
//emit ValueChanged(key, value);
//}
}
signals:
public slots:
//void ValueChanged(const char& key, float newValue);
private:
QHash<const char, float>* hash;
};
myHashMap::myHashMap(QObject *parent) :
QObject(parent)
{
hash = new QHash<const char,float>;
}
EDIT:
Aaa, bummer, I just forgot to prepend MyHashMap* with the extern keyword. Programming C++ is a nitty-gritty. Wouldn't it make sense if the compiler sent a warning here, like "hello there, you now got multiple definitions in a few headers, forgot an extern keyword?" ?
However, I got a new problem.
In "communication.h" I have declared a namespace like this
namespace reference
{
extern const char potentiometer;
extern const char KpAngle;
extern const char KiAngle;
extern const char KdAngle;
extern const int length;
extern myHashMap* hmap;
void init();
}
In the communication.cpp I want to use *hmap like below, but the compiler returns "undefined reference to hmap". Yet I have included the "communication.h" both in "communication.cpp" and in the *.pro file. See the code below:
#include "communication.h"
namespace reference
{
const char potentiometer = 0x05;
const char KpAngle = 0x06;
const char KiAngle = 0x07;
const char KdAngle = 0x08;
const int length =1;
//QList<const char>* summary = new QList<const char>();
void init()
{
//using namespace reference;
//#include "communication.h"
//myHashMap* hmap = new myHashMap();
//when using this a hmap is created, however setValue returns undefined ref
//Both methods below returns "undefine reference to hmap". Same if i try "using namespace"
//reference::hmap = new myHashMap();
//hmap = new myHashMap();
/*
reference::hmap->setValue(potentiometer,-1);
reference::hmap->setValue(KpAngle,-1);
reference::hmap->setValue(KiAngle,-1);
reference::hmap->setValue(KdAngle,-1);*/
}
}
EDIT: Above a pointer to hmap is only declared, not defined. I therefore had do define hmap by putting
myHashMap* hmap directly in the reference scope in "communication.cpp". Now it works.
Prefix the declaration with extern in your header file; i.e.:
extern myHashMap* map;
You will also need to put the variable definition (without extern) into a source file somewhere.
The reason this causes a problem is because header files are typically included by multiple source files. Each source file is compiled in its own translation unit, independently of all the other ones. If you put a variable declaration in a header file, then you'll get multiple translation units all trying to define the same variable, causing a conflict.
What you want is for the variable to exist only within a single translation unit, hence the declaration in one source file. The extern version just indicates that there is (or will be) a variable with that name and type, but it doesn't actually cause it to be created. It effectively just makes it visible to anything which sees it.
It is likely that the name "map" is already reserved by another variable or function in a library you are using.Also if you include the header in other headers that are all included in your main file you can get this error.

Class declaration in a header file and static variables

Noob question, but would like to understand the following:
Imagine I have a multifile project. I'm specifying a class in a header file to be shared among all the files in the project, and I write this : static int test = 0; and in the next line this: static const int MAX = 4;
The first one would be an error trying to compile because of the one definition rule. But the second one will compile without errors. Why?
From what I understand, both have the same properties: whole execution storage duration, class scope and no linkage.
Any help?
EDIT: testing an external constant declaration in a header: extern const int MAX = 4; to force external linkage produced the expected error. So I don't understand why with the variable it gives me the error and with the constant it doesn't.
Try
static const int test = 0;
I've sometimes noticed compiler errors with the immediate initialization of static const variables in the header file. You can always use the declaration in the header
class MyClass
{
// ...
static const int test;
// ...
}
and initialize it in the corresponding .cpp file
const int MyClass::test = 0;
This should work properly with any other types than int as well.
Integer constants in C++ don't actually occupy any space in the object and don't act like variables in general. Think about them more like numbers that are given names in this particular context.

Why can I declare an array in a header file, but not a pointer?

I have a problem with my Consts.hpp file:
#pragma once
#include <stdafx.h>
namespace consts {
const GLchar* TEXTURE_DIR = "../../blabla/"; // doesn't work
const GLchar TEXTURE_DIR[14] = "../../blabla/"; // works
};
This file is added to the stdafx.h file. ColladaReader.cpp is the file where I access TEXTURE_DIR.
Why does the pointer not work in the namespace? When accessing the constant I get this error:
Error 5 error LNK2005: "char const * const consts::TEXTURE_DIR" (?TEXTURE_DIR#consts##3PBDB) already defined in ColladaReader.obj D:\Privat\code\openglearn\projects\ColladaReader\stdafx.obj
The issue here is that if you put this code in a header file, every .cpp file that includes it will think that it has the one unique copy of consts::TEXTURE_DIR. This will cause a linker error when you compile the code, because the linker will find multiple copies of this variable and will not know which one it should use.
The reason why the first version doesn't work while the second one does is subtle. In C++, any constant at file scope automatically has internal linkage and therefore sidesteps the above problem (since the linker treats each copy as separate). When you declared the array, your array itself is a constant:
const GLchar TEXTURE_DIR[14] = "../../blabla/";
However, the pointer you declared is not a constant:
const GLchar* TEXTURE_DIR = "../../blabla/";
The reason for this is that the pointer points at GLchars that are constant, but the pointer itself can be reassigned. For example, the line
consts::TEXTURE_DIR = "Now I'm different!"
will compile just fine.
To fix this, change the pointer declaration so that it is a constant pointer:
const GLchar* const TEXTURE_DIR = "../../blabla/";
Note the second const here, which means that the pointer cannot be reassigned. This should resolve the linker error.
Hope this helps!

external array definition

I would like to define array of strings in different cpp file, but there seems to be some discrepancy between definition and declaration when I try to make pointer (array element) also const. Using the same definition as declaration seems to work fine, so I suspect initialization is not an issue. In the code below I have commented out offending const - so it will compile, but if const is un-commented, linker (tested with g++ 4.6 & VS10) will not find ext_string_array.
main.cpp:
#include <iostream>
const char* const string_array[2] =
{
"aaa",
"bbb"
};
extern const char* /*const*/ ext_string_array[2]; // <- offending const
int main()
{
std::cout << string_array[0];
std::cout << ext_string_array[0];
}
definition.cpp:
const char* /*const*/ ext_string_array[2] = // <- offending const
{
"aaa",
"bbb"
};
In this context const also means static, unless you also specify extern. Change your .cpp file to this
extern const char* const ext_string_array[2] =
{
"aaa",
"bbb"
};
C++ 2003, 3.5 Program and Linkage, 3:
A name having namespace scope (3.3.5) has internal linkage if it is the name of
[...]
— an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage; [...]
So you need an explicit extern in the declaration..