Erase element per key from multi_index_container with composite_key - boost

I have a multi_index_container with an index that is a composite_key.
But I can not find a way to erase an element by its key.
Please see below:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/composite_key.hpp>
using namespace boost::multi_index;
struct Point
{
int x, y;
};
void func()
{
multi_index_container<Point,indexed_by<
hashed_unique<
composite_key<Point,
member<Point,int,&Point::x>,
member<Point,int,&Point::y> >
> > > points;
points.find( boost::make_tuple( 3, 3 ) ); // <- works
points.erase( boost::make_tuple( 3, 3 ) ); // <- failes to compile
}
erase(key) works for indices that are not composite. But I am unable to find the correct syntax for composite keys.

erasedoesn't have the type of overloads that allow for interoperation with tuples (technically, this relates to the concept of compatible extensions.) But you can have the same effect with a little more code:
auto p=points.equal_range(boost::make_tuple(3,3));
points.erase(p.first,p.second);

Adding to the previous answer as per your request. You can have it like this:
Point p={3,3};
points.erase(points.key_extractor()(p));
The only problem with this is that it doesn't scale (if Point is expensive to construct.)

Related

std::vector of type deduced from initializers before C++17 ... any workaround for C++11?

I learned that from C++17, with the deduction guides, template arguments of std::vector can be deduced e.g. from the initialization:
std::vector vec = { function_that_calculate_and_return_a_specifically_templated_type() }
However I do not have the luxury of C++17 in the machine where I want to compile and run the code now.
Is there any possible workaround for C++11? If more solutions exist, the best would be the one that keep the readability of the code.
At the moment the only idea that I have is to track the various cases along the code (luckily they should not be too many) and make some explicit typedef/using.
Any suggestion is very welcome
The usual way to use type deduction for class template when CTAD is not available is providing a make_* function template, e.g. for your case (trailing return type is necessary for C++11):
#include <vector>
#include <type_traits>
#include <tuple>
template <class ...Args>
auto make_vec(Args&&... args) ->
std::vector<typename std::decay<typename std::tuple_element<0, std::tuple<Args...>>::type>::type>
{
using First = typename std::decay<typename std::tuple_element<0, std::tuple<Args...>>::type>::type;
return std::vector<First>{std::forward<Args>(args)...};
}
You can invoke the above with
const auto v = make_vec(1, 2, 3);
which gets at least kind of close to CTAD in the sense that you don't have to explicitly specify the vector instantiation.
While the answer by lubgr is a correct way, the following template is simpler and seems to work as well:
#include <vector>
#include <string>
template <typename T>
std::vector<T> make_vec(const std::initializer_list<T> &list)
{
return std::vector<T>(list);
}
int main()
{
auto v = make_vec({1,2,3});
auto v2 = make_vec({std::string("s")});
std::string s("t");
auto v3 = make_vec({s});
return v.size() + v2.size() + v3.size();
}
One advantage of using the initializer_list template directly are more clear error messages if you pass mixed types like in make_vec({1,2,"x"});, because the construction of the invalid initializer list now happens in non-templated code.

copying data between different types of containers

I am trying to copy data from one container type to another container type, the containers are both double dimensional vectors, i am using boost::combine and boost::tie to iterate over the containers, the code builds fine but throws a bad_alloc when run. i am not able to understand why a simple code like below throw bad_alloc, what am i doing wrong ?? also what are the other strategies to copy data between containers when they are multi-dimensional.
#include <vector>
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/range/combine.hpp>
#include <algorithm>
#include <tuple>
int
main(int ac, char **av)
{
std::vector<std::vector<int>> bv(1, std::vector<int>(4, 99));
boost::container::small_vector<boost::container::small_vector<int, 4>, 1> bvv;
decltype(bv)::value_type v1;
decltype(bvv)::value_type v2;
BOOST_FOREACH(boost::tie(v1, v2), boost::combine(bv, bvv)){
for ( auto &e : v1 )
v2.push_back( e );
}
return 0;
}
boost::combine expects the passed arguments have the same size. In your case bv has 1 vector<int> but bvv has 0 elements of boost::container::small_vector type. And for this reason program crashed. You can add 1 to constructor of small_vector
boost::container::small_vector<boost::container::small_vector<int, 4>, 1> bvv(1);
then program works, but it doesn't do what you expected. v2 vector is created as new local variable in main function and it is filled by elements of first vector from bv container. I think you want to fill bvv vector by elements from bv container.
If you want to modify bvv vector you should pass it as parameter to boost::tie function.
BOOST_FOREACH(boost::tie(bv[0], bvv[0]), boost::combine(bv, bvv)){
std::copy (bv[0].begin(), bv[0].end(), std::back_inserter(bvv[0]));
}
but it only works if bvv and bv have one element.
Maybe you should not use boost::combine and just write loop to iterate over all elements in bv and add them to bvv container.
for (const auto& v : bv)
{
bvv.push_back(boost::container::small_vector<int, 4>());
std::copy (v.begin(), v.end(), std::back_inserter(bvv.back()));
}

Directly assigning to a std::vector after reserving does not throw error but does not increase vector size

Let's create a helper class to assist visualizing the issue:
class C
{
int ID = 0;
public:
C(const int newID)
{
ID = newID;
}
int getID()
{
return ID;
}
};
Suppose you create an empty std::vector<C> and then reserve it to hold 10 elements:
std::vector<C> pack;
pack.reserve(10);
printf("pack has %i\n", pack.size()); //will print '0'
Now, you assign a new instance of C into index 4 of the vector:
pack[4] = C(57);
printf("%i\n", pack[4].getID()); //will print '57'
printf("pack has %i\n", pack.size()); //will still print '0'
I found two things to be weird here:
1) shouldn't the assignment make the compiler (Visual Studio 2015, Release Mode) throw an error even in Release mode?
2) since it does not and the element is in fact stored in position 4, shouldn't the vector then have size = 1 instead of zero?
Undefined behavior is still undefined. If we make this a vector of objects, you would see the unexpected behavior more clearly.
#include <iostream>
#include <vector>
struct Foo {
int data_ = 3;
};
int main() {
std::vector<Foo> foos;
foos.reserve(10);
std::cout << foos[4].data_; // This probably doesn't output 3.
}
Here, we can see that because we haven't actually allocated the object yet, the constructor hasn't run.
Another example, since you're using space that the vector hasn't actually started allocating to you, if the vector needed to reallocate it's backing memory, the value that you wrote wouldn't be copied.
#include <iostream>
#include <vector>
int main() {
std::vector<int> foos;
foos.reserve(10);
foos[4] = 100;
foos.reserve(10000000);
std::cout << foos[4]; // Probably doesn't print 100.
}
Short answers:
1) There is no reason to throw an exception since operator[] is not supposed to verify the position you have passed. It might do so in Debug mode, but for sure not in Release (otherwise performance would suffer). In Release mode compiler trusts you that code you provide is error-proof and does everything to make your code fast.
Returns a reference to the element at specified location pos. No
bounds checking is performed.
http://en.cppreference.com/w/cpp/container/vector/operator_at
2) You simply accessed memory you don't own yet (reserve is not resize), anything you do on it is undefined behavior. But, you have never added an element into vector and it has no idea you even modified its buffer. And as #Bill have shown, the vector is allowed to change its buffer without copying your local change.
EDIT:
Also, you can get exception due to boundary checking if you use vector::at function.
That is: pack.at(4) = C(57); throws exception
Example:
https://ideone.com/sXnPzT

C++11 container of borrowed unique_ptrs

I have a vector of unique_ptrs and want to filter it into a new vector of the same type.
vector<unique_ptr<Thing>> filter_things(const vector<unique_ptr<Thing>> &things) {
vector<unique_ptr<Thing>> things;
// i want the above line to be something like: vector<const unique_ptr<Thing> &>
// but I don't think this is valid
for (const unique_ptr<Thing> &thing : things) {
if (check(thing)) {
filtered.push_back(thing); // this part shouldn't work since it
// would duplicate a unique_ptr
}
}
return filtered;
}
I want the caller to maintain ownership of all the Things. I want the return value of this function to be purely read only (const), and I don't want to make copies as it is very expensive to copy a Thing.
What is the best way to accomplish this?
Is this possible with unique_ptrs?
In some sense, we are creating multiple references by returning a new vector of references, so unique_ptr may not make sense. However, it is purely read only! So there should be some way to make this work. The lifetime of ``things'' is guaranteed to be larger than the filtered things.
Note that the caller owns the parameter supplied.
You can use reference_wrapper from <functional>
#include <memory>
#include <functional>
#include <vector>
#include <iostream>
using namespace std;
struct Thing {};
using PThing = unique_ptr<Thing>;
using RefThing = reference_wrapper<const PThing>;
vector<RefThing> filter_things( const vector<PThing>& things )
{
vector<RefThing> filtered;
int i = 0;
for( auto&& thing : things )
{
if( i++%2 )
filtered.push_back( ref(thing) );
}
return filtered;
}
int main()
{
vector<PThing> vec;
vector<RefThing> flt;
vec.resize(25);
flt = filter_things(vec);
cout << flt.size() << endl;
}
If what you want is getting a filtered set of element not an actual container containing them, boost::range can be a good solution.
auto filtered_range(const std::vector<std::unique_ptr<Thing>> &things) {
return things | boost::adaptors::filtered([](const auto& thing) {
return check(thing);
});
}
I used some of c++14 syntax but I don't think it's hard to make it to c++11.
You can use it like this.
std::vector<std::unique_ptr<Thing> > things;
for(const auto& thing : filtered_range(things)) {
// do whatever you want with things satisfying 'check()'
}
One of disadvantages is that the range itself is not a container so if you traverse the range more than once, every 'thing' will be checked if it satisfies check().
If a container storing the checked things AND controlling the lifetime of things are what you really want, I would prefer using std::vector<std::shared_ptr<Thing> > and returning std::vector<std::weak_ptr<Thing> >. You can check if it's really one and the only ptr to a thing with std::shared_ptr::unique() before deleting it from things.

In C++, how to iterate array in reverse using for_each?

In C++11, using lambda/for_each, how do we iterate an array from end?
I tried the following, but both result in infinite loop:
for_each (end(A), begin(A), [](int i) {
....
});
for_each (A.rend(), A.rbegin(), [](int i) {
...
});
Any idea? Thanks.
You missed this ?
Flip your rbegin & rend
for_each (A.rbegin(), A.rend(), [](int i) {
...
});
Increasing reverse iterator moves them towards the beginning of the container
std::for_each( A.rbegin(), A.rend(), [](int i) { /*code*/ } ); is the simple solution.
I instead have written backwards which takes a sequence, extracts the begin and end iterator from it using the free begin and end functions (with std::begin and std::end using declarations nearby -- full ADL), creates reverse iterators around them, then returns a sequence with those two reverse iterators.
It is sort of neat, because you get this syntax:
for( int i : backwards(A) ) {
// code
}
which I find easier to read than std::for_each or manual for loops.
But I am a bit nuts.
Here is a minimal backwards. A full on solution handles adl and a few corner cases better.
template<class It, class C>
struct range_for_t{
It b,e;
C c; // for lifetime
It begin()const{return b;}
It end()const{return e;}
}
template<class It, class C>
range_for_t<It,C> range_for(It b,It e,C&& c){
return {std::move(b),std::move(e),std::forward<C>(c)};
}
template<class It>
range_for_t<It,int> range_for(It b,It e){
return {std::move(b),std::move(e)};
}
A simple range for range for only. Can be augmented with perfect forwarding.
Passing C as the container that may need lifetime extending. If passed as rvalue, copy is made, otherwise just reference. It is otherwise not used.
Next part is easy:
template<class It>
auto reverse_it(It it){
return std::reverse_iterator<It>(std::move(it));
}
template<class C>
auto backwards(C&&c){
using std::begin; using std::end;
auto b=begin(c), e=end(c);
return range_for(
reverse_it(e),reverse_it(b),
std::forward<C>(c)
);
}
That is untested but should work.
One important test is ensuring it works when you feed an rvalue vec like:
for(auto x:backwards(make_vec()))
works -- that is what the mess around storing C is about. It also assumes that moved container iterators have iterators who behave nicely.
Boost offers a feature named reversed, that can be used with C++ 11 range based for loop as describes Yakk in his answer:
for(int i : reverse(A))
{
// code
}
or
for(int i : A | reversed)
{
// code
}
Since C++20, there is a convenient adaptor for this:
#include <ranges>
...
for (auto& element: container | std::views::reverse)
For example:
#include <iostream>
#include <ranges>
#include <vector>
int main()
{
std::vector<int> container {1, 2, 3, 4, 5};
for (const auto& elem: container | std::views::reverse )
{
std::cout << elem << ' ';
}
}
// Prints 5 4 3 2 1
Try it here:
https://coliru.stacked-crooked.com/a/e320e5eec431cc87

Resources