static initialization of multiple static variables in one function - c++

hi I have static std::map with some values and static iterator to default element like this and initialize both at once:
in .h file
class foo
{
static std::map<std::string, int> sqlenumToInt;
static std::map<std::string, int> initEnumToInt();
static std::map<std::string, int>::iterator defaultIt;
};
in .c file
std::map<std::string, int> foo::sqlenumToInt = initEnumToInt();
std::map<std::string, int> foo::defaultIt = std::map<std::string, int>::iterator();
std::map<std::string, int> foo::initEnumToInt();
{
std::map<std::string, int> test;
defaultIt = test.insert(std::make_pair("a", 0)).first
test["b"] = 2;
test["c"] = 3;
test["d"] = 4;
return test;
}
What will be default order of initialization of static variables. Will be defaultIt only
std::map::iterator()
or iterator to first element of sqlenumToInt ??

Within a translation unit, initialization order of static variables is well defined; static variables are initialized in order of definition. So initEnumToInt runs before foo::defaultIt is initialized. In your code, this will result in undefined behaviour, since at the point initEnumToInt runs, foo::defaultIt is in an uninitialized (but zero-initialized) state; you are then calling operator= on a zero-initialized object, and later calling the constructor that expects a zero- or un-initialized object.

The way you've written it, you're accessing an uninitialized element, since the initalizer for sqlenumToInt is evaluated first; this may be undefined behaviour (depending on the details of the iterator type).
If you want the front of the map, say defaultIt = sqlenumToInt.begin() in the initializer and remove it from initEnumToInt().
(Moreover, even the iterator that you obtained in your function would be meaningless, since it becomes invalid as soon as the local map object is destroyed.)

File-scope variables are initialized in the order of their definition. In the sample code, sqlenumToInt will be initialized first, calling initEnumToInt, which sets defaultIt to an iterator value that becomes invalid at the end of the function call (it points into test, which gets destroyed; sqlenumToInt gets a copy of test). Then the explicit initialization of defaultIt kicks in, storing a default-constructed iterator.

std::map<std::string, int>::iterator();
this line construcs a default iterator for map<string, int>, so your defaultIt will be just a copy of this default iterator. If you want the first map element, you should initialize it with sqlenumToInt.begin().
As for the order of initialization, within one compilation unit static variables are initialized in the same order you define them, but the order between different units is undefined.

This line inside initEnumToInt() is problematic:
defaultIt = test.insert(std::make_pair("a", 0)).first
There are two things wrong with this code. The first is that because the iterator has not been constructed before the line is reached, it causes undefined behavior if the iterator does not have a trivial constructor (calling operator= on an uninitialized object --this is not a problem in some cases, but the code will not be portable).
The second problem with that line is that you are setting the iterator to refer to an element in a local map. Any use of that iterator after the function completes would be undefined behavior.
Note that setting the iterator inside the function does not add any value at all to the code, so you can just leave the setter aside. If what you want is the iterator to refer to that element you can do multiple things: if it is the first element always, then set it to sqlenumToInt.begin() in its initializer, if you want to refer to a particular element in the map (known at the place of initialization, set it to sqlenumToInt.find(element). If you want it set to a particular element that is only known inside the initEnumToInt function, then change the order of initialization so that the iterator is initialized first and pass it to the initEnumToInt function as an argument by reference. --This is not required, being a public static variable the function can access it anyway, but passing it by reference makes the dependency and the fact that it gets modified in the function explicit in the code.

Initialization are perform line by line as #ecatmur wrote. So:
std::map foo::sqlenumToInt = initEnumToInt();
std::map foo::defaultIt = std::map::iterator();
To explain it i've write simple example:
// foo.h
#pragma once
#include <iostream>
struct bar
{
int value_;
bar(int value)
{
std::cout << "bar()\n";
set(value, true);
}
void set(int value, bool fromConstructor = false)
{
std::cout << ((fromConstructor) ? "Set from ctor" : "Set from non-ctor")
<< ", old value is: " << value_ << "\n";
value_ = value;
}
};
struct foo
{
static bar bar_;
static bool init()
{
std::cout << "init begin\n";
bar_.set(1);
std::cout << "init end\n";
return true;
}
};
// main.cpp
#include "foo.h"
bool b = foo::init();
bar foo::bar_ = bar(2);
int main()
{
std::cout << foo::bar_.value_ << "\n";
return 0;
}
And output is:
init begin
Set from non-ctor, old value is: 0
init end
bar()
Set from ctor, old value is: 1
2
So, memory for static variable are allocated but initialization are perform later, similar "placement new" mechanic. You can see in output that c'tor of bar are called after init, but old value is 1 (which will be set by init method) and overwrite to 2 due static initialization.
So, you can easy resolve that by changing order static initialization. But you have another problem described by #Kerrek SB:
(Moreover, even the iterator that you obtained in your function would
be meaningless, since it becomes invalid as soon as the local map
object is destroyed.)
One of variant to correct your case:
class foo
{
typedef std::map<std::string, int> Map;
static bool initEnumToInt();
static Map sqlenumToInt;
static Map::iterator defaultIt;
static bool inited;
};
foo::Map foo::sqlenumToInt;
foo::Map::iterator defaultIt = foo::Map::iterator();
bool foo::sqlenumToInt = initEnumToInt();
bool foo::initEnumToInt();
{
defaultIt = sqlenumToInt.insert(std::make_pair("a", 0)).first;
sqlenumToInt["b"] = 2;
sqlenumToInt["c"] = 3;
sqlenumToInt["d"] = 4;
return true;
}

Related

Reference to value in scope produces garbage

I want to write a class with a reference as a field, but am finding that even this simple code which initializes the reference and prints its value fails:
struct Referencer {
explicit Referencer(int in) : num(in) {}
void print() {
std::cout << num << std::endl;
}
int &num;
};
int main() {
int longlived = 500;
Referencer ref(longlived);
ref.print();
}
The result is garbage (no errors are thrown, nor does the program segfault). My understanding is that the variable longlived has the same lifetime as the instance of Referencer, as they are both declared in the stack and have the same scope. Therefore, the field num should point to valid stack memory when ref.print() is called. Why is this not the case?
The constructor of Referencer takes the argument by value. So in holds a copy of the value in longlived. You then initialized num to a reference to in, which get's destroyed as soon as the constructor returns.
If you want num to hold a reference to the object used as the argument in the constructor, pass in by reference like explicit Referencer(int &in) : num(in) {}.

Is it possible to return a std::initializer_list from a function?

The following works:
struct A {
int i;
int v;
};
std::initializer_list<A> getList() {
return {A{0,1}, A{2,3}};
}
int main() {
auto list = getList();
}
I can verify that the contents of list is correct.
However, if I change the member v to a std::vector, it does not work:
struct A {
int i;
std::vector<int> v;
};
std::initializer_list<A> getList() {
return {A{0,{1,2}}, A{3,{4,5}}};
}
in this case, the returned list contains junk values.
I know the standard says that the underlaying objects are not copied when a std::initializer_list is copied. Is this what happens here?
Why did it work in the first example? Was it luck? Is it something special with the case when the objects in the list contains a std::vector?
Returning initializer_list compiles, but it seldom does the right thing. The list is a temporary value and the initializer_list object points to it. It is a dangling pointer.
In your first example, an optimization is applied because the list is a constant expression. Instead of being written into temporary storage, it is a static global. However, this optimization is not specified in the standard. The return value working, is only a form of undefined behavior.

Accessing map of class A from class B

I have two classes class A and class B. Class A has a map of type map<int,int>.
In class A, i have the following definition,
typedef std::map<int, int> mymap;
mymap MYMAP;
A A_OBJ;
// did some insert operations on A_OBJ's MYMAP
I also have the following function in class A that when invoked by B will return A's MYMAP as a copy to class B.
A::mymap A::get_port(){
// Returns A's map
return this -> MYMAP;
}
In class B,
void B::initialize_table(){
A::mymap port_table = A_OBJ.get_port();
cout<< "size of ports table at A is"<<port_table.size());
}
Code got compiled with out any issue. The only problem is that even if I insert some data to A's map, B always shows that A's map has 0 elements.
I have a timer at B which calls initialize_table() every 2s and it is supposed to get the latest copy of the map from A. Not sure where it went wrong.
Any help is appreciated. Thanks.
You're correct that you're creating a copy of the std::map. The way to fix this is by initializing a reference to the map.
Consider the following stub:
#include <iostream>
#include <map>
using my_map = std::map<int, int>;
struct A {
A() : m() {}
my_map& get_my_map() { return m; }
my_map m;
};
struct B {
B() : a() {}
void initialize_map_ref();
void initialize_map_val();
void print_index_42() { std::cout << a.get_my_map()[42] << '\n'; }
A a;
};
void B::initialize_map_ref() {
// notice this is a reference
my_map& m = a.get_my_map();
m[42] = 43;
}
void B::initialize_map_val() {
// notice this is a copy
my_map m = a.get_my_map();
m[42] = 43;
}
int main() {
B b;
b.initialize_map_ref();
b.print_index_42();
return 0;
}
B::initialize_map_ref initializes a reference (i.e., a reference to the map within a), where B::initialize_map_val creates a copy and initializes the copy. The copy dies after the call, so outside of the call, m[42] == 0. The reference initialization, on the other hand, persists because you've changed a reference to the underlying object.
Ideone: With reference and with value.
I suggest returning by an R-value reference for the sake of efficiency (i.e. A::mymap&& A::get_port(){return A::mymap(this -> MYMAP;) };).
I can only see your code failing in one of two circumstances:
you are updating A_OBJ.MYMAP after you call A_OBJ.get_port()
This will cause the copy to be out of date.
or you are updating A_OBJ.get_port(). and then calling A_OBJ.get_port() again. This will cause the copy to be modified but the original map, to be left unmodified, resulting in the second call to A_OBJ.get_port() to not consider any changes made to the value returned by the previous.
Depending on what you want you may want to return a (const) reference to the map.
EDIT I mistakenly originally thought that A::mymap port_table = A_OBJ.get_port(); would cause two copies, but now I realize it will cause a copy and then a move, it would also do that in the rvalue case, however that is likely to introduce undefined behaviour (I think) because of returning a reference to a temporary... (originally I had it return this -> MYMAP but would be an error as it would try to bind an lvalue (this -> MYMAP) to an rvalue reference)

Theoretical clarification regarding maps and iterators

If I have a class with a map as a private member such as
class MyClass
{
public:
MyClass();
std::map<std::string, std::string> getPlatforms() const;
private:
std::map<std::string, std::string> platforms_;
};
MyClass::MyClass()
:
{
platforms_["key1"] = "value1";
// ...
platforms_["keyN"] = "valueN";
}
std::map<std::string, std::string> getPlatforms() const
{
return platforms_;
}
And in my main function would there be a difference between these two pieces of code?
Code1:
MyClass myclass();
std::map<std::string, std::string>::iterator definition;
for (definition = myclass.getPlatforms().begin();
definition != myclass.getPlatforms().end();
++definition){
std::cout << (*definition).first << std::endl;
}
Code2:
MyClass myclass();
std::map<std::string, std::string> platforms = myclass.getPlatforms();
std::map<std::string, std::string>::iterator definition;
for (definition = platforms.begin();
definition != platforms.end();
++definition){
std::cout << (*definition).first << std::endl;
}
In Code2 I just created a new map variable to hold the map returned from the getPlatforms() function.
Anyway, in my real code (which I cannot post the real code from but it is directly corresponding to this concept) the first way (Code1) results in a runtime error with being unable to access memory at a location.
The second way works!
Can you enlighten me as to the theoretical underpinnings of what is going on between those two different pieces of code?
getPlatforms() returns the map by value, rather than reference, which is generally a bad idea.
You have shown one example of why it is a bad idea:
getPlatforms().begin() is an iterator on a map that is gone before the iterator is used and getPlatforms().end() is an iterator on a different copy from the same original map.
Can you enlighten me as to the theoretical underpinnings of what is going on between those two different pieces of code?
When you return by value, you return a deep copy of the data.
When you call myclass.getPlatforms().begin(); and myclass.getPlatforms().end(); you are effectively constructing two copies of your data, then getting the begin iterator from one copy and the end iterator from the other. Then, you compare the two iterators for equality; This is undefined behavior.
results in a runtime error with being unable to access memory at a location.
This is because definition is initialized, then the temporary object used to create it is deleted, invalidating the data the iterator pointed to. Then, you attempt to use the data, through the iterator.
A problem that you have is that you should be using const_iterator not iterator. This is because the function getPlatforms is const qualified, whereas the function in the map iterator begin() is not; you must use the const qualified const_iterator begin() const instead to explicitly tell the compiler you will not modify any members of the class.
Note: this is only the case for code 1, which should, by the way return const&

C++98/C++03 STL set bug?

all,
One weird thing happens when I'm using set:
Let's say:
struct A {
int num;
bool operator<(const A& x) const
{
return num < x.num;
}
};
struct B {
set<A> ASet;
};
struct C {
list<B> BList;
list<B>::iterator BListIter;
C()
{
BList.push_back(B());
BListIter = --(BList.end());
}
};
somewhere we can have this code:
list<C> CList;
CList.push_back(C());
list<C>::iterator CListIter = --(CList.end());
for (...) {
A a = ...;
...
CListIter->BListIter->ASet.insert(a);
this will end up with segmentation fault after a few iterations. When this happens, I found an invalid item is referenced and compared (its address is 0x0000001b, so definitely wrong) during the insertion.
Funny thing is when I turn on -std=c++0x this option to use C++11 the problem goes aways! So I wonder if this is a bug of STL set in C++98/C++03?
Thanks,
Kevin
In C++03 or earlier, copies of the temporary objects are put on CList and the BList inside the CList object.
When the C::BList member is copied, a completely new list<B> is created, but when the C::BListIter member is copied, the new C::BListIter iterator still refers to the C::BList list in the original C object - which is a temporary that gets destroyed. So that iterator refers to an element of a destroyed list.
In C++11 move constructors are used instead of copy constructors, so the resulting iterator doesn't refer to a dead object.