I encountered with some weird problem. I have class which store its values inside map. But in one case I need to expose map to do some external calculation and possible adding of data inside that map.
And I have next problem. I have shared_ptr of that class and expose map through reference, but during processing map wont accept new data.
I wrote some dummy example of that just to be clear. What is happening here? And why?
Why changes made to map won't hold up after function end?
#include <map>
#include <iostream>
#include <memory>
class MapWrap {
public:
MapWrap() {}
~MapWrap(){}
std::map<int, int>& getMap() { return map; }
private:
std::map<int, int> map;
};
void goGo(std::shared_ptr<MapWrap> m){
auto map = m->getMap();
std::cout << "Func: before: map size: " << map.size() << std::endl;
for(int i = 0; i < 3; ++i){
// This should and will add new value to map.
if(map[i] == 3){
std::cout << "blah" << std::endl;
}
}
std::cout << "Func: after: map size: " << map.size() << std::endl;
}
int main(){
auto mapWrap = std::make_shared<MapWrap>();
for(int i = 0; i < 3; ++i){
goGo(mapWrap);
}
return 0;
}
EDIT: Removed const from getMap() method.
The problem is that here:
auto map = m->getMap();
type of map is std::map<int, int> so you make a copy and you modify this copy. Change it to :
auto& map = m->getMap();
and you will modify the passed map instead of copy.
btw. if you dont know what type your auto variable have, you can always use compiler errors to check this:
template<typename T> struct TD;
auto map = m->getMap();
TD<decltype(map)> dd;
will result in:
main.cpp:19:21: error: aggregate 'TD<std::map<int, int> > dd' has incomplete type and cannot be defined
TD<decltype(map)> dd;
here you can read map type is std::map<int, int>
Related
I'm trying to loop through a map of maps and pass each pair to a function which modifies the contents. When I try to compile the below code, I get the following error with respect to the item range variable declaration:
error: invalid initialization of non-const reference of type 'std::pair<std::__cxx11::basic_string<char>, std::map<std::__cxx11::basic_string<char>, int> >&' from an rvalue of type 'std::pair<std::__cxx11::basic_string<char>, std::map<std::__cxx11::basic_string<char>, int> >'
for(std::pair<std::string, std::map<std::string, int>>& index : names)
When I try to use auto& to declare range variable index, the error moves from the range variable declaration to the function call incrementAndPrintIt(index);
#include <iostream>
#include <vector>
#include <map>
void incrementAndPrintIt(std::pair<std::string, std::map<std::string, int>>& value)
{
for(auto& j : value.second) {
j.second = j.second + 1;
std::cout << "j: " << j.second << std::endl;
}
}
int main() {
//initialize a map of maps
std::map<std::string, std::map<std::string, int>> names = {{"women",{{"Rita",2}}},{"men",{{"Jack",4}}}};
for(std::pair<std::string, std::map<std::string, int>>& index : names) {
incrementAndPrintIt(index);
}
return 0;
}
for(std::pair<std::string, std::map<std::string, int>>& index : names)
In a std::map, the map's key, the first value in the pair, is a constant value.
This should be:
for(std::pair<const std::string, std::map<std::string, int>>& index : names)
incrementAndPrintIt()'s parameter should also be adjusted to the same.
It's easy to use auto to avoid this entire headache in the first place:
for(auto& index : names)
But this doesn't help with incrementAndPrintIt()'s parameter. However it doesn't need map's key, so you can simply pass index.second to it, and save a lot of wear and tear on your keyboard:
#include <iostream>
#include <vector>
#include <map>
void incrementAndPrintIt(std::map<std::string, int> &value)
{
for(auto& j : value) {
j.second = j.second + 1;
std::cout << "j: " << j.second << std::endl;
}
}
int main() {
//initialize a map of maps
std::map<std::string, std::map<std::string, int>> names = {{"women",{{"Rita",2}}},{"men",{{"Jack",4}}}};
for(auto& index : names) {
incrementAndPrintIt(index.second);
}
return 0;
}
You must admit: this is much simpler, isn't it?
I have a map with types <int, foo*> which I am populating from a database. int is a unique key so before populating I am checking if key is already in map if not than a new object of type foo is created and inserted in map.
The foo class also has another map as attribute with types <int, float> which also needs to be populated from the database. I have the below code which is supposedly simple:
std::map<int, foo> MAP;
while(getline(infile, line))
{ //reading records in data file
string item;
stringstream ss(line);
vector<string> splittedString;
int a = stoi(splittedString[0]); // cells after splitting data
int b = stoi(splittedString[1]);
float c = stof(splittedString[2]);
if (MAP.find(a) == MAP.end())
{
foo f = foo();
MAP.insert({a, &f});
f.fooMap.insert({b, c/100});
}
else
{
foo* f = MAP.at(a);
f->fooMap.insert({b, c/100});
}
}
This is taking a ridiculous amount of time to compute. There are about 50,000 records but that should not take forever. I am pretty sure I am doing something wrong since I am somewhat rusty with c++.
I know I have some redundancy when creating the foo object and I think a more efficient way of doing the above is the below 'incorrect' code:
while(getline(infile, line))
{//reading records in data file
string item;
stringstream ss(line);
vector<string> splittedString;
int a = stoi(splittedString[0]); //cells after splitting data
int b = stoi(splittedString[1]);
float c = stof(splittedString[2]);
foo f = *MAP[a];
if (f == NULL)
{
f = foo();
MAP.insert({a, &f});
}
f->fooMap.insert({b, c/100});
}
update
if I use MAP<int, foo> instead of MAP<int, foo*> (and arranging the insertion code accordingly), the code works normally. So I am doing something wrong with pointers. what is the best way to tackle this?
You code is a little bit more complicated than it needs to be. You should let std::map do the work instead of trying it yourself. What you want is the .emplace function of the container. It not only skips the insert if not necessary, but it also gives you the right entry to modify. For example, you could do something like:
#include <iostream>
#include <map>
#include <string>
int main()
{
//helper
auto print = [](auto MAP)
{
std::cout << "MAP:\n";
for (auto && each : MAP)
std::cout << each.first << "->" << each.second << '\n';
};
//work
std::map<int, std::string> MAP;
MAP.emplace(1,"").first->second = "one";
MAP.emplace(2,"").first->second = "two";
print(MAP);
MAP.emplace(1,"").first->second = "three";
print(MAP);
}
Which produces the following output:
MAP:
1->one
2->two
MAP:
1->three
2->two
Of course, this has this weird .first->second syntax, but we can refactor it into a new lambda:
auto update = [](auto& MAP, auto index, auto entry)
{
MAP.emplace(index,"").first->second = entry;
};
which lets you write the following:
update(MAP, 1, "one");
update(MAP, 2, "two");
print(MAP);
update(MAP, 1, "three");
print(MAP);
EDIT
Because you asked for a std::unique_ptr solution, here is it:
#include <iostream>
#include <map>
#include <string>
#include <memory>
int main()
{
//helper
auto print = [](const auto& MAP)
{
std::cout << "MAP:\n";
for (auto && each : MAP)
std::cout << each.first << "->" << *each.second.get() << '\n';
};
//work
std::map<int, std::unique_ptr<std::string>> MAP;
auto update = [](auto& MAP, auto index, auto entry)
{
*MAP.emplace(index, std::make_unique<std::string>()).first->second.get() = entry;
};
update(MAP, 1, "one");
update(MAP, 2, "two");
print(MAP);
update(MAP, 1, "three");
print(MAP);
}
It unfortunately gets a little bit unreadable and also annoying because std::unique_ptr cannot be copied. (This is just the way it is implemented.)
When I run the next code in VS2017:
#include <boost/pool/pool_alloc.hpp>
#include <map>
#include <iostream>
int main()
{
using Map = std::map<int, int, std::less<int>, boost::pool_allocator<std::pair<const int, int>>>;
using Pool = boost::singleton_pool<boost::pool_allocator_tag, sizeof(Map)>;
Map temp;
for (int i = 1; i < 5; i++) temp[i] = i;
std::cout << "First addresses:\n";
for (auto& kv : temp) std::cout << &kv.second << "\n";
temp.clear();
Pool::purge_memory();
Map temp2;
for (int i = 1; i < 5; i++) temp2[i] = i;
std::cout << "Second addresses:\n";
for (auto& kv : temp2) std::cout << &kv.second << "\n";
temp2.clear();
Pool::purge_memory();
return 0;
}
I get the output:
First addresses:
02A108F4
02A1090C
02A10924
02A1093C
Second addresses:
02A1090C
02A10924
02A1093C
02A10954
Live example
This behavior seems incorrect: what happened to address 02A108F4? It seems it was not returned to the pool during the purge.
This doesn't happen when I use a std::vector instead of a std::map.
gcc also seems to return the memory correctly: Live example.
Is this a bug in VS2017?
You're assuming things about the implementation details of the pool. You may well be right there is a loss, but you can't conclude this from the allocation patterns you see.
Also, you're purging the memory for the pool allocator associated with sizeof(int). However, the value_type is already std::pair<int const, int>, and that leaves the fact that the map implementation allocates an unspecified node-type instead.
Oh, and the reason your allocator worked is precisely the same: the container implementation knows you cannot possibly provide the right type of allocator, since the allocated type is unspecified. Therefore it will always rebind to get the required type.
So, at least make it
Live On Rextester
#include <boost/pool/pool_alloc.hpp>
#include <map>
#include <iostream>
using Map = std::map<int, int, std::less<int>, boost::pool_allocator<int>>;
using Pool = boost::singleton_pool<boost::pool_allocator_tag, sizeof(Map::value_type)>;
void foo() {
Map temp;
for (int i = 1; i < 5; i++) temp[i] = i;
std::cout << "First addresses:\n";
for (auto& kv : temp) std::cout << &kv.second << "\n";
}
int main()
{
foo();
Pool::purge_memory();
foo();
Pool::purge_memory();
}
This, though is still assuming implementation details. I think c++17 gives you some more information to work with (http://en.cppreference.com/w/cpp/container/node_handle) or otherwise you could see wheter Boost Container has the relevant details: https://www.boost.org/doc/libs/1_51_0/doc/html/boost/container/map.html#id463544-bb
I have the following typedefs
typedef map<string, IPAddressPolicyRulesInfo> SecondMap;
typedef map<string, SecondMap> FirstMap;
FirstMap CPCRF::m_mIMSI2PCRFInfo;
Within the c++ function:
within the function, i want to clear the 2nd map. Which is the best way to do this ? Any comments or suggestions are appreciated.
thanks
pdk
Hopefully this might get you started in the right direction:
#include <map>
#include <iostream>
// In general you shouldn't use _t after your own types (some people don't like it)
// But for a post here it doesn't matter too much.
typedef std::map<int, double> map1_t;
typedef std::map<int, map1_t> map2_t;
typedef map2_t::iterator it_t;
int main(int argc, char** argv)
{
// Create the map
map2_t m;
// Add some entries
{ map1_t h; h[0] = 1.1; h[1] = 2.2; m[5] = h; }
{ map1_t h; h[0] = 5.2; h[8] = 7.2; m[1] = h; }
// Output some information
std::cout << m.size() << std::endl;
std::cout << m[5].size() << std::endl;
// For each element in the outer map m
for (it_t it = m.begin(); it != m.end(); ++it)
{
// Assign a friendly name to the inner map
map1_t& inner = it->second;
// Clear the inner map
inner.clear();
}
// Output some information (to show we have done something)
std::cout << m.size() << std::endl;
std::cout << m[5].size() << std::endl;
return 0;
}
What I have here is two arrays of different types that I'm converting to vectors.
int ham_array[] = {32,71,12,45,26};
char word_array[] = {"cat", "bat", "green", "red", "taxi"};
vector < int > hamvector (ham_array, ham_array + 5);
vector < char > wordvector(word_array, word_array + 5);
I am going to call a sort function to sort the elements of ham_array from least to greatest. At the same time, I would like the word_array to also get sorted the same way ham_vector gets sorted using references.
For example,
after I call sort(hamvector)
ham_array[] = {12, 26, 32, 45, 71}
and sort(wordvector)
word_array[] = {"green", "taxi", "cat", "red", "bat"};
Is there an easy way to do this?
Well for one thing, that would be char *word_array[], the way you declared it would be a string.
Anyway the way to do this is you declare a structure to keep these things paired:
struct t {string name; int number;};
vector<t> list;
// fill in list
// comparer to compare two such structs
bool comparer(t &a, t &b) { return a.number>=b.number; }
// and to sort the list
sort(list.begin(), list.end(), comparer);
If by simple, you mean a more direct way then yes. The std::sort() does support sorting of raw arrays as well:
sort(word_array, word_array + 5, wordcmp);
As Blindy showed, you need a comparator function to tell sort how the ordering is suppose to be done for your list of words. Otherwise you'll end up sorting by the memory address that the string resides at instead of by the letters in your string. Something like this should work:
int wordcmp(const char *lhs, const char *rhs)
{
return strncmp(lhs, rhs, 256) < 0;
}
One other note, in practice you'll want to prefer std::vector over just raw pointer arrays since the latter isn't as safe.
I've tried to find a solution to a similar problem before and ultimately had to sort it manually. Another way I imagine you could do this would be to write a sorter functor that can somehow figure out, based on which string is being sorted, which integer is associated, and sort based on that. This is terribly inefficient, so I would highly advise doing your own manual sorting using std::swap.
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
template<typename KeyType, typename ValueType>
class CMappedSorter
{
std::map<KeyType, ValueType>* const m_Mappings;
public:
CMappedSorter(std::map<KeyType, ValueType>* Mappings) : m_Mappings(Mappings)
{
}
bool operator()(KeyType& LHS, KeyType& RHS)
{
const ValueType LHSSortingValue = m_Mappings->find(LHS)->second;
const ValueType RHSSortingValue = m_Mappings->find(RHS)->second;
return (LHSSortingValue < RHSSortingValue);
}
};
int main(int argc, char* argv[])
{
std::vector<int> Integers;
std::vector<std::string> Strings;
Integers.push_back(3);
Integers.push_back(1);
Integers.push_back(2);
Strings.push_back("Apple");
Strings.push_back("Banana");
Strings.push_back("Cherry");
std::map<std::string, int> Mappings;
if(Integers.size() == Strings.size())
{
const unsigned int ElementCount = Strings.size();
// Generate mappings.
auto StringsIterator = Strings.begin();
auto IntegersIterator = Integers.begin();
for(unsigned int i = 0; i < ElementCount; ++i)
{
Mappings[*(StringsIterator)] = *(IntegersIterator);
++StringsIterator;
++IntegersIterator;
}
// Print out before sorting.
std::cout << "Before Sorting" << std::endl;
std::cout << "Int\tString" << std::endl;
StringsIterator = Strings.begin();
IntegersIterator = Integers.begin();
for(unsigned int i = 0; i < ElementCount; ++i)
{
std::cout << *(IntegersIterator) << '\t' << *(StringsIterator) << std::endl;
++StringsIterator;
++IntegersIterator;
}
// Sort
std::sort(Strings.begin(), Strings.end(), CMappedSorter<std::string, int>(&(Mappings)));
std::sort(Integers.begin(), Integers.end());
// Print out after sorting.
std::cout << "After Sorting" << std::endl;
std::cout << "Int\tString" << std::endl;
StringsIterator = Strings.begin();
IntegersIterator = Integers.begin();
for(unsigned int i = 0; i < ElementCount; ++i)
{
std::cout << *(IntegersIterator) << '\t' << *(StringsIterator) << std::endl;
++StringsIterator;
++IntegersIterator;
}
}
else
{
std::cout << "Error: Number of elements in each container are not equivalent." << std::endl;
}
}