Too much declaration and initialization in header file C++ - c++

I've a big file of Constants.h file where about 200 variables(mostly arrays) are declared and initialised. I'm using namespace.
METHOD 1:
//Constants.h
#ifndef CONSTANTS_H_
#define CONSTANTS_H_
namespace LibConstants
{
const int a = 12;
const std::string theme[2]; = {"themeA", "themeB"};
const int arr[2] = {1, 2};
// and around 200 more declarations with initialization
}
#endif
This .h file is #include in almost every .cpp file but each time only very minimal variables are being used like LibConstants::theme[0].
My ways works fine but doesn't it allocate memory unnecessarily?
Shall I follow the approach, to only define variables in .h file and initialize in .cpp?
Like in below code:
METHOD 2:
//Constants.h
#ifndef CONSTANTS_H_
#define CONSTANTS_H_
namespace LibConstants {
std::string getMyTheme(int arg);
std::string getMyThemeName(int arg);
const int a;
const std::string theme[2];
const int arr[2];
// and around 200 more declarations with initialisation
};
#endif
Initialising in cpp file
//Constants.cpp
#include LibConstants.h
using namespace LibConstants {
std::string getMyTheme(int arg) {
theme[2] = {"themeA", "themeB"};
return theme[arg];
}
std::string getMyThemeName(int arg) {
...
}
}
Using it like
//HelloWorld.cpp
#include Constants.h
int main() {
//METHOD 1:
std::string a = LibConstants::theme[0]; // arg=0 is dynamic and is being read from localStorage in my case.
//METHOD 2:
std::string a = LibConstants::getMyTheme(0); //Here also arg=0 is dynamic.
...
}
Here, unnecessary allocation of memory for unneeded variables won't happen except for arrays which are declared as const std::string st[2]; in the header file.
Here "arg=0" is run time involvement. Does it matter if some variable is not dependent on run-time but only compile time in which case it will simply replace the value of placeholder in corresponding .cpp file?
Please correct me wherever I am wrong.

Take the std::string example. Consider,
namespace Constants {
const std::string str = "A not very short string";
}
str is a class type, it has a constructor, and it has to allocate memory to store its contents (unless short-string optimization is used in its implementation. Even in that case, it only applies to, well, "short string"). It is declared a namespace scope variable. So the program has to construct this variable at launch. Every program that has this variable declared, will need to allocate memory, and initialize its contents. That is an overhead you probably don't want, depending on how much that will effect your performance.
On the other hand, if you have,
const std::string &str()
{
const static std::string s = "A not very short string";
return s;
}
Now, you have a static local variable. It will be initialized at the first entry of the function. And it will be initialized only once. If str is never called, it will not be initialized at all.
However, note that the string literal "A not very short string" is still going to occupy some memory. Where and how it is store is implementation defined. Usually in the data segments, and the impact is usually minimal.
In contrast to class types. It is preferable to define fundamental types, especially integral types in the header.
For example,
namespace Constants {
constexpr int x = 10;
}
An optimizing compiler will most likely not to store the variable x and its contents 10 at all. Instead, wherever x is used, it will be replaced by 10, and in some cases, coded into the instruction op-code (so called immediate operands). Of course, this is again an implementation detail. And such optimization cannot be relied on with all compilers. And if you take x's address or otherwise ODR used, compiler will be compelled to make room for this variable anyway. But the bottomline is that, it will be very unlikely that you will be worse off by declaring this constants in header than defining it in an external source file.

Related

How to use extern variable correctly?

Q1:
I have recently read book C++ Primer, when I read follow:
To substitute the value for the variable, the compiler has to see the variable’s initializer. When we split a program into multiple files, every file that uses the const must have access to its initializer. In order to see the initializer, the variable must be defined in every file that wants to use the variable’s value.
I have a question: when I use a variable defined in other file, I just use extern to declare is enough, why should I must have access to its initializer, so I have done a test:
in main.cpp, I write below:
#include <iostream>
using namespace std;
extern int test;
int main()
{
cout << test << endl;
return 0;
}
in test.cpp, I write below:
int test = 100;
and by the way, these two files are added in the same projects, or it will not build successful.
When I run them, it print
100
like I expect. But in main.cpp, I don't need to define something like int test = 100 like book said. I don't know who is right.
Q2:
int i = 43;
const int &r = i;
i = 1; //when i changed, r alse changed
I have tested in gcc 4.7 and visual studio 2013, they both get same result,
r changed. Then, what's the point of const? Shouldn't r always be 43?
I think the quote from the book means something like the following
const size_t N = 10;
int main()
{
int a[N];
//...
}
If the constant N is defined in some other module with specifier extern then in the module with main the compiler does not have an access to the value of the constant and as result it is unable to define the array
extern const size_t N;
int main()
{
int a[N]; // error: the value N is unknown
//...
}
By this reason constants have internal linkage that it could be possible to define them in each module where their values are required at compile time.
As for the second question then constant references are used that to prevent modifying the referenced objects using references.
For example if you want that some function would not change your compound object you can declare its parameter as a constant reference to the object.
Also a temporary objects are bound to constant references.
Consider an example
void display_string( const std::string &s )
{
std::cout << s << std::endl;
}
you may call the function using a character array. For example
display_string( "Hello, World" );
The compiler implicitly converts the string literal to a temporary object of type std::string and binds it to the constant reference of the parameter.
If the parameter would not be declared like constant reference as for example
void display_string( std::string &s );
then the compiler would issue an error.
Using a constant reference it is supposed that the client code will not change the referenced object itself.
That is the client code may look at the object but may not touch it by hands.:)
Q1. have access to its initializer means compiler need to known the variable's initializer(definition). You can let compiler achieve that by link main.cpp and test.cpp together. You said that these two files are added in the same projects, so IDE will do that for you.
You can find more on this question.
Q2. Compiler don't allow you to change r's value, because it's a reference to a const variable, but i is an integer variable, so you can change it's value.

Trouble declaring an array using symbolic constant

This code will not compile:
#ifndef RemoteControl_h
#define RemoteControl_h
#include "Arduino.h"
class RemoteControl
{
public:
RemoteControl();
~RemoteControl();
static void prev_track();
static void next_track();
static void play_pause_track();
static void mute();
static void vol_up();
static void vol_down();
void respond(int code);
void add_code(int code, void (*func)());
private:
boolean active = true;
struct pair {
int _code;
void (*_func)();
};
const int max = 1000;
int database_length = 0;
pair database[max]; //This line doesn't compile unless I use a literal constant instead of "max"
};
#endif
But if I put the section below in the constructor for the class instead it works fine.
const int max = 1000;
int database_length = 0;
pair database[max];
Am I not allowed to declare an array within a class in c++ and use a virtual constant as the length? I am working in arduino if that makes a difference, but I expect that I am not understanding something with the c++ language since this is a standard .h file. Oh and the problem isn't the .cpp file because I completely removed it with the same results: compiles with literal constant length but not virtual constant length.
In C or C++,try using malloc() in stdlib.h, cstdlib for c++. Don't forget free()
const int max = 1000;
struct pair *ptr = malloc(sizeof(pair) * max); // allocated 1000 pairs
free(ptr); // when the amount of memory is not needed anymore
Let me first clear a few things up for you.
In C, a const variable is considered as const-qualified, it is not a compile-time constant value (unlike an integer literal, which is a compile time constant value). So, as per the rules for normal array size specification, you cannot even use a const variable in this case.
In C, we may have the provision to use VLA which enables us to use syntax like pair database[max] even if max is not a const variable but that is again some optional feature of the compiler (as per C11).
In C++, we can use a const variable as the size of array, as in C++, a const variable is a compile time constant.
So, to answer your question:
In C, your code will be ok if your compiler supports VLA. and even if max is not const.
In C++, there is no VLA, but it maybe supported as a gnu extension. If max is const, it will be ok.
The easiest fix is to just take the
const int max = 1000;
out of the class and put it above the class.
Even better would be to ensure that it is a compile-time constant like so:
constexpr int max = 1000;

Initializing static global constants with enum value. Is this safe? Pitfalls?

If you want to wrap some enum type with a class, e.g., to build some functions around it, you could end up with the following situation:
main.cpp:
#include "WrappedEnumConstants.h"
int main(int argc, char * argv[])
{
WrappedZero.print();
WrappedOne.print();
}
WrappedEnumConstants.h
#ifndef WRAPEDENUMCONSTANTS_H
#define WRAPEDENUMCONSTANTS_H
#include "WrappedEnum.h"
#include "InternalEnum.h"
static const WrappedEnum WrappedZero(ZeroEnum);
static const WrappedEnum WrappedOne(OneEnum);
#endif
WrappedEnum.h
#ifndef WRAPPEDENUM_H
#define WRAPPEDENUM_H
#include <iostream>
#include "InternalEnum.h"
class WrappedEnum
{
public:
WrappedEnum(InternalEnum a);
void print() const;
private:
InternalEnum a;
};
#endif
WrappedEnum.cpp
#include <iostream>
#include "WrappedEnum.h"
WrappedEnum::WrappedEnum(InternalEnum a) :
a(a)
{}
void WrappedEnum::print() const {
std::cout << "WrappedEnum: " << a << std::endl;
}
InternalEnum.h
#ifndef INTERNALENUM_H
#define INTERNALENUM_H
enum InternalEnum { ZeroEnum, OneEnum};
#endif
The output I get is like expected:
WrappedEnum: 0
WrappedEnum: 1
I am wondering if initialization of the static constants WrappedZero and WrappedOne is safe. Are the constants of ZeroEnum and OneEnum guaranteed to be initialized before WrappedZero and WrappedOne or was I only lucky? Especially, I am wondering what pitfalls there might be if you used the WrappedEnum in a large project with many things linked together. Do you see any?
Are there differences in initialization of enum constants (like ZeroEnum and OneEnum) and a global "static const int"?
Are the constants of ZeroEnum and OneEnum guaranteed to be initialized before WrappedZero and WrappedOne or was I only lucky?
They are compile-time constants, so are not initialised at runtime at all. It's safe to use them at any time.
Are there differences in initialization of enum constants (like ZeroEnum and OneEnum) and a global "static const int"?
If the static const int is initialised with a constant value, it will be a compile-time constant in any code that knows its value. Otherwise, it will be initialised during the static initialisation phase, before the program starts. In either case, it's also safe to use it at any time.
If it needs to be initialised with a run-time value, then it will be initialised during the dynamic initialisation phase (like your "wrapped" objects), and you may encounter problems with the initialisation order.
Yes there is a difference.
A field of enum type in an instance is for the compiler unknown at compile time so when generating code it's forced to actually access the instance to find the value and the generated code will be bigger and slower.
A const int can instead be known at compile time (if you initialize it with a constant) and so the compiler can generate code that uses directly the value (and you can also use that as a constant expression for example in array declarations or templates).
Also note that wrapping your values in static-duration instances you're creating problems for who will use those wrapped values during the initialization of other static duration instances because the order of initialization is not guaranteed.
I'm not really sure why you think it's a good idea to wrap a box that contains what you need in a bag. Except writing a lot more code and annoying the compiler and your users, what good thing are you achieving doing that?

assigning values to structure of a class using class object

I have got a very strange problem.
Here is my code:
Header File
namespace test
{
class LoadConfigData
{
struct control_params
{
char active[];
char suspended[];
char erased[];
}*ctrl_params;
bool loadConfig();
};
};
Main.cpp
using namespace std;
using namespace test;
namespace test
{
extern LoadConfigData *loadConfigDataobj;
LoadConfigData *loadConfigDataobj = new LoadConfigData;
};
int main()
{
loadConfigDataobj->loadConfig();
cout <<loadConfigDataobj->ctrl_params->active_status_code_v<<endl;
cout <<loadConfigDataobj->ctrl_params->suspended_status_code_v<<endl;
cout <<loadConfigDataobj->ctrl_params->erase_status_code_v<<endl;
return 0;
}
bool LoadConfigData::loadConfig()
{
std::string a = "AC";
std::string b = "SP";
std::string c = "ER";
LoadConfigData::ctrl_params = new LoadConfigData::control_params;
sprintf(loadConfigDataobj->ctrl_params->active,"%s",a.c_str());
sprintf(loadConfigDataobj->ctrl_params->suspended,"%s",b.c_str());
sprintf(loadConfigDataobj->ctrl_params->erased,"%s",c.c_str());
return true;
}
Output:
ER
ER
ER
Which means that it's printing the last copied string for each struct member.
What is wrong in my code.
The problem is that you don't give the character arrays a size:
struct control_params
{
char active[];
char suspended[];
char erased[];
}*ctrl_params;
I'm rather surprised that this compiles; my understanding was that an unsized array was an incomplete type, and so couldn't be a non-static class member. However, my compiler at least (and presumably yours) treats these as arrays of size zero, all at the same location in memory. Therefore, each time you write to one it overwrites whatever you wrote to the others. (Of course, the behaviour is undefined since it writes outside the array bounds).
The simplest solution is to use std::string to represent the strings. That will manage its size automatically, and you can simply write active = a rather than messing around with sprintf (or, slightly more sensibly, strncpy) and hoping you don't get a buffer overrun.
The problem is your usage of sprintf. sprintf will not allocate any memory for you, active, suspended and erased are pointing to strange and undefined addresses. You are invoking undefined behaviour.
Without adding all the normally necessary detail, use std::string and it's operator overloads instead. For simple parsing, use streams. For now, totally avoid any C-string-function, and instead use C++ solely.

Setting static const char[] to a predefined static const char[] fails

Hey guys! When I try to do the following in a header file
static const char FOOT[] = "Foot";
static const char FEET[] = FOOT;
I get a compiler error of error: initializer fails to determine size of FEET. I was wondering what the cause of this is, and if there is a way to rectify it. Thanks!
Even though why you get this error has been answered, there's more to the story. If you really need for FEET to be an array, then you can make it a reference instead of a pointer:
char const foot[] = "foot";
char const (&feet)[sizeof foot] = foot;
// reference to array (length 5) of constant char
// (read the declarations "from the inside-out")
char const* const smelly_feet = foot;
// constant pointer to const char
int main() {
cout << sizeof feet << ' ' << feet << '\n';
cout << sizeof smelly_feet << ' ' << smelly_feet << '\n';
cout << sizeof(void*) << " - compare to the above size\n";
return 0;
}
(More examples of the inside-out rule.)
Secondly, static at file and namespace scope means internal linkage; so when you use it in a header, you'll get duplicate objects in every TU using that header. There are cases when you want this, but I see no reason for it in your code, and it's a common error.
Regarding array size: The sizeof operator returns the memory-size of an object or instance of a type. For arrays this means the total memory-size of all of its items. Since C++ guarantees that sizeof(char) is 1, the memory-size of a char array is the same as its length. For other array types, you can divide by the memory-size of one item to get the length of an array:
void f() {
int array[5];
assert((sizeof array / sizeof *array) == 5);
}
And you can generalize it to a function template:
template<class T, int N>
int len(T (&)[N]) {
return N;
}
// use std::size_t instead of int if you prefer
This exists in boost as boost::size.
You may see code that uses sizeof array / sizeof *array, either through a macro or directly, either because it's old or doesn't want to complicate matters.
The cause is that you are saying that FEET[] is an array of chars but you're initializing it to a ptr;
To rectify, you can change FEET from FEET[] to *FEET
Could also do the same with FOOT
There is no '=' operator for arrays in C++, you need to use strcpy or something similar (which you can't do with a static const). Depending on your requirements the following might be useful:
#define contentsOfFoot "FOOT"
static const char FOOT[5] = contentsOfFoot;
static const char FEET[5] = contentsOfFoot;
static const char *feeet = FOOT;
static is deprecated, and const variables at namespace scope are effectively static, anyway. Also, please reserve ALL_CAPS for preprocessor identifiers.
A bigger question is why you are using raw arrays at all. You very likely want something more like this:
std::string const foot = "foot";
std::string const feet = foot;
Or, more likely:
#include <string>
namespace spbots {
typedef std::string string_t;
string_t const foot = "foot";
string_t const feet = foot;
}
One way would be this
static const char FOOT[] = "Foot";
static const char *FEET = FOOT;
I want to emphasise that you shouldn't put that stuff in a header file. Variables should only be put in header files if you want to make them globally visible (generally not a great idea but it's a legitimate technique).
feet.h
extern char feet[];
feet.cpp
char feet[] = "FEET";
Now when you include feet.h in a different .cpp file, say leg.cpp, the declaration in feet.h means leg.cpp can see and use the array defined in feet.cpp
The extern keyword has the opposite meaning to the static keyword you use. A typical use of static is as follows;
feet.cpp
static char feet[] = "FEET";
Now feet[] is explicitly not visible outside feet.cpp. The static keyword is responsible for this lack of visibility. So static=local, extern=global and (perhaps unfortunately?) extern is the default, so if you don't use either keyword you get extern whether you like it or not.
Because static=local, there's nothing to communicate to other modules, so no need to have a declaration in a header file for other modules to see. Everything is kept within feet.cpp, probably a better idea than a global variable.
Another point I am implicitly trying to get through with this explanation is that declarations go into header files, definitions go into .cpp files. In your question you have a definition in a header file. Not illegal, but if you include it in more than one file (and if you don't, why would you put it in a header?), you will get a single variable defined multiple times, which will give you a link error.
All of this is actually old school C rather than C++ in the modern sense of the word. In other words you are really writing C, but since C is (almost) a subset of C++, you can still use C++ to do this stuff.