I have two questions:
I used to have a Constants.h containing
const std::string PATH("/ram/")
and it worked fine.
But when I added
const char* BLAH = "blah";
to the same file. I got a redefinition error during linking.
I get that it's because each compilation unit creates its own constants so this causes problem during linking, but why did it work for regular std::strings before and not for char*s?
So after that first issue, I decided to make everything extern and I now have a Constants.h and a Constants.cpp. But then I ran into another problem. I have another file Test.cpp, where a constant is defined as
const std::string FOO(PATH + "booyah");
This used to create "/ram/booyah" but now it's just "booyah". PATH has somehow become empty. I'm guessing it has something to do with the extern, and with constants being created during compile time. The constant PATH works okay during runtime.
Is this correct understanding? Is there a way to make all these work together nicely?
Thanks in advance!
When something is a const it is automatically made static as in internal linkage.
Therefore when you had const std::string it was actually static const std::string.
This means every compilation unit gets its own copy of the string, which is wasteful of memory, although usually not a big concern.
To fix that, make extern const std::string s; in the header file and extern const std::string s = ".."; in the source file.
To avoid requiring a source file, you can try this alternative, although it is potentially longer to compile. It will also allocate the string only once.
inline const std::string& GetStr()
{
static const std::string s = "..";
return s;
}
Now, why didn't const char* BLAH compile in the header? This is because BLAH is NOT a constant. Although it is a read-only pointer, the pointer is not constant, and can be changed to point to something else.
To make the code compile like it did with const std::string, you must use const char *const BLAH = "..";. Now it is a real constant and will compile as a static. This also suffers from the same problem that every compilation unit receives a copy of the pointer. It may also receive a copy of the string literal, depending on compiler optimizations. You can fix this by using the extern method above.
The order in which constants are initialized in the same compilation unit is the order in which they appear. However, the order in which constants are initialized between compilation units is NOT defined. This means an extern from one source file shouldn't depend on an extern from another source file.
You can fix this by defining all your constants in the same source file in the correct order, or using functions (possibly inline) to return the strings.
A global variable that is const has internal linkage. Your BLAH variable is not const, and so it defaults to external linkage. You could either declare it static, or simply make it const:
const char * const BLAH = "blah";
If you find it difficult to spell type names in C++, you can use type aliases to make your life easier:
using ccp = const char *;
const std::string foo = "foo";
const ccp bar = "bar";
Since the header file can be included ffrom multiple CPP files, you would get conflicts. You can use the static keyword.
I typically do:
static const char* const str= "blah";
Related
Is there a reason to not use "constexpr const char*" in header file?
The argument from a colleague is that every translation unit including this header file would have a copy.
My understanding was that since its compile-time constant, no memory is allocated and acts more like a "#define" macro with respect to memory usage.
Here is the source,
TestConstExpr.h
#include <string.h>
namespace TestConstExpr
{
constexpr const char* TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
constexpr const int TIME_FORMAT_SIZE = strlen(TIME_FORMAT) + 1;
class TestClass
{
char arr[TIME_FORMAT_SIZE];
}
}
The argument from a colleague is that every translation unit including this header file would have a copy.
You're colleague is technically correct. But it doesn't matter, since the redundant copies are thrown away when the units are linked together.
Although, I've seen comments about this not being necessarily the case on some non-standard-conforming systems when dynamic linking is involved.
constexpr const char* TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
.... sizeof(TIME_FORMAT)
This doesn't do what you probably think it does. It gives you the size of the pointer, not the size of the pointed string.
constexpr const int TIME_FORMAT_SIZE = strlen(TIME_FORMAT) + 1;
Your attempted fix also doesn't work, because strlen is not a constant expression.
You can fix the problems by using a reference to the string literal:
static constexpr auto& TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
constexpr const int TIME_FORMAT_SIZE = sizeof(TIME_FORMAT);
I'm writing an c++ class that needs to reference either English or French words sorted by syllable count. The formatted dictionary files are named based on their language, and after they are read in, the English and French functions in the class share the same logic.
//Files needed for French
const char sSingleSyllablePath[] = "FR-SGL.txt";
const char sDoubleSyllablePath[] = "FR-DBL.txt";
const char sTripleSyllablePath[] = "FR-TRI.txt";
//...
The arrays need to have the same name, but reference different files:
//Files needed for English
const char sSingleSyllablePath[] = "EN-SGL.txt";
const char sDoubleSyllablePath[] = "EN-DBL.txt";
const char sTripleSyllablePath[] = "EN-TRI.txt";
//...
The directory containing all of the files is passed to the constructor, then the filename strings are referenced in an initialization function.
What is the best way to call this with the correct filename assignments? Having two separate classes with the same code but different names would be easy but does not seem efficient. Since there are many required files, it also does not seem right to have a huge list of parameters in the constructor.
you can just use std::string:
const std::string sSingleSyllablePath = "-SGL.txt";
const std::string sDoubleSyllablePath = "-DBL.txt";
const std::string sTripleSyllablePath = "-TRI.txt";
then you have to define a function like this:
std::string getLocalized(const std::string &loc, const std::string &filename)
{
return loc + filename;
}
this is a separate function just because - what if you need to do some stuff before create localized filename.
You could pass a constant pointer to constant data:
void My_Function(char const * const p_text);
This doesn't create a new variable, unlike using std::string.
BTW, you may want to consider declaring the strings as static. I've found that the static keyword allows compilers to emit code that directly accesses the strings (rather than copying them). You can still pass a pointer to a static string.
I'm creating software for an Linux + AVR Arduino project. Obviously the whole work is split in several projects in Eclipse (I'm not using Arduino IDE). I'd like to use common, mostly string, constants for all those projects. I also have to spare microcontroller's RAM so compile-time constants needed. How do I best implement it? My idea is to create a separate, header-only, project for those constants.
Using:
class A {
public:
static const char * const STRING;
static const unsigned char BOOL;
};
is not good enough, because I'd like to be able to concatenate string constants like this:
class A {
public:
static const char * const STRING_PART1;
static const char * const STRING_PART2;
static const unsigned char BOOL;
};
const char * const A::STRING_PART1 = "PART1_";
//const char * const A::STRING_PART2 = A::STRING_PART1 + "PART2"; //obviously won't compile
//const char * const A::STRING_PART2 = strcat("PART2", A::STRING_PART1); //this is not compile-time
I also don't want to use define. I'd like to use:
class A {
public:
static const std::string STRING_PART1;
static const std::string STRING_PART2;
}
which allows for string concatenation and is (AFAIK) compile-time, but std::string is not available in avr projects - or I'm wrong here and just don't know how to use it.
Any help appreciated.
You can continue with the current idea of using const char* const (if std::string is not usable). I would suggest to use #define for assignment purpose only. Example:
class A {
public:
static const char * const STRING_PART1;
static const char * const STRING_PART2;
static const unsigned char BOOL;
};
#define PART1_ "PART1_" // <--- for value assignent
#define PART2_ "PART2_"
const char * const A::STRING_PART1 = PART1_;
const char * const A::STRING_PART2 = PART1_ PART2_; // <--- ok! concatenation by compiler
Compile time for me means it is stored in ROM (eg. microcontroller's flash), where as runtime variables are stored in RAM. In my case, it's RAM I have to spare. The compiler decides where to put variables basing on many rules. Defines are one example of compile-time constants, they clearly don't count up to RAM usage. An other example should be static class constants - but in this case my compiler decides otherwise. Of course, I may be confusing things.
I think that you're in fact confusing things:
defines are not stored in ROM - they are not part of your program at all as they get evaluated by the preprocessor already.
the difference between "compile time" and "run time" you're making applies to evaluation, not storage.
If you want to use program memory (= AVR flash) for constant strings, use the PSTR macro from avr/pgmspace.h.
When we declare the const variable it is said in Bruce eckel book that constant folding happens that means memory is not allocated for the variable.
What happens when we declare the variable as const?
How is the compiler free to optimise const variables?
Is memory for const variables is always allocated, and under which circumstances it may not be?
The compiler is free to optimise const variables (and anything else) as long as you can't tell the difference from what the program does.
For example:
const int three = 3;
const int four = 4;
int f() { return three * four; }
Here the compiler is free to omit allocating storage for the variables, and will probably produce exactly the same code as if it was written return 12;
But if you took the address of three, or bound a reference to it, or declared it as extern, then the compiler would probably be forced to allocate memory for it.
I found the following quote on a PDF attributed to Bruce Eckel:
const int bufsize = 100; You can use
bufsize anyplace where the compiler
must know the value at compile time.
The compiler can use bufsize to
perform constant folding, which means
the compiler will reduce a complicated
constant expression to a simple one by
performing the necessary calculations
at compile time. This is especially
important in array definitions:
Assuming that you meant "constant folding" and not "memory folding", what he means is the following:
const int i = 10;
const int j = 20;
int k = i + j;
Memory does not have to be allocated for i and j if the compiler can prove that this is the only time they are used. In that case, it can replace all three lines with int k = 30; Because the variables were marked const, the compiler is free to assume their values never change, and it is smart enough to do the addition for you in advance rather than wait for run-time.
For what its worth, I would not worry about this too much if you're still learning the language. Its an implementation detail; you can generally count on it happening, but the end result is the same either way. Write code that makes sense, use const as needed to prevent mistakes [not as a means to save memory], and if the compiler is able to spit out something special for you then all the better.
----------- Original answer follows, since it is a sort of related topic
Without a direct quote, my guess is he is referring to something like this:
//in file 1:
const char* string1 = "Hello world!";
//in file 2:
const char* string2 = "Hello world!";
//in file 3:
const char* string3 = "world!";
Under some circumstances, the compiler/linker is able to spot that string1 and string2 both point to the same string. So it does not have to create two separate string objects that contain the same data, and assert(string1 == string2) would end up succeeding. An even more intelligent setup could notice that string3 is a substring of the first two, and you end up with assert(string3 == string1+6). In the executable's actual data, you end up with one string, even though in the code there are three of them.
In C++, it doesn't really matter that they are const, because you can't assign a non-const char* to a string literal. But in general, that substitution would not be valid if any of the three strings were allowed to modify the memory they point to. The user probably doesn't intend for *string1 = '5' to change *string2, so the three strings can not be condensed in that case.
" means memory is not allocated for the variable .. "
You are mistaken. If it is a variable( be it either const or non const), it should reside in some part of memory.
When you declare a variable as const means, the variable cannot be modified in it's life time.
const int num = 10 ;
num = 20 ; // Error : num cannot be modified. It is just a read only variable.
int var = 10 ;
var = 20 ; // var is a modifiable integer variable.
anyone knows why this does not work when I try to include a library with the following declarations:
namespace wincabase
{
const char* SOMESTRING = "xx";
}
While this is perfectly fine:
namespace wincabase
{
const int X = 30;
}
I get a "multiple definitions" error with gcc for the first case when I link the lib. Thanks!
const char* means pointer to const char. This means the pointer itself is not constant.
Hence it's a normal variable, so you'd need to use
extern const char* SOMESTRING;
in the header file, and
const char* SOMESTRING = "xx";
in one compilation unit of the library.
Alternatively, if it's meant to be a const pointer to a const char, then you should use:
const char* const SOMESTRING = "xx";
You're declaring the pointer as const, and then pointing it to a string literal defined in the compilation unit, so you'd be duplicating the string literal if you used this in a header file. What you need to do is declare pointer in the header file, and define the string in a source file in the library.
Header:
extern const char* SOMESTRING;
In some source file in the library:
const char* SOMESTRING = "xx";
Besides the approach Tobi pointed out:
const char* const SOMESTRING = "xx";
another alternative is to declare it as a const character array:
const char SOMESTRING[] = "xx";
This approach potentially provides the compiler with additional optimization opportunities, such as placing the string in the read-only section of the resulting binary; although it's conceivable the compiler may be able to perform similar optimizations with the first approach.
You need to declare and define them seprately:
Plop.h
======
namespace wincabase
{
extern const char* SOMESTRING; // declare
}
Plop.cpp
========
const char* wincabase::SOMESTRING = "xx"; // define