Initializing static array of strings (C++)? - 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.

Related

Putting all const values of a class in the same place

I am trying to use some compile-time const values for a class and I am using constexpr/const, but I dislike a lot the fact that some will be inited in the hpp and some in the .cpp.
I've read the explanation related to string incompatibility with constexpr, but
isn't a trick to put all of them in the same location?
It's strange that in the modern C++ 11/14 you have to declare them in two separate locations.:)
Thank you
// test.hpp
class Test
{
// initialization values are located clear in the header
static constexpr int a_{10};
static constexpr int b_{20};
// we must go to the cpp file to see this string value
static const std::string str_;
};
// test.cpp
const std::string Test::str_{"abc"};

C++: Defining void* array in header file and declaring it in a cpp file?

I saw this question and I tried to do as the answer to that question said. To use the extern keyword in the header file to define an array and then declare it outside of that namespace or class in a other cpp file.
It didn't work for me really, I'm not sure if it because I'm using a void pointer array (i.e void* array[]) or if it's just my ignorance that prevents me from seeing the problem.
This is the shortest example I can come up with:
[cpp.cpp]
#include "h.h"
void main(){
void* a::b[] = {
a::c = a::d(1)
};
}
[h.h]
namespace a{
struct T* c;
struct T* d(int e);
extern void* b[];
}
So the problem is that I receive the error:
IntelliSense: variable "a::b" cannot be defined in the current scope
And I have no clue why that is.
First, you should declare main() as int ! See here why.
Declaring your array as extern in a namespace means that it belongs to the namespace but is defined somewhere ele, normally in a separate compilation unit.
Unfortunately, in your main(), you try to redefine the element as a local variable. This explains the error message you receive.
You shoud do as follows:
#include "h.h"
void* a::b[] { a::c, a::d(1) }; // global variable belonging to namespace
int main() // int!!!
{
/* your code here */
}
The code will compile. The fact that a::b[] is defined in the same compiling unit is accepted. But the linker will complain because a::d(1) is a call to the function d returning a pointer to a struct, and this function is defined nowhere.
Therfore you should also define this:
namespace a {
struct T* d(int e)
{
return nullptr; // in reality you should return a pointer to struct T
}
}
Interestingly, struct T does not need to work for this code to compile and link.

Global pointer to object causes access violation?

Maybe you will be able to clear something for me, because I don't know exactly where my thinking is flawed. First some code:
Talker.h:
class talker
{
public:
talker();
void sayHello() {cout << "Hello";} ;
};
anotherClass.h:
class anotherClass
{
public:
anotherClass();
void doSomethingYourself() { cout << "will do"; };
void askHimToSayHello() { pointerToTalker->sayHello; };
//Access violation, bad pointer(?)
};
common.h:
static talker *pointerToTalker;
// if I add here "= new talker", code works fine
main.cpp:
#include "common.h"
int main()
{
pointerToTalker = new talker; // Here is the bug, but why?
pointerToTalker -> sayHello; // says Hello alright
anotherClass *pointerToAnotherClass = new anotherClass;
pointerToAnotherClass -> doSomething (); //Does something OK
pointerToAnotherClass -> askHimToSayHello(); // Causes access violation
}
Of course functions are a bit more complex, and each is implemeted in coresponding .cpp including "common.h". My question is - why pointerToTalker, if initialized inside main() does not work inside anotherClass::AskHimToSayHello()? It should pointing to valid memory by the time it is used there.
It is my "Hello world, OOP!" btw, so please be gentle if there is no hope for me :)
Sorry for the childish style btw. It helps me cut down the code I have to something more compact without me loosing the big picture :).
Because
static talker *pointerToTalker;
is not a global. In this context, static gives the variable internal linkage for each translation unit (cpp file + included files) in which common.h is included.
You need to declare it as extern:
extern talker *pointerToTalker;
and initialize it in a single implementation file.
Declaring it static will create a copy of pointerToTalker for each translation unit. So you're initializing the one from main.cpp. Others are left uninitialized, and thus you run into undefined behavior. The proper way is:
//common.h:
extern talker *pointerToTalker;
//common.cpp
#include "common.h"
talker* pointerToTalker = new talker;

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.

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