external array definition - c++

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..

Related

C++ : const declrations duplicate symbols issue

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";
};

C++ Linker complains multiple definition of char* but not std::string

In a big project, I have a .h file which defines a lot of constants within a namespace. Introducing const char* constants results in linker errors complaining of multiple definitions.
ex.h
#include <string>
namespace Dv
{
const int MAX = 10;
const std::string NAME = "bobo";
const char* NAME2 = "fred"; // <-- ERROR: multiple definition of `Dv::NAME2'
}
exA.cpp
#include "ex.h"
void aFunction() { printf("this is aFunction\n"); }
exB.cpp
#include "ex.h"
void aFunction(void);
int main(int argc, char **argv)
{
aFunction();
return 0;
}
Compile and Link
g++ -c exA.cpp
g++ -c exB.cpp
g++ exA.o exB.o -o ex
exB.o:(.data+0x0): multiple definition of `Dv::NAME2'
exA.o:(.data+0x0): first defined here
collect2: ld returned 1 exit status
The linker error happens if NAME2 is included in the .h file. Why?
If MAX is also a POD (like char[]?), why does the linker not complain of multiple int MAX definitions? (or multiple std::string NAME definitions for that matter?)
I don't understand what's special about char[] in this situation.
Thanks
According to the C++ Standard (3.5 Program and linkage)
3 A name having namespace scope (3.3.6) has internal linkage if it is
the name of
— a non-volatile variable that is explicitly declared const or constexpr and neither explicitly declared extern nor previously
declared to have external linkage; or
and
4 An unnamed namespace or a namespace declared directly or indirectly
within an unnamed namespace has internal linkage. All other
namespaces have external linkage. A name having namespace scope that
has not been given internal linkage above has the same linkage as the
enclosing namespace if it is the name of — a variable; or
In this namespace definition
namespace Dv
{
const int MAX = 10;
const std::string NAME = "bobo";
const char* NAME2 = "fred"; // <-- ERROR: multiple definition of `Dv::NAME2'
}
(the only) variable NAME2 is not a constant and hence has external linkage.
You could make it as having internal linkage defining it for example with keyword static. For example
namespace Dv
{
const int MAX = 10;
const std::string NAME = "bobo";
static const char* NAME2 = "fred";
}
Or could define it as a constant
namespace Dv
{
const int MAX = 10;
const std::string NAME = "bobo";
const char * const NAME2 = "fred";
}

global const variable definition - access through extern in c++

I read some answers about this topic, but I am still not sure:
In C++ a global const variable definition is automatically static. However I can access it from another cpp-file through extern:
// module.cpp
const int i = 0;
and
// main.cpp
extern const int i;
int main ()
{
if (i > 10)
return 0;
else
return 1;
}
Why is this possible (accessing an object with internal linkage from another module)? Normally I should be forced to define i as extern const int i = 0 in module.cpp, to have an explicitely global but non-static const, or?
In comparison, this is not possible:
// module.cpp
static int i = 0;
and
// main.cpp
extern int i;
int main ()
{
i = 10; // but read-only access like (i > 10) would be possible!
return 0;
}
So is the answer that: yes, you can access internal linked objects from other modules, but only for read (so always with const)?
Edit:
Sorry, but I made a mistake: in my original code I just tried an expression without effect (in both examples):
extern const int i; // or extern int i for second example
int main ()
{
i>10;
return 0;
}
I thought, that it behaves the same, as if program flow or data was dependent of this expression, but in fact it does not! The compiler seems to just cut out this effectless expression, so that the linker does not see it! So all is alright: in the first example i must be indeed defined extern const int i = 0 in module.cpp, and in the second example i cannot be accessed at all (unless in effectless expression). Compiler is VC++2010.
Edit2:
However, now I don't understand why this is possible:
// module.cpp
extern const int i = 0;
and
// main.cpp
int i = 99;
int main ()
{
bool b = i>10;
return 0;
}
i have both external linkage. But no error. When in module.cpp I define int i = 0, then error (multiple symbols). Why?
As for me it looks like a compiler bug. Constant variable i is not defined It is only declared in main.cpp. As for variable i in module.cpp then it has internal linkage and shall not be accessible outside the module.
Relative to your addition to the original post then the compiler has nothing common with this situation. It is linker that checks whether there are duplicate external symbols. I think it decided that if one variable has qualifier const and other has no it then there are two different variables. I think that it is implementation defined whether the linker will issue an error or not. Moreover it can have some options that could control the behaviour of the linker in such situations.

static ints, compilation units and the ternary operator

// SomeCls.h
class SomeCls
{
static const int PERIOD_ALARM_NORMAL = 5;
static const int PERIOD_ALARM_THRESH = 1;
void method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
} obj;
It is going to build ok. Now take out the method() implementation and place it in a cpp file:
//SomeCls.cpp
#include "SomeCls.h"
void SomeCls::method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
Why does mr. linker say
undefined reference to SomeCls::PERIOD_ALARM_NORMAL' undefined
reference toSomeCls::PERIOD_ALARM_THRESH'
?
Thanks
EDIT:
It seems to me that that inside .h, the ternary operator takes static const ints it as rvalues but ... outside the decalrative .h, it regards them as lvalue and needs definition.
This is what I have managed to understand from the answers below. Kudos to Bada compiler (some eabi linux thinggie)
If the compiler can't see all the static class constants' values, then you have to provide definitions for them so that they'll actually be stored somewhere. Add the following to your cpp file:
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
This is a GCC limitation, but it's completely standard comforming. Technically a static const int is still an lvalue. You've provided the value inline so compiler will almost always use it as an rvalue. There is one exception. The abstract instructions emitted by the compiler for ternary operators queries the address of lvalues. Hence the error you're seeing.
You can work around this by using enum instead. Or if you're using a new version of GCC constexpr was added to standard to fix this exact problem (named and typed rvalues).
Alternatively you can provide the linker with a definition for the constants. E.g. in your classes cpp file add the a line like
// I wish I had constexpr
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
As a side note: I was a staunch proponent of static const for class scope constants. Then I found out that MSVC doesn't allow for static const float with the value inline. So the only values you can portably put in a static const are integers, in which case enums provide all the same features plus the guarantee that they'll never silently convert to an lvalue.
If, for whatever reason, you compiler simply refuses to link the code (like GCC 4.4.5 does), here's a simple fix: Replace the static const ints with an enum.
// someclass.h
// include guards, blabla
class SomeClass
{
enum AlarmPeriod{
PERIOD_ALARM_NORMAL = 5,
PERIOD_ALARM_THRESH = 1
};
public:
void method();
};
// someclass.cpp
#include "someclass.h"
void SomeClass::method(){
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
// main.cpp
#include "someclass.h"
int main(){
someclass sc;
sc.method();
}
This links cleanly with GCC 4.4.5, which wouldn't link the former version, even though both are technically the same.
Note that you cannot, amongst other things, take the address of PERIOD_ALARM_NORMAL and PERIOD_ALARM_TRESH anymore, because both names are just aliases for their respective values.

Initializing static array of strings (C++)?

I can't for the life of me figure out how to do this properly. I have a class that needs to store some constants (text that corresponds to values in an enum type) - I have it declared like this (publicly) in my class:
const static char* enumText[];
And I'm trying to initialize it like this:
const char* MyClass::enumText[] = { "A", "B", "C", "D", "E" };
However gcc gives me the following error:
'const char* MyClass::enumText[]' is not a static member of 'class MyClass'
What am I doing wrong? Thanks!
This code compiles:
struct X {
static const char* enumtext[];
};
const char* X::enumtext[] = { "A", "B", "C" };
Check your code and find differences. I can only think that you did not define the static attribute in the class, you forgot to include the header or you mistyped the name.
This compiles with gcc version 4.0.1:
#include <iostream>
class MyClass {
public:
const static char* enumText[];
};
const char* MyClass::enumText[] = { "a", "b", "c" };
int main()
{
std::cout << MyClass::enumText[0] << std::endl;
}
Compiled with:
g++ -Wall -Wextra -pedantic s.cc -o s
Are you sure that MyClass::enumText is referencing the right class?
Given the error message, it seems to me that you have a declaration of MyClass somewhere (in another header maybe?) that doesn't have enumText[] declared in it. The error message indicates that the compiler knows about MyClass, but it doesn't know about the enumText member.
I'd look to see if you have multiple declarations of MyClass lurking in the shadows.
Can you get wintermute's or T.E.D.'s examples to compile?
As the compiler say, you're trying to define a static member of MyClass that would be a const char* array named enumText. If you don't have it's declaration in the class, then there is a problem.
You should have :
class MyClass
{
//blah
static const char* enumText[];
};
or maybe you didn't want a static member. If not, you shouldn't have to use a class in the name.
Anyway, that has nothing to do with array initialization.
The following code compiles just fine for me in VS 2005:
class MyClass
{
const static char* MyClass::enumText[];
};
const char* MyClass::enumText[] = { "A", "B", "C", "D", "E" };
If I had to take a wild guess, I'd say that your initilization line is in a separate source file, and you forgot to #include the .h file that contains MyClass. That's exactly the kind of thing I screw up and do all the time.
Also, it seems quite likely to me that you have the const in the wrong spot (or not enough of them). The way you have it now, it isn't the array that is constant, or the pointers in the array; just the character elements within it.