Accessing map of class A from class B - c++

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)

Related

Avoiding undefined behaviour: passing a temporary to a `std::function` which has a const ref member variable

The following example is a simplified version found in production code
#include <string>
#include <functional>
#include <iostream>
#include <vector>
struct A
{
std::string myString = "World";
};
struct B
{
void operator()()
{
std::cout << a.myString;
}
const A& a;
};
std::vector<std::function<void()>> v;
void Store(std::function<void()> myFunc)
{
v.emplace_back(myFunc);
}
void Print()
{
v[0]();
}
int main()
{
A a; a.myString = "Hello";
Store(B{a}); // temporary passed with a const-ref of on-the-stack `A`
Print();
}
In production code, accessing the string A (i.e. invoking the function through the vector which in turn accesses myString in A) results in a crash. Compiler Explorer seems to be okay with it, however if this behaviour is undefined, the output probably cannot be trusted: https://godbolt.org/z/cPPKeK9zd
Assuming this is undefined behaviour and I can only store a const & to A, what can I do in the Store(...) function to issue a compiler error and try to catch the undefined behaviour at compile time.
This example has no undefined behavior.
Calling Store copy-initializes the argument of type std::function<void()> from the temporary object of type B. In doing so, std::function uses perfect forwarding to initialize its own internal object of type B, which is therefore move-constructed from the original temporary.
The call to emplace_back copy-constructs the function which therefore copy-constructs the internal object of type B.
That the initial B object is a temporary is irrelevant, as the copy inside the function inside the vector is not. The reference to A inside this B copy still points to the same A object, which is still within its lifetime.

Strange behavior of reference in c++

I'm little confused about reference type in c++, here goes my code snippet.
class data
{
public:
std::vector<int> Get() const
{
return vec;
}
private:
std::vector<int> vec = {1,2,3,4};
};
int main()
{
data dat;
auto const& a = dat.Get()[1];
auto const& b = dat.Get()[2];
std::cout << "a = " << a << ", b = " << b << std::endl;
return 0;
}
The output a = 0, b = 1433763856 doesn't make any sense, after I remove the leading & before a and b, everything works fine. Now here goes my questions:
Since reference of reference is not allowed, but vector::operator[] do return a reference of element inside container, why no error thrown?
I know data::Get() function causes deep copy, but why I get the wrong value of a and b? Will the return value be destroyed right after function call?
You return a copy of the vector, as the signature
std::vector<int> Get() const
implies, as opposed to
std::vector<int> /*const*/& Get() const
which would return a reference, this is true, but that doesn't really explain why returning a copy is a mistake in this situation.
After all, if the call was
auto const& v = data.Get(); // *your* version here, the one returning by copy
v would not be dangling.
The point is that you're not keeping that copy alive by bounding it to a reference (as I've done in the last snippet).
Instead, you're calling operator[] on that temporary, and that call results in a reference to an entry (int&) in that vector. When the temporary vector returned by dat.Get() is destroyed, that's the reference which dangles.
If operator[] returned by value, then not even the a and b in your example would dangle.

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.

static initialization of multiple static variables in one function

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;
}

C++11 initialize map

I am trying to initialize a STL map using C++11 syntax but that doesn't seem to work. After initialization, when i try to access the element, it tries to call the private constructor of Foo. Did I miss something? It works if I use at. I am wondering if I could use operator[] to access initialized values...
#include <map>
#include <string>
class Foo{
public:
int a, b;
Foo(int a_, int b_){
a = a_;
b = b_;
}
private:
Foo(){};
};
int main(){
std::map<std::string, Foo> myMap = { {"1", Foo(10,5)}, {"2", Foo(5,10)} };
int b = myMap["1"].b; // it tries to call private constructor of Foo.
return 0;
}
When you use the operator[] on a map, you may use the operator to either get a value from the map or assign a value into the map. In order to assign the value into the map, the map has to construct an object of its value type, and return it by reference, so that you can use operator= to overwrite an existing object.
Consequently, the type has to be default constructible so that a new object can be created for you to assign into.
At run time, the constructor won't be called if the key already exists, but the compiler has no way of knowing whether you'll ever use operator[] to access a value that doesn't exist, so it requires the constructor to be public.
operator[] of map requires the type to be default constructible, because it creates a new entry if one doesn't exist.
You can use at() instead, which throws if the entry doesn't exist:
int b = myMap.at("1").b;