Insert into boost::BIMAP using BOOST::associative property map ... failed - c++

With reference to my previously asked question about boost::bimaps and boost associative property maps interface here, I want to use Put and Get helper functions for my bimap.
With reference to a sample code given here, I tried to add the following and i get a long compile error for assertion failed ... Here is the code :
#include <boost/bimap.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/bimap/property_map/set_support.hpp>
#include <iostream>
using namespace boost;
int main()
{
typedef int vertex_descriptor_t;
typedef boost::bimaps::bimap< vertex_descriptor_t, size_t > vd_idx_bimap_t;
typedef boost::associative_property_map<vd_idx_bimap_t::left_map> asso_vd_idx_bimap_t;
// define bimap
vd_idx_bimap_t my_bimap;
asso_vd_idx_bimap_t my_asso_bimap(my_bimap.left);
typedef typename vd_idx_bimap_t::value_type value_type;
my_bimap.insert( value_type( 1, 100 ) );
// print bimap
for(auto t = my_bimap.left.begin(); t != my_bimap.left.end(); ++t)
std::cout << t->first << " " << t->second << "\n";
int z = 1;
std::cout << "value = " << get ( my_bimap.left, z ) << std::endl; // prints correctly value = 100
// ERROR here .
boost::put( my_asso_bimap, 2, 19 );
}
It gives error as: ( a long list. but i have just put a snippet )
cannot convert âboost::bimaps::detail::non_mutable_data_unique_map_view_access<Derived, Tag, BimapType>::operator[](const CompatibleKey&)::BIMAP_STATIC_ERROR__OPERATOR_BRACKET_IS_NOT_SUPPORTED360::assert_arg<long unsigned int>()â (type âmpl_::failed************ (boost::bimaps::detai
There is also one error which gives me error at line number 364 of the file (property_map.hpp) in boost
put(const put_get_helper<Reference, PropertyMap>& pa, K k, const V& v)
{
static_cast<const PropertyMap&>(pa)[k] = v;
}
I understand the error that associative map cannot mutate the data as it references to the left map view . but How do I use put and get helper functions ?
I can use GET (my_bimap.left, z ) functions, but I am not able to use PUT function. I wanted to use associative property map for get and put functions to operate on actual bimap so that i dont have to use insert( value_type() )...
I hope I am clear enough for my problem. Please suggest.

In general you cannot update bimap entries via iterators:
The relations stored in the Bimap will not be in most cases modifiable directly by iterators because both sides are used as keys of key-based sets. When a bimap left view iterator is dereferenced the return type is signature-compatible with a std::pair< const A, const B >.
So there's your answer. Likewise, you couldn't
my_bimap.left[2] = 19;
http://www.boost.org/doc/libs/release/libs/bimap/doc/html/boost_bimap/the_tutorial/differences_with_standard_maps.html#boost_bimap.the_tutorial.differences_with_standard_maps.iterator__value_type
Now, reading a bit more on there leads me to "suspect" the following solution:
typedef bm::bimap< vertex_descriptor_t, bm::list_of<size_t> > vd_idx_bimap_t;
Disclaimer: I don't know about the semantics that this changes (?) but it at least appears to support writable references. The below sample prints
1 100
value = 100
1 100
2 42
See it Live On Coliru
Full Listing
#include <boost/bimap.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/bimap/property_map/set_support.hpp>
#include <boost/bimap/list_of.hpp>
#include <iostream>
using namespace boost;
int main()
{
typedef int vertex_descriptor_t;
namespace bm = boost::bimaps;
typedef bm::bimap< vertex_descriptor_t, bm::list_of<size_t> > vd_idx_bimap_t;
typedef boost::associative_property_map<vd_idx_bimap_t::left_map> asso_vd_idx_bimap_t;
// define bimap
vd_idx_bimap_t my_bimap;
asso_vd_idx_bimap_t my_asso_bimap(my_bimap.left);
typedef typename vd_idx_bimap_t::value_type value_type;
my_bimap.insert( value_type( 1, 100 ) );
// print bimap
for(auto t = my_bimap.left.begin(); t != my_bimap.left.end(); ++t)
std::cout << t->first << " " << t->second << "\n";
int z = 1;
std::cout << "value = " << get ( my_bimap.left, z ) << std::endl; // prints correctly value = 100
boost::put( my_asso_bimap, 2, 42 );
for(auto t = my_bimap.left.begin(); t != my_bimap.left.end(); ++t)
std::cout << t->first << " " << t->second << "\n";
}

Related

How can I order a map that has duplicated values in C++?

I am trying to reorder the map in a descending way depending on the values, I have been trying to create a new map and insert the one which has the biggest value first but it keeps ordering the map by the keys.
I have also tried to reorder it by the value changing the form of the map into the other way but I will loose some data because I have more than one key which has the same value.
#include <iostream>
#include "SymbolFreq.h"
#include <string>
#include <fstream>
#include <streambuf>
#include <map>
using namespace std;
int main()
{
map <char, int> mymap;
map <char, int> realmap;
ifstream infile{ "ToCompress.txt" };
std::string str((std::istreambuf_iterator<char>(infile)),
std::istreambuf_iterator<char>());
std::map<char, int>::iterator itera;
for (auto it = str.begin(); it != str.end(); ++it)
{
itera = mymap.find(*it);
if (itera != mymap.end())
{
itera->second++;
}
else
{
mymap.insert({ *it, 1 });
}
}
int max = 0;
char provisionalChar;
int provisionalInt;
while (mymap.empty() == false)
{
for (auto it = mymap.cbegin(); it != mymap.cend(); ++it)
{
if (it->second > max)
{
max = it->second;
provisionalChar = it->first;
provisionalInt = it->second;
}
//cout << it->first << "\t" << it->second << "\n";
}
mymap.erase(provisionalChar);
realmap.insert({ provisionalChar, provisionalInt });
max = 0;
}
for (auto it = realmap.cbegin(); it != realmap.cend(); ++it)
{
cout << it->first << "\t" << it->second << "\n";
}
return 0;
}
If I understand the question properly, you'd like to count how many times each char appears in the file and then produce a map sorted with the char that appeared most time first.
Here's one idea:
#include <algorithm>
#include <cstdint>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <map>
#include <string>
#include <unordered_map>
int main() {
std::ifstream infile{"ToCompress.txt"};
// "mymap" is only used for counting how many times each char appears.
std::unordered_map<char, std::uintmax_t> mymap;
// Loop directly over the file. No vector needed:
std::for_each(std::istreambuf_iterator<char>(infile),
std::istreambuf_iterator<char>(), [&mymap](char ch) {
// No need to find first - operator[] inserts an element
// for the key ("ch") if it's missing.
++mymap[ch];
});
// Transform the unordered_map into a multimap where the count is the key
// and in which we use a descending sort order (std::greater):
std::multimap<std::uintmax_t, char, std::greater<std::uintmax_t>> realmap;
std::transform(mymap.begin(), mymap.end(),
std::inserter(realmap, realmap.end()),
[](const auto& ch_co) -> std::pair<std::uintmax_t, char> {
// return a pair with key and value swapped
return {ch_co.second, ch_co.first};
});
// Print the result
for(auto& [count, ch] : realmap) {
std::cout << count << '\t' << ch << '\n';
}
}
Possible output:
537479
120204 t
113285 e
80681
80670 i
79862 n
77984 r
77464 s
69994 o
67377 a
...
Apparently, <space>, t, e and \n are tne most common characters in my C++ programs (which is what I used as input)
Your question may be ill-posed; take a step back and state what you are really trying to accomplish.
That said, I'll attempt an answer based on what you've written.
It looks like you're trying to sort an std::map by value, in which case your question is a duplicate of either this or this question.
Regarding your initial attempt:
Take a look at this table. Only sequence containers allow you to directly influence order. As with priority queue you have limited control over the order of associative containers and almost zero control for unordered containers.

Map reference confusion

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>

float value used as a key in multimap

If compare between float, I think cannot just use equal ==, need to check if abs(a-b) < epsilon. So when float type value is used as a key, can we use equal_range function?
such as:
std::multimap<float, string> ds;
ds.insert(make_pair(2.0, string("a")));
ds.insert(make_pair(2.0, string("b")));
ds.insert(make_pair(3.0, string("d")));
ds.equal_range(2.0)
std::multimap::equal_range is not actually calculated using operator== at all. It is calculated using < AND > only. It is actually two iterators, the first being the std::multimap::lower_bound (first element not less than the given key) and the second being the std::multimap::upper_bound (first element greater than the given key).
So it is quite safe to use with floats and doubles.
Just test it and you'll see it obviously works.
#include <map>
#include <string>
#include <iostream>
int main()
{
std::multimap<float, std::string> ds;
ds.emplace(2.0f, std::string("a"));
ds.emplace(2.0f, std::string("b"));
ds.emplace(3.0f, std::string("d"));
auto r = ds.equal_range(2.0f);
for ( auto it = r.first; it != r.second; ++it )
std::cout << it->second << std::endl;
}
Output:
a
b
You can define your own less-operator for float. See the following example.
// http://www.cplusplus.com/reference/map/multimap/
#include <map>
#include <cassert>
#include <iostream>
#include <algorithm>
class CApproxFloatLess {
double m_eps;
public:
CApproxFloatLess(float eps) :
m_eps(eps)
{
assert(eps >= 0);
}
bool operator () (float x, float y) const {
return x + m_eps*(1+std::abs(x)) < y;
}
};
template <class It>
void info(float x, It& it) {
std::cout << "Found pair (" << it->first << ", " << it->second << ") for " << x << ".\n";
}
int main() {
typedef std::multimap<float,std::string,CApproxFloatLess> MyMap;
MyMap ds(CApproxFloatLess(1e-3));
ds.insert(make_pair(2.0, std::string("a")));
ds.insert(make_pair(2.0, std::string("b")));
ds.insert(make_pair(3.0, std::string("d")));
float x=2.001;
MyMap::iterator it=ds.find(x);
if( it != ds.end() )
info(x,it);
x=1.999;
it=ds.find(x);
if( it != ds.end() )
info(x,it);
x=2.01;
it=ds.find(x);
if( it != ds.end() )
info(x,it);
x=3.001;
it=ds.find(x);
if( it != ds.end() )
info(x,it);
return 0;
}
The output of this program is:
Found pair (2, a) for 2.001.
Found pair (2, a) for 1.999.
Found pair (3, d) for 3.001.
Who says you cannot use == to compare two floats? == works perfectly fine for floats; it will return true if they are equal and false if they are different. (There is some odd things about NaN's and negative zeroes, but that's not covered by a range check).
Obviously if you search for a value that isn't equal using == to any value in the multi map, it won't be found. And if you add two values that are as close to each other as possible, they will both be added.

resizing boost::multi_array to match another

I need to resize one multi_array to the size of another.
In Blitz++ I could just do
arr1.resize(arr2.shape());
Is there a multi_array solution of similar length? Because
arr1.resize(boost::extents[arr2.shape()[0]][arr2.shape()[1]]);
seems a little long and arduous.
You can use the shape() member. Sadly it cannot directly serve as an ExtentList (it doesn't model the Collection concept) but it's easy to make it into one:
using MA = multi_array<double, 2>;
MA ma(extents[12][34]);
auto& ma_shape = reinterpret_cast<boost::array<size_t, MA::dimensionality> const&>(*ma.shape());
So that
// demo
std::cout << "[" << ma_shape[0] << "][" << ma_shape[1] << "]\n";
Prints [12][34].
Now, ma_shape can directly be used to reshape/resize another array:
Live On Coliru
#include <boost/multi_array.hpp>
#include <iostream>
int main() {
using namespace boost;
using MA = multi_array<double, 2>;
MA ma(extents[12][34]);
auto& ma_shape = reinterpret_cast<boost::array<size_t, MA::dimensionality> const&>(*ma.shape());
// demo
std::cout << "[" << ma_shape[0] << "][" << ma_shape[1] << "]\n";
// resize
MA other;
assert(!std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
other.resize(ma_shape);
assert(std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
// reshape
other.resize(extents[1][12*34]);
assert(!std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
other.reshape(ma_shape);
assert(std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
}
I think this is another oversight of Boost.MultiArray. I have written a bunch of "utility" functions that allows to take the shape (dimension sizes), base indices (e.g. 0 or 1 for each dimension) and extensions (base indices and sizes in each dimension).
namespace boost{
template<class MultiArray>
detail::multi_array::extent_gen<MultiArray::dimensionality>
extension(MultiArray const& ma){ //this function is adapted from
typedef detail::multi_array::extent_gen<MultiArray::dimensionality> gen_type;
gen_type ret;
typedef typename gen_type::range range_type;
for(int i=0; i != MultiArray::dimensionality; ++i)
ret.ranges_[i] = range_type(ma.index_bases()[i], ma.index_bases()[i]+ma.shape()[i]);
return ret;
}
}
Which is later used as:
boost::multi::array<double, 3> m(boost::multi::extents[3][4][5]);
boost::multi::array<double, 3> n(extension(m)); // n takes the extension (and shape) of m
(If the base indexes are not zero it also works. It works also for view and other multi_array-type classes.)

c++ map clear inside a map

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