Boost custom intervals with interval_map and closed bounds - c++

I am trying to use boost::icl::interval_map with a custom interval MyInterval and closed bounds (interval_bounds::static_closed), similarly to interval_set example. This construct is, however, throwing the following error:
---- Map State -----------------
[0,10] - A
prog.exe: /opt/wandbox/boost-1.71.0/gcc-head/include/boost/icl/interval_base_map.hpp:557: boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::iterator boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::gap_insert(boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::iterator, const interval_type&, const codomain_type&) [with Combiner = boost::icl::inplace_plus<std::__cxx11::basic_string<char> >; SubType = boost::icl::interval_map<int, std::__cxx11::basic_string<char>, boost::icl::partial_absorber, std::less, boost::icl::inplace_plus, boost::icl::inter_section, MyInterval>; DomainT = int; CodomainT = std::__cxx11::basic_string<char>; Traits = boost::icl::partial_absorber; Compare = std::less; Combine = boost::icl::inplace_plus; Section = boost::icl::inter_section; Interval = MyInterval; Alloc = std::allocator; boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::iterator = std::_Rb_tree<MyInterval, std::pair<const MyInterval, std::__cxx11::basic_string<char> >, std::_Select1st<std::pair<const MyInterval, std::__cxx11::basic_string<char> > >, boost::icl::exclusive_less_than<MyInterval>, std::allocator<std::pair<const MyInterval, std::__cxx11::basic_string<char> > > >::iterator; boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::interval_type = MyInterval; boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::codomain_type = std::__cxx11::basic_string<char>]: Assertion `this->_map.find(inter_val) == this->_map.end()' failed.
I have noticed that either (1) using another interval_bounds type, e.g. static_open, or (2) using default interval_type, i.e.interval<int>::closed(), works just fine. Using MyInterval and static_closed bounds combination seems to be the problem. What configuration am I missing or what have I done incorrectly?
Code: below or Wandbox.
Boost: 1.71
GCC: 9.2.0.
MyInterval.hxx
#ifndef MY_INTERVAL_HXX
#define MY_INTERVAL_HXX
#include <iostream>
#include <boost/icl/interval_map.hpp>
using namespace std;
using namespace boost::icl;
class MyInterval
{
public:
MyInterval(): _first(), _past(){}
MyInterval(int lo, int up): _first(lo), _past(up){}
int first()const{ return _first; }
int past ()const{ return _past; }
private:
int _first, _past;
};
namespace boost { namespace icl
{
template<>
struct interval_traits<MyInterval>
{
typedef MyInterval interval_type;
typedef int domain_type;
typedef std::less<int> domain_compare;
static interval_type construct(const domain_type &lo, const domain_type &up)
{ return interval_type(lo, up); }
static domain_type lower(const interval_type &inter_val) { return inter_val.first(); }
static domain_type upper(const interval_type &inter_val) { return inter_val.past(); }
};
template<>
struct interval_bound_type<MyInterval>
{
typedef interval_bound_type type;
BOOST_STATIC_CONSTANT(bound_type, value = interval_bounds::static_closed);
};
}} // namespace boost icl
#endif
Main.cxx
#include <iostream>
#include <cstdlib>
#include <string>
#include <boost/icl/interval_map.hpp>
#include "MyInterval.hxx"
using namespace boost::icl;
int main()
{
interval_map <int
, std::string
, partial_absorber
, std::less // ICL_COMPARE_INSTANCE(ICL_COMPARE_DEFAULT, int),
, inplace_plus // ICL_COMBINE_INSTANCE(inplace_plus, int),
, inter_section // ICL_SECTION_INSTANCE(inter_section, int),
, MyInterval
> imap;
std::string A("A");
std::string B("B");
std::string C("C");
auto Ai = MyInterval(0,10);
//auto Ai = interval<int>::closed(0,10);
auto Bi = MyInterval(5,15);
//auto Bi = interval<int>::closed(5,15);
auto Ci = MyInterval(7,12);
//auto Ci = interval<int>::closed(7,12);
imap += std::make_pair(Ai, A);
std::cout << "---- Map State -----------------" << std::endl;
for (const auto& val : imap) { std::cout << val.first << " - " << val.second << std::endl; }
std::cout << std::endl;
imap += std::make_pair(Bi, B);
std::cout << "---- Map State -----------------" << std::endl;
for (const auto& val : imap) { std::cout << val.first << " - " << val.second << std::endl; }
std::cout << std::endl;
imap += std::make_pair(Ci, C);
std::cout << "---- Map State -----------------" << std::endl;
for (const auto& val : imap) { std::cout << val.first << " - " << val.second << std::endl; }
std::cout << std::endl;
return 0;
}
Expected Output
---- Map State -----------------
[0,10] - A
---- Map State -----------------
[0,5) - A
[5,10] - AB
(10,15] - B
---- Map State -----------------
[0,5) - A
[5,7) - AB
[7,10] - ABC
(10,12] - BC
(12,15] - B

This is the same problem a friend of mine had once.
The problem is that the default constructor of your custom class is not right. I'm not sure if the boost documentation says this, but it must produce an invalid range. In your case, the default constructor is producing a range [0, 0].
Basically, the way the library checks if a range is valid is based on the type of bounds:
If it is static_open, for instance the function that checks if the interval is invalid is equivalent to upper() <= lower(). That is why it works in your code: 0 <= 0 -> true.
For the static_closed, the function that checks if the interval is invalid is equivalent to upper() < lower().
In order for it to work, you need to change the default constructor to produce something where upper() < lower().
TL;DR
Change the default constructor to something like:
MyInterval() : _first(0), _past(-1) {}

Related

Finding duplicate values of std::map at compile time

I have a template class (BiMap) which is used as a bidirectional map for lookup purposes e.g. an enum value mapped to a std::string equivalent and vice versa.
To achieve this the std::string values must also be unique to prevent duplicate std::string values returning the first found enum key during a search by value lookup.
template<typename Key, typename Value>
class BiMap {
public:
explicit BiMap(std::initializer_list<std::pair<Key, Value>> &&items) : bimap_{items.begin(), items.end()} {
assert(!HasDuplicates(bimap_));
}
Key GetKeyFromValue(const Value &value) const {
auto it = std::find_if(bimap_.begin(), bimap_.end(), [&value](const std::pair<Key, Value> &kvp) {
return kvp.second == value;
});
return (it != bimap_.end() ? it->first : Key());
}
Value GetValueFromKey(const Key &key) const {
auto it = bimap_.find(key);
return (it != bimap_.end() ? it->second : Value());
}
private:
const std::map<Key, Value> bimap_;
};
I use a function called HasDuplicates to check for any duplicate values:
template<typename Key, typename Value>
bool HasDuplicates(const std::map<Key, Value> &bimap) {
// Create a map to use the values as keys
std::map<Value, Key> value_key_map;
for (auto &kvp : bimap) value_key_map.emplace(kvp.second, kvp.first);
// If there are no duplicate values then the sizes should be the same
std::cout << "HasDuplicates: " << std::boolalpha << (value_key_map.size() != bimap.size()) << std::endl;
return (value_key_map.size() != bimap.size());
}
And I can run the following example code which will indicate at runtime whether there is any duplicate values:
// Test 1: No duplicates
std::cout << "**No duplicates test:**" << std::endl;
const BiMap<std::string, int> bi_map_no_dups({{"foo", 1}, {"bar", 2}, {"foobar", 3}});
std::cout << "foo: " << bi_map_no_dups.GetValueFromKey("foo") << std::endl;
std::cout << "bar: " << bi_map_no_dups.GetValueFromKey("bar") << std::endl;
std::cout << "foobar: " << bi_map_no_dups.GetValueFromKey("foobar") << std::endl;
// Test 2: Duplicates
std::cout << "**Duplicates test:**" << std::endl;
const BiMap<std::string, int> bi_map_dups({{"foo", 1}, {"bar", 2}, {"foobar", 1}});
std::cout << "foo: " << bi_map_dups.GetValueFromKey("foo") << std::endl;
std::cout << "bar: " << bi_map_dups.GetValueFromKey("bar") << std::endl;
std::cout << "foobar: " << bi_map_dups.GetValueFromKey("foobar") << std::endl;
The output of this would be:
**No duplicates test:**
HasDuplicates: false
foo: 1
bar: 2
foobar: 3
**Duplicates test:**
HasDuplicates: true
main.cpp:22: BiMap<Key, Value>::BiMap(std::initializer_list<std::pair<_T1, _T2> >&&) [with Key = std::basic_string<char>; Value = int]: Assertion `!HasDuplicates(bimap_)' failed.
A working example of the above code can be found here.
The Question:
How can I evaluate whether the std::map has duplicate values at compile time?
What I've tried:
I've already tried to implement the constexpr template function like here:
template <typename K, typename V> constexpr bool has_duplicates(const std::map<K,V> *map)
{
std::map<V,K> value_key_map;
for(auto &kvp : map) value_key_map.emplace(map->second,map->first);
return map->size() == value_key_map.size();
}
int main() {
// Cannot get this part to work
constexpr std::map<std::string, int> bimap({{"foo", 1}, {"bar", 2}, {"foobar", 1}});
static_assert(!has_duplicates(&bimap));
return 0;
}
Note: I'm using C++11 where I cannot yet declare local variables and loops inside the constexpr function and should thus revert to recursion as seen here. But, for this example I'm happy if I can find a suitable solution with C++14's constexpr features and I'll get a recursive version later on (if possible).
How can I evaluate whether the std::map has duplicate values at compile time?
You can't. std::map() isn't usable at compile time.
You could instead use https://github.com/serge-sans-paille/frozen or https://github.com/mapbox/eternal to make that check (or some other constexpr structure).
The other way (if you want to stay at the C++11 level) is to build a template metaprogramming based map, like in this answer.
With the help of the comment section, I was able to formulate a suitable solution to my problem.
Important: std::map is not constexpr and can't be used to evaluate its contents at compile time.
However, you can use std::array<std::pair<const K, V>, N> to assist in evaluating the contents at compile time from C++14 as follows:
template<typename K, typename T, size_t N>
constexpr bool has_duplicates(const std::array<std::pair<K, T>, N> *arr) {
for (int i = 0; i < N - 1; i++) {
for (int j = i + 1; j < N; j++) {
if (compare_equal((*arr)[i].second,(*arr)[j].second) ||
compare_equal((*arr)[i].first, (*arr)[j].first)) return true;
}
}
return false;
}
with usage:
constexpr std::array<std::pair<int, const char *>, 3> arrd{{ {1, "foobar"},
{2, "bar"},
{3, "foobar"}}};
static_assert(!has_duplicates(&arrd), "Duplicates Found!");
and you need the following additional comparison functions:
template<typename A, typename B>
constexpr bool compare_equal(A a, B b) {
return a == b;
}
template <>
constexpr bool compare_equal(const char * a, const char * b) {
return *a == *b && (*a == '\0' || compare_equal(a + 1, b + 1));
}
For C++11 support you need to change the has_duplicates function to a recursive implementation. Here is a fully working example of both versions.
You can thus use this in your class to check for duplicate values at compile time.

Finding the top K frequent Elements

I am trying to solve a question on leetcode which is finding the top k frequent elements. I think my code is correct but the output for a test case is failing.
Input: [ 4,1,-1,2,-1,2,3]
K=2
My answer comes out to be {1,-1} but the expected is {-1,2}. I am not sure where am i getting wrong.
struct myComp{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
if(a.second==b.second)
{
return a.first<b.first;
}
return a.second<b.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> mp;
for(int i=0;i<nums.size();i++)
{
mp[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,myComp> minheap;
for(auto x:mp)
{
minheap.push(make_pair(x.second,x.first));
if(minheap.size()>k)
{
minheap.pop();
}
}
vector<int> x;
while(minheap.size()>0)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
link: https://leetcode.com/problems/top-k-frequent-elements
In the minheap, pairs of <frequency, element> are being pushed. Since we want to sort these pairs on basis of frequency, we need to compare on the basis of frequency only.
Let's say there are two pairs a and b. Then for normal sorting, the comparison would be :
a.first < b.first;
And for reverse sorting, the comparison would be :
a.first > b.first;
In case of min-heap, we need reverse sorting. Hence, the following comparator makes your code pass all the test cases :
struct myComp
{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
return a.first > b.first;
}
};
There are several issues with your code.
Obviously there is somewhere using namespace std; in your code. This should be avoided. You will find many posts here on SO explaining, why it this should not be done.
Then we need to qualify all elements from the std library with std::, which makes the scope very clear.
Next: You do not need your own sorting function. Since you insert the elements from the pair in swapped order into the std::priority_queue, the sorting criteria is valid for the counter part, not for the key value. So, your sorting function was anyway wrong, because it was sorting accodring to "second" and not to "first". But if we have a standard sorting, we do not need a special sorting algorithm. A std::pair has a less-than operator. So, the definition can be simply:
std::priority_queue<std::pair<int, int>> minheap;
Then, your if statement
if(minheap.size()>k)
{
minheap.pop();
}
is wrong. You will allow only k values to be inserted. And this are not necessarily the biggest ones. So, you need to insert all values from the std::unordered map. And then they are sorted.
With some cosmetic changes the code will look like the below:
#include <iostream>
#include <utility>
#include <unordered_map>
#include <vector>
#include <queue>
std::vector<int> topKFrequent(std::vector<int>& nums, size_t k) {
std::unordered_map<int, int> mp;
for (size_t i = 0; i < nums.size(); i++)
{
mp[nums[i]]++;
}
std::priority_queue<std::pair<int, int>> minheap;
for (auto x : mp)
{
minheap.push(std::make_pair(x.second, x.first));
}
std::vector<int> x;
for (size_t i{}; i< k; ++i)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
}
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
std::vector result = topKFrequent(data, 2);
for (const int i : result) std::cout << i << ' '; std::cout << '\n';
return 0;
}
An additional solution
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
auto topKFrequent(std::vector<int>& nums, size_t k) {
// Count occurences
std::unordered_map<int, size_t> counter{};
for (const int& i : nums) counter[i]++;
// For storing the top k
std::vector<std::pair<int, size_t>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
// Test code
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
for (const auto& p : topKFrequent(data, 2))
std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
return 0;
}
And of course, we do have also the universal solution for any kind of iterable container. Including the definition for type traits using SFINAE and checking for the correct template parameter.
#include <iostream>
#include <utility>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>
// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()), // begin/end and operator !=
++std::declval<decltype(std::begin(std::declval<Container&>()))&>(), // operator ++
void(*std::begin(std::declval<Container&>())), // operator*
void(), // Handle potential operator ,
std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);
// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));
// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;
// Function to get the k most frequent elements used in any Container ------------------------------------------------
template <class Container>
auto topKFrequent(const Container& data, size_t k) {
if constexpr (is_iterable<Container>::value) {
// Count all occurences of data
Counter<Container> counter{};
for (const auto& d : data) counter[d]++;
// For storing the top k
std::vector<Pair<Container>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
else
return data;
}
int main() {
std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
for (const auto& p : topKFrequent(testVector, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
for (const auto& p : topKFrequent(cStyleArray, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
std::string s{"abbcccddddeeeeeffffffggggggg"};
for (const auto& p : topKFrequent(s, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double value = 12.34;
std::cout << topKFrequent(value,2) << "\n";
return 0;
}
Developed and tested with Microsoft Visual Studio Community 2019, Version 16.8.2.
Additionally compiled and tested with clang11.0 and gcc10.2
Language: C++17

std::for_each no known conversion for argument 1 from 'std::pair<const point, int>' to 'std::pair<point, int>&'

I cannot understand, as the both cases look so similar, in first for_each i cannot get reference to pair, and in second I get the reference to pair with no fuss. Could anybody please explain it to me?
#include <unordered_map>
#include <cstring>
#include <algorithm>
#include <iostream>
struct table_info_t {
char name[32] = {0};
int posx;
int posy;
table_info_t(const char *name, int posx, int posy) : posx(posx), posy(posy) {
strncpy(this->name, name, 31);
}
};
struct point {
int posx;
int posy;
point(int posx, int posy) : posx(posx), posy(posy) { }
};
size_t hashstruct(const char* hptr, size_t size) {
size_t h = 31;
for (size_t i = 0; i < size; i++) {
h = (h + *hptr) * 31;
hptr++;
}
return h;
}
#define MAP_OPERATORS(typ)\
namespace std {\
\
template<>\
struct hash<typ> {\
std::size_t operator()(const typ &k) const { return hashstruct((const char*)&k, sizeof(typ)); }\
};\
\
bool operator==(const typ& one, const typ& other) { return memcmp(&one, &other, sizeof(typ)) == 0; }\
};
MAP_OPERATORS(point); //hash structure and operator==
MAP_OPERATORS(table_info_t); //hash structure and operator==
int main(int argc, char** argv) {
std::unordered_map<point, int> sp;
sp[point(3, 4)] = 7;
std::for_each(sp.begin(), sp.end(),
[](std::pair<point, int> pair) {
std::cout << pair.first.posx << "+" <<pair.first.posy << "=" << pair.second << "\n";
});
std::unordered_map<table_info_t, const char *> m;
m[table_info_t("make my day", 3, 14)] = "make my day 3,14";
std::for_each(m.begin(), m.end(),
[](std::pair<const table_info_t, const char * >& pair)
{
std::cout << pair.first.name << pair.first.posx << pair.first.posy << " " << pair.second << "\n";
}
);
return 0;
}
Both structures do not differ so much, but when compiling this, I get this error:
In file included from .../4.8.2/include/c++/4.8.2/algorithm:62:0,
from .../src/main/c/hashtable.cpp:10:
.../4.8.2/include/c++/4.8.2/bits/stl_algo.h: In instantiation of '_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = std::__detail::_Node_iterator<std::pair<const point, int>, false, true>; _Funct = main(int, char**)::__lambda0]':
.../src/main/c/hashtable.cpp:45:24: required from here
.../4.8.2/include/c++/4.8.2/bits/stl_algo.h:4417:14: error: no match for call to '(main(int, char**)::__lambda0) (std::pair<const point, int>&)'
__f(*__first);
^
.../src/main/c/hashtable.cpp:43:24: note: candidates are:
[](std::pair<point, int> &pair) {
^
In file included from .../4.8.2/include/c++/4.8.2/algorithm:62:0,
from .../src/main/c/hashtable.cpp:10:
.../4.8.2/include/c++/4.8.2/bits/stl_algo.h:4417:14: note: void (*)(std::pair<point, int>&) <conversion>
__f(*__first);
^
.../4.8.2/include/c++/4.8.2/bits/stl_algo.h:4417:14: note: candidate expects 2 arguments, 2 provided
.../src/main/c/hashtable.cpp:43:53: note: main(int, char**)::__lambda0
[](std::pair<point, int> &pair) {
^
.../src/main/c/hashtable.cpp:43:53: note: no known conversion for argument 1 from 'std::pair<const point, int>' to 'std::pair<point, int>&'
and I need to remove the reference. However reference to pair<const table_info_t, const char * > is perfectly fine for the compiler in second for_each.
When you iterate over std::unordered_map<Key, Value>,
you iterate on std::pair<const KEY, VALUE>
In the second case, you take std::pair<const KEY, VALUE>& so it is fine.
You might even add const as you don't change the pair: const std::pair<const KEY, VALUE>&.
In the first case, you use another type std::pair<KEY, VALUE>&.
std::pair<KEY, VALUE> can be constructed from std::pair<const KEY, VALUE>. However, a temporary can not be bound to non-const lvalue-reference. So using std::pair<KEY, VALUE>& is invalid. Using std::pair<KEY, VALUE> is valid but does extra copies. See unexpected copies with foreach over a map for details.
If you have access to C++14, you may simplify it using a generic lambda:
[](const auto& p) {
std::cout << p.first.posx << "+" << p.first.posy << "=" << p.second << "\n";
});
In addition std::for_each can also be replaced by a range-based for loop (even in C++11):
for(const auto& p : sp) {
std::cout << p.first.posx << "+" << p.first.posy << "=" << p.second << "\n";
};

How can I preserve sequenced order with an ordered_non_unique index?

I have a boost::multi_index_container indexed by an ordered_non_unique key as well as sequenced. When I iterate over the non-unique index, the entries come out in the order they were added to the container rather than their position in the sequence.
How can I set up the non-unique index so that it preserves the insertion order? I tried making a composite_key with ordered_non_unique and sequenced, but since sequenced isn't a keyed index it doesn't compile.
Here's a minimal example (live version here):
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <iostream>
#include <vector>
using namespace boost::multi_index;
using namespace std;
struct Entry {
int nonUniqueInt;
string somethingExtra;
};
using Container_t = multi_index_container<Entry, indexed_by<
sequenced<>,
ordered_non_unique<
// ***** What can I put here? *****
member<Entry, int, &Entry::nonUniqueInt>
>
>>;
std::vector<Entry> const sampleData{
{1, "first"}, {2, "second"}, {3, "third"}, {3, "fourth"}, {2, "fifth"}, {1, "sixth"}
};
// fillFront should make the sequence LIFO
template <typename T>
void fillFront(T & container) {
for (auto & item : sampleData) {
container.push_front(item);
}
}
// fillBack should make the sequence FIFO
template <typename T>
void fillBack(T & container) {
for (auto & item : sampleData) {
container.push_back(item);
}
}
int main() {
Container_t container;
auto & sequenced = container.get<0>();
auto & ordered = container.get<1>();
fillFront(sequenced);
for(auto & entry : ordered) {
cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
}
cout << endl;
container.clear();
fillBack(sequenced);
for(auto & entry : ordered) {
cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
}
}
// Expected/desired output: Actual output:
// 1: sixth 1: first
// 1: first 1: sixth
// 2: fifth 2: second
// 2: second 2: fifth
// 3: fourth 3: third
// 3: third 3: forth
//
// 1: first 1: first
// 1: sixth 1: sixth
// 2: second 2: second
// 2: fifth 2: fifth
// 3: third 3: third
// 3: forth 3: forth
You want the items to "remain stable" for equivalent keys, in the ordered index.
Boost Multi Index doesn't support that. The "best" you could do is sort the iterators by their appearance in the insertion order index.
Use the random_access index for this.
using Container_t = multi_index_container<Entry, indexed_by<
random_access<>,
ordered_non_unique< member<Entry, int, &Entry::nonUniqueInt> >
>>;
Here's a demo:
int main() {
Container_t container;
auto & sequenced = container.get<0>();
fillFront(sequenced);
stabled_ordered(container, [](Entry const& entry) {
cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
});
cout << endl;
container.clear();
fillBack(sequenced);
stabled_ordered(container, [](Entry const& entry) {
cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
});
}
See it Live On Coliru.
The magic, of course, is stabled_ordered, which is a modified version of std::for_each taking a container and a functor:
template <typename Container, typename F, int RA = 0, int ONU = 1>
F stabled_ordered(Container const& container, F&& f);
The implementation iterates the Order-Non-Unique index (indicated by the ONU template argument) and calls the functor, but in the insertion order (indicated by the RA (random_access) for ranges with equivalent keys:
template <typename Container, typename F, int RA = 0, int ONU = 1>
F stabled_ordered(Container const& container, F&& f)
{
using RAIt = typename Container::template nth_index<RA> ::type::const_iterator;
using ONUIt = typename Container::template nth_index<ONU>::type::const_iterator;
auto& ordered = container.template get<ONU>();
for(ONUIt cursor = ordered.begin(); cursor != ordered.end(); )
{
// get range with equiv. keys
auto key_range = ordered.equal_range(ordered.key_extractor()(*cursor));
cursor = key_range.second;
// project into first index
std::vector<RAIt> v;
for(auto it = key_range.first; it != key_range.second; ++it)
v.push_back(boost::multi_index::project<RA>(container, it));
// put into original order
std::sort(v.begin(), v.end());
for_each(v.begin(), v.end(), [&f](RAIt const& it) { f(*it); });
}
return std::forward<F>(f);
}
Don't be intimidated by the typename .... ::template incantations: these are only there because I wanted to make the algorithm implementation more generic than you probably need :)

Using boost::mpl::iter_fold and also obtaining boost::mpl::vector of keys from boost::mpl::map

I am trying to write a small meta-program using boost.mpl that matches "named channels" between to audio formats using two channel maps.
The Name is also an integer (enumeration).
A simple example of what I am trying to achieve in a runtime might look like:
typedef int Name;
std::map<int, int> map_channels(const std::map<int, Name>& m1, const std::map<Name, int>& m2)
{
std::map<int, int> result;
for (std::map<int, Name>::const_iterator it = m1.begin(); it != m1.end(); ++it)
{
result[it->first] = m2.find(it->second)->second; // Ignoring errors for example
}
return result;
}
int main()
{
std::map<int, Name> m1;
m1[0] = 20;
m1[1] = 22;
m1[2] = 21;
std::map<Name, int> m2;
m2[19] = 1;
m2[20] = 2;
m2[21] = 0;
m2[22] = 3;
std::map<int, int> m3 = map_channels(m1, m2);
for (std::map<int, int>::iterator it = m3.begin(); it != m3.end(); ++it)
{
std::cerr << it->first << ":" << it->second << std::endl;
}
return 0;
}
I am trying to write a compile time version using boost.mpl and so far have something like:
typedef map<
pair<int_<0>, int_<20> >
, pair<int_<2>, int_<22> >
, pair<int_<3>, int_<21> >
> m1;
typedef map<
pair<int_<19>, int_<1> >
, pair<int_<20>, int_<2> >
, pair<int_<21>, int_<0> >
, pair<int_<22>, int_<3> >
> m2;
What i want to do is create a 3rd map from the other two maps that looks like:
typedef map<
pair<int_<0>, int_<2> >
, pair<int_<2>, int_<3> >
, pair<int_<3>, int_<0> >
> mapping;
I have tried the following ideas:
// TODO : Dont know how to obtain a vector of keys from a mpl map
typedef vector<int_<0>, int_<2>, int_<3> > m1_keys;
//typedef transform<m1, first<_1> >::type m1_keys; // This does not work. I tried a number of ideas and none worked
// Create the composed function that given a key for m1, looks up the value and then uses that value as a lookup to m2
// This is working fine.
typedef at<m2, at<m1, _1> > composed_m1_m2_expr;
typedef lambda<composed_m1_m2_expr>::type composed_m1_m2_lambda;
// TODO : This fold is no good eithre.
typedef iter_fold<m1_keys, map<>, insert<_1, pair<deref<_2>, apply<composed_m1_m2_lambda, _2 > > > >::type mapping;
Note: I am printing out the values for these things like:
struct print_iipair
{
template <typename data_tpt>
void operator()(data_tpt p)
{
(void)p;
std::cerr << "k: " << data_tpt::first::value << ", v: " << data_tpt::second::value << std::endl;
//std::cerr << "value: " << typeid(p).name() << std::endl;
}
};
struct print_value
{
template <typename data_tpt>
void operator()(data_tpt p)
{
(void)p;
std::cerr << "value: " << data_tpt::value << std::endl;
//std::cerr << "value: " << typeid(p).name() << std::endl;
}
};
...
for_each<m1_keys,_>(print_value());
for_each<mapping,_>(print_iipair());
My questions are:
1) How would I create a boost::mpl::vector containing the keys of a boost::mpl::map?
2) How would I implement this iter_fold? I think the issue is around the apply and the _2 used, but not sure how to overcome this problem
Thanks.
Well it turns out that a solution is to use mpl::fold instead of mpl::transform as you can't transform a mpl::map as it produces a container of the same type as the source sequence and uses mpl::push_back to construct it which does not work with a map.
My two solutions which seem to work are:
1) How would I create a boost::mpl::vector containing the keys of a boost::mpl::map?
// Get a mpl::vector containing just the keys from the map m1
typedef fold<m1, vector<>, push_back<_1, first<_2> > >::type m1_keys;
2) How would I implement this iter_fold?
// Create a mapping
typedef at<m2, at<m1, _1> > composed_m1_m2_expr;
typedef lambda<composed_m1_m2_expr>::type composed_m1_m2_lambda;
typedef fold<m1_keys, map<>, insert<_1, pair<_2, composed_m1_m2_lambda::apply<_2> > > >::type mapping;