At the moment I am using a rather large piece of code to analyse data.
In this code apart from the analysis itself, I include some functions that will be used in the analysis, some function that are related to objects and some global variables. My code has the following structure
#include "header1.h"
#include "header2.h"
...
#define global_variable_1 1
#define global_variable_2 3
...
double function1(){
<code>
{
double function2(){
<code>
{
...
main(){
<code>
}
At the moment I want to make my code a bit more elegant, sophisticated and programmable correct. I am thinking of making a header file that will include the headers that I am using and some functions, a second header file with the rest of the functions and a last header file that will contain the global parameters that I use.
So I will have something like
#include "headers.h"
#include "functions.h"
#include "variables.h"
main(){
<code>
}
My question is whether it is programably proper to do so and if there is another and perhaps better way to include the global variables.
If you are trying to use variables that are global and you know that they are a constant value such as PI I would refrain from using both local globals and #define macros. This is a matter of preference but over time I've learned that it is better practice and more elegant to declare them as static const type name; in the header file and set the value accordingly in the cpp file. I also do not prefer to have a bunch of dangling values or basic methods hanging around so I usually will group similar types of values and methods and contain them in a class declaring them to be static. Here is an example:
Utility.h
#ifndef UTILITY_H
#define UTILITY_H
#include <iostream>
#include <string>
#include <stdio.h>
class Utility {
public:
static void pressAnyKeyToQuit();
static std::string& toUpper( std::string& str );
static std::string& toLower( std::string& str );
private:
Utility(); // Not Implemented - This class is not an object and can not be declared.
Utility( const Utility& c ); // Copy Constructor - Not Implemented
Utility& operator=( const Utility&c ); Assignment Operator - Not Implemented
}; // Utility
#endif // UTILITY_H
Utility.cpp
#include "Utility.h"
// -------------------------------------------------------------------------
// pressAnyKeyToQuit()
void Utility::pressAnyKeyToQuit() {
std::cout << "Press any key to quit" << std::endl;
_getch();
} // pressAnyKeyToQuit
// -------------------------------------------------------------------------
// toUpper()
std::string& Utility::toUper( std::string& str ) {
std::transform( str.begin(), str.end(), str.begin(), ::toupper );
return str;
} // toUpper
// -------------------------------------------------------------------------
// toLower()
std::string& Utility::toLower( std::string& str ) {
std::transform( str.begin(), str.end(), str.begin(), ::tolower );
return str;
} // toLower
To use these functions here is an example:
main.cpp
#include <iostream>
#include <string>
#include "Utility.h"
int main() {
std::string strHello( "Hello World!" );
std::cout << strHello << std::endl;
std::cout << Utility::toLower( strHello ) << std::endl;
std::cout << Utility::toUpper( strHello ) << std::endl;
Utility::pressAnyKeyToQuit();
return 0;
} // main
With this type of containment it is illegal to do this:
int main() {
Utility util;
util.pressAnyKeyToQuit();
} // main
This will fail because the Utility default constructor is private or inaccessible, however any type of function or variable that is declared as being static can be called through the scope resolution operator. Here is an example of const variables that would be considered globals.
GeneralMath.h
#ifndef GENERAL_MATH_H
#define GENERAL_MATH_H
class Math {
public:
static const float PI;
static const float PI_HAVLES;
static const float PI_2;
static const float ZERO;
inline static bool isZero( float fValue );
template<typename T>
inline static void swap( T& value1, T& value2 );
private:
Math();
Math( const Math& c ); // Not Implemented
Math& operator( const Math& c ); // Not Implemented
}; // Math
#include "GeneralMath.inl"
void dummy(); // does nothing used to have a method in the *.cpp file
#endif // GENERAL_MATH_H
GeneralMath.inl
// -------------------------------------------------------------------------
// isZero()
inline bool Math::isZero( float fValue ) {
if ( (fValue > -ZERO) && (fValue < ZERO) ) {
return true;
}
return false;
} // isZero
// -------------------------------------------------------------------------
// swap()
template<class T>
inline void Math::swap( T& value1, T& value2 ) {
T temp;
temp = value1;
value1 = value2;
value2 = temp;
} // swap
GeneralMath.cpp
#include "GeneralMath.h"
const float Math::PI = 4.0f * atan(1.0f); // tan(pi/4) = 1
const float Math::PI_HALVES = 0.5f * Math::PI;
const float Math::PI_2 = 2.0f * Math::PI;
const float Math::ZERO = static_cast<float>( 1e-7 );
void dummy(){return;}
Using it in an example:
main.cpp
#include <iostream>
#include "Utility.h"
#include "GeneralMath.h"
int main() {
float value = 3.14957;
if ( Math::isZero( value - Math::PI ) ) {
std::cout << "true" << std::endl;
} else {
std::cout << "false" << std::endl;
}
Utility::pressAnyKeyToQuit();
return 0;
} // main
I prefer this approach so that when common stand alone methods and constant variables are frequently used in multiple files and have a similar grouping you do not have a bunch of #defines or global's declared everywhere.
Now on the other hand if a specific class object that you are creating depends on a specific constant value that is unique to it then you can have a global in its *.h or *.cpp file but again I would still declare it as a static const type name;
The simplicity of this is to include the *.h file where it is needed and use the class name with the scope resolution operator followed by the const variable or static method where it is needed.
You can also do a similar approach without using a class and not declaring them as static by containing them in a namespace, but this could cause problems because other developers might have the same name space and this could create conflicts to resolve names. This is why I prefer the class approach and declaring them as static members.
Remember these classes are not Objects and they can not be constructed and all members have to be static!
Related
I have the following problem, I have a class that I want to be constructed from two different kinds of complex objects. There is a map inside a class method, that constructs from extern variables that I defined in the outer of the class. If I want to declare a class object inside the main function everything goes fine. But if I declare it outside the main, constructor of the class is called before the initialization of the extern objects from which I constructed the inner map in the class method. The error repeats itself on Clang and MSVC. If I use simple objects to construct the map (like int or double), everything works fine in both ways.
main.cpp
#include "TestClass.h"
TestClass test1 = TestClass(g_Spec1); // 1
int main()
{
TestClass test2 = TestClass(g_Spec1); // 2
}
If you comment 1 everything works fine, however, as is, 2 didn't see extern variables.
TestClass.h
#pragma once
#include <iostream>
#include <map>
#include "Token.h"
extern const Spec_t g_Spec1;
extern const Spec_t g_Spec2;
extern const Spec_t g_Spec3;
extern const Name_t g_Name1;
extern const Name_t g_Name2;
extern const Name_t g_Name3;
class TestClass {
private:
Name_t name;
Spec_t spec;
static const Name_t& foo(const Spec_t& j);
public:
TestClass() = default;
TestClass(const Spec_t& j);
virtual ~TestClass() = default;
};
TestClass.cpp
#include "TestClass.h"
const Spec_t g_Spec1{ {Token::Type1, 2} };
const Spec_t g_Spec2{ {Token::Type1, 3} };
const Spec_t g_Spec3{ {Token::Type1, 4} };
const Name_t g_Name1{ "Name1" };
const Name_t g_Name2{ "Name2"};
const Name_t g_Name3{ "Name3" };
const Name_t& TestClass::foo(const Spec_t& spec_)
{
static const std::map<Spec_t, Name_t > glmap1 =
{
{g_Spec1, g_Name1},
{g_Spec2, g_Name2}
};
std::cout << "Foo: Map size must be 2, but it is " << glmap1.size() << std::endl;
std::cout << "In FOO Name1 is " << g_Name1 << std::endl;
auto constIterator = glmap1.find(spec_);
if (constIterator == glmap1.cend())
{
std::cout << "Not found " << std::endl; // 3
return g_Name3;
}
else
{
std::cout << "Found " << constIterator->second << std::endl; // 4
return constIterator->second;
}
}
TestClass::TestClass(const Spec_t& j)
{
name = foo(j);
spec = j;
std::cout << "In CONSTRUCTOR name as global variable from map " << name << std::endl;
}
Another interesting thing is that 1 call 4(Found) and 2 calls 3(Not found) but the map is empty in both ways. Maybe it is because g_Spec1 didn't initialize at this time?
And I didn't get why the map inside is debugger is empty, but have size = 1
Token.h
#pragma once
#include <cinttypes>
#include <string>
#include <vector>
enum class Token
{
Type1,
Type2
};
struct TokenId
{
Token tok;
std::uint64_t val;
bool operator==(const TokenId& p_Other) const noexcept
{
return (tok == p_Other.tok && val == p_Other.val);
}
bool operator<(const TokenId& p_Other) const noexcept
{
if (tok < p_Other.tok) return true;
else if (tok == p_Other.tok)
{
return val < p_Other.val;
}
else return false;
}
};
using Name_t = std::string;
using Spec_t = std::vector<TokenId>;
This is not entirely surprising. Initialization of global objects is partially ordered. Definitions from a single file are ordered by their appearance within that file, but there is no order across files.
You might see unexpected results in a debugger, because using uninitialized variables is undefined behavior.
This is just one of the reasons why global variables are best avoided. In your case, just put that variable inside main().
Static1.hpp
#include <string>
class Static1
{
public:
static const std::string my_string;
};
Static1.cpp
#include "Static1.hpp"
const std::string Static1::my_string = "aaa";
Static2.hpp
#include <string>
class Static2
{
public:
static const std::string my_string;
};
Static2.cpp
#include "Static2.hpp"
const std::string Static2::my_string = Static1::my_string;
main.cpp
#include "Static2.hpp"
#include <iostream>
int main(argc int, char** argv)
{
cout << to_string(Static2::my_string == "aaa") << endl;
return 0;
}
If I put add_executable(printMyString main.cpp Static2.cpp Static1.cpp) in my CMakeLists.txt, I get
0
while add_executable(printMyString main.cpp Static2.cpp Static1.cpp) gives me the expected behavior of
1
To make my code easier to maintain (so that I don't need to keep track of the order I list my source files), is there any way I can ensure that I get the behavior where Static2::my_string == "aaa"?
You are experiencing effects of a static initialization order fiasco.
The usual work-around is to substitute your static variables with functions that have a static variable in the scope, initialize, and return it.
Here is how it could be done for your example: Live Example (order1)
Live Example (order2)
class Static1
{
public:
static std::string my_string();
};
...
std::string Static1::my_string()
{
static const std::string my_string = "aaa";
return my_string;
}
...
class Static2
{
public:
static std::string my_string();
};
...
std::string Static2::my_string()
{
static const std::string my_string = Static1::my_string();
return my_string;
}
...
std::cout << std::to_string(Static2::my_string() == "aaa") << std::endl;
I'm trying to implement a class (C++) with an enum (with the permitted parameters). I got a working solution, but if I try to extend the functionality I get stuck.
Header data_location.hpp
class DataLocation
{
private:
public:
enum Params { model, period };
std::string getParamString(Params p);
};
Program data_location.cpp
string DataLocation::getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
The array ParamsStrings should be generally available in the class, because I need a second method (with inverse function) returning the enum value given a string.
If I try to define the array in the header I get the error:
in-class initialization of static data member ‘const char* DataLocation::ParamsStrings []’ of incomplete type
Why is the type incomplete? The compiler is for sure able to counts the strings in the array, isn't it?
In case there is no way to get my code working, is there an other way? With 1) no XML; 2) no double definition of the strings; 3) not outside the class; 4) no in code programmed mapping.
In class (header) use keyword static and initialize it outside (.cpp) without the static keyword:
class DataLocation {
public:
enum Params { model, period };
string getParamString(Params p);
static const char* ParamsStrings[];
// ^^^^^^
};
const char* DataLocation::ParamsStrings[] = {"MODEL", "BLLBLA"};
//^^^^^^^^^^^^^^^^^^^^^^^^
The code you have posted is perfectly fine.
Here's the proof:
#include <iostream>
#include <string>
struct DataLocation
{
enum Params { model, period };
std::string getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
};
int main()
{
auto a = DataLocation();
std::cout << a.getParamString(DataLocation::model) << std::endl;
return 0;
}
The error message you are getting is not to do with definition of a static data member in an inline function - that's allowed.
There's something else you're not showing us.
The main issue in my question (the second part) was that if I split the class in .hpp and .cpp the definition of the array (I mixed *char and string) has also to be split:
// data_location.hpp
class DataLocation {
static const char * ParamsStrings[];
}
// data_location.cpp
const char * ParamsStrings[] = {"MODEL", "PERIOD"};
At the end I introduced a consistency check to be sure that the number of values in enum growths as the number of strings. Because the array in C++ is somehow limited I had to go for a std::vector (to get the size).
Code for data_location.hpp
#ifndef DATA_LOCATION_HPP_
#define DATA_LOCATION_HPP_
#include <string>
#include "utils/dictionary.hpp"
extern const char* ENV_DATA_ROOT;
struct EDataLocationInconsistency : std::runtime_error
{
using std::runtime_error::runtime_error;
};
struct EDataLocationNotValidParam : std::runtime_error
{
using std::runtime_error::runtime_error;
};
class DataLocation
{
private:
std::string mRootLocation;
static const std::vector<std::string> msParamsStrings;
static bool msConsistenceCheckDone;
public:
DataLocation();
std::string getRootLocation();
std::string getLocation(Dictionary params);
enum Params { model, period, LAST_PARAM};
std::string Param2String(Params p);
Params String2Param(std::string p);
};
#endif
Code for data_location.cpp
#include "data_location.hpp"
#include <string>
#include <cstdlib>
using namespace std;
const char* ENV_DATA_ROOT = "DATA_ROOT";
bool DataLocation::msConsistenceCheckDone = false;
DataLocation::DataLocation() {
mRootLocation = std::getenv(ENV_DATA_ROOT);
if (not msConsistenceCheckDone) {
msConsistenceCheckDone = true;
if (LAST_PARAM+1 != msParamsStrings.size()) {
throw(EDataLocationInconsistency("DataLocation: Check Params and msParamsStrings"));
}
}
}
string DataLocation::getRootLocation() {
return mRootLocation;
}
string DataLocation::getLocation(Dictionary params) {
// to do
return "";
}
const vector<string> DataLocation::msParamsStrings = { "MODEL", "PERIOD", ""};
string DataLocation::Param2String(Params p) {
if (p>=msParamsStrings.size()) {
throw(EDataLocationNotValidParam("Parameter not found"));
}
return msParamsStrings[p];
}
DataLocation::Params DataLocation::String2Param(string p) {
for (int i = 0; i < msParamsStrings.size(); i++) {
if (p == msParamsStrings[i])
return (Params)i;
}
throw(EDataLocationNotValidParam("Parameter not found"));
}
And also a unit test:
#include <boost/test/unit_test.hpp>
#include "data_location.hpp"
#include <string>
using namespace std;
BOOST_AUTO_TEST_SUITE( data_location )
BOOST_AUTO_TEST_CASE(data_location_1) {
DataLocation dl;
auto s = dl.getRootLocation();
BOOST_CHECK_EQUAL(s, "/home/tc/data/forex" );
BOOST_CHECK_EQUAL(dl.Param2String(DataLocation::period),"PERIOD");
BOOST_CHECK_EQUAL(dl.String2Param("PERIOD"),DataLocation::period);
BOOST_CHECK_THROW(dl.String2Param("SOMETHING"), EDataLocationNotValidParam);
BOOST_CHECK_THROW(dl.Param2String((DataLocation::Params)100), EDataLocationNotValidParam);
}
BOOST_AUTO_TEST_SUITE_END()
C++ is very picky about what it will let you initialize inside of a class definition; there are some particularly non-intuitive rules surrounding static members. It all has to do with the ODR, and why all the rules are the way they are is not especially important.
To cut to the chase, making your array a static constexpr const member should shut the compiler up. With the C++11 standard, the restrictions were relaxed a bit, and one of the new stipulations was that static constexpr members can be initialized inline. This is perfect for your application, since the strings in your array are compile-time constants.
The recent g++ compiler which support C++0x or later compiles thus code. Pure C compile compiles, too. Because strings in initialization like {"MODEL", "PERIOD"}; implemented as const char * pointer to the char array.
I have encountered the following strange situation: I have managed to have a global static variable with two instances ... Is this normal or is this a bug in the compiler or is this a hidden realm of C++? The following reproduction is an extract from a larger project (where the behaviour is the same), obviously the names were changed to protect the culprit (and yes, I know there are memory leaks in this code).
Here comes the code:
// other.h
#ifndef _OTHER_H_
#define _OTHER_H_
struct other
{
long longer;
int inter;
char charer;
};
void dosomething();
#endif
And
// other.cpp
#include "other.h"
#include "util.h"
void dosomething()
{
other* something = alloc_mem(other, 4);
}
And
// util.h
#ifndef _UTIL_H_
#define _UTIL_H_
#include <memory.h>
#include <string>
#include "test_class.h"
template <class T> T* allocate(size_t count, const char* f, long l, const char* sth)
{
T* tmp = new T[count];
memset(tmp, 0, count * sizeof(T));
test_class<T*>::instance().throwIn(tmp, f, l, sth, count);
return tmp;
}
#define alloc_mem(type,count) allocate<type>(count, __FILE__, __LINE__, (char*)0)
#endif
And
// main.cpp
#include "other.h"
#include "util.h"
int main()
{
int* i = alloc_mem(int, 1);
int* i1 = alloc_mem(int, 20);
char* c = alloc_mem(char, 1);
dosomething();
int* i3 = alloc_mem(int, 1);
}
And the main part:
// test_class.h
#ifndef test_class_H
#define test_class_H
#include <stdlib.h>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
static long int all_alloc = 0; // THIS will get linked in two times!
template <typename T>
class test_class
{
private:
test_class() {}
static test_class<T>* pinstance;
public:
~test_class() {}
static test_class& instance() {
if(pinstance == NULL) {
pinstance = new test_class();
}
return *pinstance;
}
void throwIn(T item, const char* file, long line, const char* _compiler, long count) {
int status;
char* s = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status) ;
std::cout << "request:" << sizeof(T) * count << " bytes, type:" << s << " # "<<
file << ":" << line << " global_addr:" << &all_alloc << std::endl;
all_alloc += sizeof(T) * count ;
free(s);
std::cout<<"All memory:" << all_alloc << std::endl;
}
};
template <class T> test_class<T>* test_class<T>::pinstance = NULL;
#endif
So, you have to compile this as:
g++ main.cpp other.cpp -o test
and the run it, and:
$ ./test
request:8 bytes, type:int* # main.cpp:6 global_addr:0x6022d8
All memory:8
request:160 bytes, type:int* # main.cpp:7 global_addr:0x6022d8
All memory:168
request:8 bytes, type:char* # main.cpp:8 global_addr:0x6022d8
All memory:176
request:32 bytes, type:other* # other.cpp:6 global_addr:0x6022f8
All memory:32
request:8 bytes, type:int* # main.cpp:11 global_addr:0x6022d8
All memory:184
so, as I could see with a pretty big surprise, I have two global addresses for all_alloc... Indeed, nm -C test shows:
00000000006022d8 b all_alloc
00000000006022f8 b all_alloc
So, obviously the questions:
Why? How is this possible? Is there something allowing this kind of behaviour or is this a bug somewhere in the compiler or linker?
In this case, I don't think you want to declare your all_alloc static. In this case, it means that the linkage is internal. In your case, you get two copies since two files include the header.
If you remove static, then you will have two copies that clash, and result in a linker error. Which is not good.
I believe what you want to do is to change static to extern, then in one cpp file, define the variable and its value.
header:
extern long int all_alloc;
cpp:
long int all_alloc = 0;
This will provide one copy of all_alloc that is shared among your code.
When you have global variable it already has static duration, so static keyword means something different for it. As you have class definition in the file you define your global static variable I assume that is a header. Why it is bad idea to use static global variable in header and what you will get you can read here
So answer is: it is expected behavior and not a bug.
How do you initialize a static const member with a value that is stored in a file? For example:
Class Foo
{
private:
static const String DataFromFile;
void InitData (void);
};
I know for a short value, I can initialize with:
const String DataFromFile = "Some Value";
But what if the value is really a "big" value and encrypted and stored in a disk file? I need to decrypt it before putting it into DataFromFile.
Is there a way to do it or can I just forget it and treat it as a regular variable? That is, instead of:
static const String DataFromFile;
can I just declare it as:
String DataFromFile;
and initialize it with a function?
How do you initialize a static const member with a value that is
stored in a file? For example:
Like this:
//X.h
#include <string>
class X
{
//...
static const std::string cFILE_TEXT_;
static const bool cINIT_ERROR_;
};
//X.cpp
//...#include etc...
#include <fstream>
#include <stdexcept>
namespace {
std::string getTextFromFile( const std::string& fileNamePath )
{
std::string fileContents;
std::ifstream myFile( fileNamePath.c_str() );
if( !(myFile >> fileContents) );
{
return std::string();
}
return fileContents;
}
}
const std::string X::cFILE_TEXT_( getTextFromFile( "MyPath/MyFile.txt" ) );
const bool X::cINIT_ERROR_( cFILE_TEXT_.empty() );
X::X()
{
if( cINIT_ERROR_ )
{
throw std::runtime_error( "xxx" );
}
}