This question already has answers here:
Defining static const integer members in class definition
(7 answers)
Closed 8 years ago.
I have the following classes:
MessageConstants.h:
class MessageConstants
{
public:
...
static const int ErrorDescriptionLength = 256;
...
};
SystemMessage.h:
class EvtError
{
private:
struct MsgData
{
int errorCode;
char errorDescription[MessageConstants::ErrorDescriptionLength];
}__attribute__((packed)) msgData;
public:
EvtError(int errorCode, string errorDescription);
inline void setErrorDescription(string desc){memcpy(msgData.errorDescription, desc.c_str(),
min(MessageConstants::ErrorDescriptionLength, (int)desc.length()));}
};
SystemMessage.cpp:
EvtError::EvtError(int errorCode, string errorDesc)
{
memset(&msgData, '\0', sizeof(msgData));
msgData.errorCode = errorCode;
memcpy(msgData.errorDescription, errorDesc.c_str(), min(MessageConstants::ErrorDescriptionLength, (int)errorDesc.length()));
}
I got the following link error on SystemMessage.cpp statement memcpy(msgData.errorDescription, errorDesc.c_str(), min(MessageConstants::ErrorDescriptionLength, (int)errorDesc.length())); :
In function EvtError::EvtError(int, std::string):
undefined reference to MessageConstants::ErrorDescriptionLength
collect2: error: ld returned 1 exit status
make: [link] Error 1
If I replace the MessageConstants::ErrorDescriptionLength with sizeof(msgData.errorDescription), the link error disappear.
My questions:
Why it doesn't complain the MessageConstants::ErrorDescriptionLength in the SystemMessage.h file, where there are two places with it?
How to avoid above link error?
The signature of min is:
template <typename T>
const T& min(const T&, const T&);
It takes its inputs by reference - which requires them to have storage. Your constant:
static const int ErrorDescriptionLength = 256;
Does not currently have storage. There are two ways to approach this. First, you can just add storage in your .cpp:
const int MessageConstants::ErrorDescriptionLength;
Second, you can just cast it to an int:
min((int)MessageConstants::ErrorDescriptionLength, (int)errorDesc.length())
// ^^^^^
Static variable must have a definition outside the class:
const int MessageConstants::ErrorDescriptionLength ;
This should go in exactly one .cpp. file in your project.
Sometimes you can get away without it, but in those cases it is either because the variable was not odr-used, or the program was ill-formed (but ODR violations don't require diagnosis).
Related
This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 7 years ago.
I have a static array inside a class template. The linker complains about a undefined symbol and even after applying the tips I saw on the web, I can't figure out what goes wrong.
Header
template<unsigned int N1, unsigned int N2>
class Foo
{
private:
static const int Size = N1 * N2;
public:
// stuff
private:
static float mArray[Size];
}
CPP
template <unsigned int N1, unsigned int N2>
float Foo<N1, N2>::mArray[size] = {0};
The linker complains about Foo<...>::mArray not being defined. I got it to compile (and link) when I move the definition to the header but I know that this is bad practice for statics. What is the best approach here?
Cheers
As with nearly everything else template-y, the definition of the static data member of a class template needs to be accessible in all translation units using it—in other words, put it in the header file. The compiler+linker are required to make this work without multiple-definition errors.
This question already has answers here:
Undefined reference to static class member
(9 answers)
Closed 8 years ago.
I'm trying to define some static constant strings in C++ and reference them from different files.
Here's how I have the information set up this far:
structClass.h
namespace test {
typedef struct customstructure{
static const std::string stringA;
} customstructure;
}
structClass.cpp
namespace test {
static const std::string customstructure::stringA = "This is String A";
}
Now I'm wondering how I would call this in a third file?
execute.cpp
void printStringA(){
printf("stringA is: %s", test::customstructure::stringA.c_str());
}
gives me a compile error that says:
undefined reference to 'test::customstructure::stringA'
In this code:
namespace test {
static const std::string customstructure::stringA = "This is String A";
}
remove the word static. In fact it is an error to have this, your compiler should give a more useful error message (although I suppose 'undefined reference' meets the requirements of "a diagnostic").
Standard reference: [class.static.data]#5 says that static data members have external linkage, however using the keyword static in the definition would specify internal linkage.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
undefined reference to static member variable
What is an undefined reference/unresolved external symbol error and how do I fix it?
#include<iostream>
using namespace std;
class abc {
private:
static int a ;
public:
abc(int x) {
a = x;
}
void showData() {
cout<<"A = "<<a<<endl;
}
};
int main() {
abc a1(4);
abc a2(5);
a1.showData();
a2.showData();
return 0;
}
When I try to compile this function on Ubuntu with GCC compiler. I get the following error.
/tmp/ccCKK2YN.o: In function `main':
static1.cpp:(.text+0xb): undefined reference to `Something::s_nValue'
static1.cpp:(.text+0x14): undefined reference to `Something::s_nValue'
collect2: ld returned 1 exit status
Compilation failed.
Where as the following code runs fine
#include<iostream>
using namespace std;
class Something
{
public:
static int s_nValue;
};
int Something::s_nValue = 1;
int main()
{
Something cFirst;
cFirst.s_nValue = 2;
Something cSecond;
std::cout << cSecond.s_nValue;
return 0;
}
Is this because Static member variables needs to initialized explicitly before accessing them via objects.Why so ?
static int s_nValue; doesn't allocate any storage to store the int, it just declares it.
You allocate somewhere in memory to store the variable with:
int Something::a=0;
The declaration of a static data member in the member list of a class is not a definition. You must define the static member outside of the class declaration, in namespace scope.
See this thread.
In short, the static member needs to be initialized somewhere in a .cpp file so that the compiler allocates space for it. The declaration would look like this:
int abc::a = 0;
That happens because since static members are shared between all instances of a class, they need to be declared in one single place.
If you define the static variable inside the class declaration then each include to that file would have a definition to that variable (which is against to the static meaning).
Because of that you have to define the static members in the .cpp.
// 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.
I am baffled by the linker error when using the following code:
// static_const.cpp -- complete code
#include <vector>
struct Elem {
static const int value = 0;
};
int main(int argc, char *argv[]) {
std::vector<Elem> v(1);
std::vector<Elem>::iterator it;
it = v.begin();
return it->value;
}
However, this fails when linking -- somehow it needs to have a symbol for the static const "value."
$ g++ static_const.cpp
/tmp/ccZTyfe7.o: In function `main':
static_const.cpp:(.text+0x8e): undefined reference to `Elem::value'
collect2: ld returned 1 exit status
BTW, this compiles fine with -O1 or better; but it still fails for more complicated cases. I am using gcc version 4.4.4 20100726 (Red Hat 4.4.4-13).
Any ideas what might be wrong with my code?
If you want to initialize it inside the struct, you can do it too:
struct Elem {
static const int value = 0;
};
const int Elem::value;
Try writing it as
struct Elem {
static const int value;
};
const int Elem::value = 0;
etc
.
static class members are generally supposed to be defined outside the class (declared inside, defined outside) in one compilation unit.
I don't remember how that interacts with inline initialization of const static integral members.
Also see this post: essentially, the problem is that somehow compiler ends up expanding your code into taking the address of Elem::value.
Why not just do this?
return Elem::value;
But the answer is that you are assigning a value in the declaration. This is supposed to work for basic types such as int, and is only required for complex types (i.e. classes, such as if you had a string instead of int). What I have found in practice is that this is hit or miss depending on what version of what compiler you are using. And, as you found out, which optimization level.
In most of the compilers defining in-class static const <integral type> works fine. But some compilers like Android NDK, such in-class definitions results in linker errors. For such case, we may use the typed enums:
struct X
{
enum : int64_t { VALUE = 100; }; // == static const int64_t VALUE = 100;
};