so basically i have a vector of objects called "Things" that I populate over the console. These are the name of the object I populate them with (tom, coin, coin, bomb).
for (int i = 0; i < 5; i++){
for (Thing * t : *locations[i]->getThings()) {
if (t->getName().compare("bomb") == 0){
for (Thing * all : *locations[i]->getThings()){
if(all->getName().compare("tom") != 0){
locations[i]->remove(all);
}
}
}
}
}
This code is run every time to check if there is a "thing" object called bomb in the list and would remove every other object other than tom.
So from the populated example above, the expected list should just be {tom}. However when the code runs it is {tom, coin} which means it fails to delete the other "non-tom" object
The problem is that you are modify the Things vector while you are still iterating through it, so you are going to be invalidating the iterators that the for loops are using, which is undefined behavior.
You can do something more like this instead, using the Erase-Remove idiom:
const auto is_bomb = [](const Thing *t){ return t->getName() == "bomb"; };
const auto is_not_tom = [](const Thing *t){ return t->getName() != "tom"; };
...
auto *things = locations[i]->getThings();
if (std::any_of(things->begin(), things->end(), is_bomb)){
things.erase(
std::remove_if(things->begin(), things->end(), is_not_tom),
things->end()
);
}
Or, if you are using C++20 or later:
const auto is_bomb = [](const Thing *t){ return t->getName() == "bomb"; };
const auto is_not_tom = [](const Thing *t){ return t->getName() != "tom"; };
...
auto *things = locations[i]->getThings();
if (std::any_of(things->begin(), things->end(), is_bomb)){
std::erase_if(*things, is_not_tom);
}
Related
Let's assume that I have a class named Store which contains products. Functions are inlined for simplicity.
class Store
{
public:
Store(string name)
: _name(name)
{}
string getName() const
{ return _name; };
const std::vector<string> getProducts()
{ return _products; };
void addProduct(const string& product)
{ _products.push_back(product); }
private:
const string _name;
std::vector<string> _products;
};
Then I have a two dimensional string array which contains store-product -pairs. Same store can be multiple times in array.
string storeListing[4][2] = {{"Lidl", "Meat"},
{"Walmart", "Milk"},
{"Lidl", "Milk"},
{"Walmart", "Biscuits"}};
Now I want to iterate through array, create Store-object for each store in array and add products of it to object. So I need to use existing Store-object or create a new if there is no any with correct name yet. What is a way to implement this? Currently I'm trying to use pointer and set it to relevant object, but I'm getting sometimes segmentation faults and sometimes other nasty problems when I modify code slightly. I guess I'm calling some undefined behavior here.
std::vector<Store> stores;
for (int i = 0; i < 4; ++i) {
string storeName = storeListing[i][0];
string productName = storeListing[i][1];
Store* storePtr = nullptr;
for (Store& store : stores) {
if (store.getName() == storeName) {
storePtr = &store;
}
}
if (storePtr == nullptr) {
Store newStore(storeName);
stores.push_back(newStore);
storePtr = &newStore;
}
storePtr->addProduct(productName);
}
Most likely, because you insert "Store" copies into your vector:
if (storePtr == nullptr) {
Store newStore(storeName); //create Store on stack
stores.push_back(newStore); //Make a COPY that is inserted into the vec
storePtr = &newStore; // And this is where it all goes wrong.
}
newStore goes out of scope at the end of the if and StorePtr is lost.
Try it with:
storePtr = stores.back();
Or make your vector a std::vector<Store*>.
And then:
if (storePtr == nullptr) {
Store * newStore = new Store(storeName); //create Store on stack
stores.push_back(newStore); //Make a COPY that is inserted into the vec
storePtr = newStore; // And this is where it all goes wrong.
}
And of course, as the comments suggest, a std::map would be better suited here.
In short, std::map stores key-value pairs. The key would most likely be your store name, and the value the product.
Quick example:
std::map<std::string, std::string> myMap;
myMap["Lidl"] = "Milk";
myMap["Billa"] = "Butter";
//check if store is in map:
if(myMap.find("Billa") != myMap.end())
....
Note, you can of course use your Store object as value. To use it as key, you have to take care of a few things:
std::maps with user-defined types as key
For your specific example i would suggest you use a std::string as key, and a vector of Products as value.
Use a std::unordered_set<Store>, where the hash type is the string name of the store. Using a map-like type would lead to duplicated storage of the store name (one time as a key to the map and one time inside the Store object itself).
template <>
struct std::hash<Store> {
using Store = argument_type;
using result_type = std::size_t;
result_type operator()(const argument_type& s) const noexcept {
return result_type{ std::hash<std::string>{}(s._name) }();
}
};
std::unordered_set<Store> stores;
for (int i = 0; i < 4; ++i) {
string storeName = storeListing[i][0];
string productName = storeListing[i][1];
auto iter = stores.find(storeName);
if(iter == stores.end()) iter = stores.emplace(storeName);
iter->addProduct(productName);
}
There are a few problems in your approach.
Problem 1:
Store has a const data member. This will make it impossible to reorder the vector of stores. That needs to be corrected.
Problem 2:
You need to point at the right Store after insertion. Here's one approach:
// decompose the problem:
// first step - get a pointer (iterator) to a mutable store *in the vector*
auto locate_or_new(std::vector<Store>& stores, std::string const& storeName)
-> std::vector<Store>::iterator
{
auto iter = std::find_if(begin(stores), end(stores),
[&](Store& store)
{
return store.getName() == storeName;
});
if (iter == end(stores))
{
iter = stores.emplace(end(stores), storeName);
}
return iter;
}
//
// 2 - insert the product in terms of the above function.
auto addProduct(std::vector<Store>& stores, std::string const& storeName, std::string const& productName)
-> std::vector<Store>::iterator
{
auto istore = locate_or_new(stores, storeName);
istore->addProduct(productName);
return istore;
}
Note:
Since inserting objects into a vector can cause iterator invalidation, you will need to be careful to not hold references to objects inside the vector across sections of code that could create new stores.
if (storePtr == nullptr) {
Store newStore(storeName);
stores.push_back(newStore);
storePtr = &newStore;
}
Once the if ends newStore is gone and you are left with dangling pointer storePtr.
You could use an std::set<Store *> here
std::set<Store *> myset;
Store *c = new Store("store3");
std::set<Store *>::iterator iter = myset.find(c);
if(iter!=myset.end())
{
(*iter)->addProduct("Product1");
}
else
{
c->addProduct("Product1");
myset.insert(c);
}
There is a solution using vectors and iterators. Dont forget to include the "algorithm" header!
std::vector<Store> stores;
for (int i = 0; i < 4; ++i) {
string storeName = storeListing[i][0];
string productName = storeListing[i][1];
auto storeIter = std::find_if(stores.begin(), stores.end(), [storeName](Store store) -> bool {
return store.getName() == storeName;
}); //Find the store in the vector
if (storeIter == stores.end()) //If the store doesn't exists
{
stores.push_back(Store(storeName)); //Add the store to the vector
storeIter = prev(stores.end()); //Get the last element from the vector
}
Store* storePtr = &(*storeIter); //You can convert the iterator into a pointer if you really need it
storeIter->addProduct(productName);
//storePtr->addProduct(productName);
}
I'm iterating through a set of elements within a vector of structures and want to change an element in one of the structures. When I write to the element to change the value, the update isn't retained. Here is what I have:
first, in a header file:
std::vector<Sched::ScheduledEvent_t> v_SchedEvents;
typedef std::vector<Sched::ScheduledEvent_t>::const_iterator event_iter;
then later in a .cpp module...
for (event_iter i = v_SchedEvents.begin(); i != v_SchedEvents.end(); ++i)
{
ScheduledEvent_t event = *i;
if(event.member == true) {
event.member = false;
}
}
The value of event.member for the given structure in the vector isn't staying false. When returning to this loop, the conditional statement is run again.
Could it have anything to do with the typedef for the iterator?
Two problems here.
1) You're making a copy:
ScheduledEvent_t event = *i;
event is a copy of the element in the vector. Modifing event won't affect it.
2) You are using a const_iterator which only allows reading the value, not changing it.
Use a iterator instead
typedef std::vector<Sched::ScheduledEvent_t>::iterator event_iter;
and use it directly:
if (i->member) { // == true useless
i->member = false;
}
Or a for-range loop if you have access to C++11 or more recent:
for (auto & event : v_SchedEvents) {
if (event.member) {
event.member = false;
}
}
The const_iterator prevents the modification of the referenced value.
iterator : Behaves like value_type*.
const_iterator : Behaves like const_value_type*
vector<node> v;
v.push_back(node(10, 11));
for( std::vector<node>::const_iterator it = v.begin() ; it != v.end() ; ++it ){
node n = *it;
n.member = 12; //A local copy of node `*it`. So its ok to modify.
n = node(10, 13); //Okay since local Copy is not const
//(*it).member = 12; //error assignment of member 'node::a' in read-only object
//*it = node(10, 13); //error passing 'const node' as 'this' argument discards qualifiers
}
for( std::vector<node>::iterator it = v.begin() ; it != v.end() ; ++it ){
//cout << *it.a;
node n = *it;
n.a = 12;
n = node(10, 13); //No problem
(*it).a = 12; //No problem
*it = node(10, 13); //No problem
}
You are able to modify member value because you are getting a local copy of const_iterator referenced value by doing ScheduledEvent_t event = *i;
It is a simple matter of reference type and local variable.
When You do
ScheduledEvent_t event = *i;
You are creating a simple local variable "event" and copying everything from the source where "i" is pointing to.
So, if you wish to change the element in the structure change your code from
event.member = false;
to
*i.member = false;
or (my preference)
i->member = false;
I hope that helps.
I need to implement the following datastructure for my project. I have a relation of
const MyClass*
to
uint64_t
For every pointer I want to save a counter connected to it, which can be changed over time (in fact only incremented). This would be no problem, I could simply store it in a std::map. The problem is that I need fast access to the pointers which have the highest values.
That is why I came to the conclusion to use a boost::bimap. It is defined is follows for my project:
typedef boost::bimaps::bimap<
boost::bimaps::unordered_set_of< const MyClass* >,
boost::bimaps::multiset_of< uint64_t, std::greater<uint64_t> >
> MyBimap;
MyBimap bimap;
This would work fine, but am I right that I can not modify the uint64_t on pair which were inserted once? The documentation says that multiset_of is constant and therefore I cannot change a value of pair in the bimap.
What can I do? What would be the correct way to change the value of one key in this bimap? Or is there a simpler data structure possible for this problem?
Here's a simple hand-made solution.
Internally it keeps a map to store the counts indexed by object pointer, and a further multi-set of iterators, ordered by descending count of their pointees.
Whenever you modify a count, you must re-index. I have done this piecemeal, but you could do it as a batch update, depending on requirements.
Note that in c++17 there is a proposed splice operation for sets and maps, which would make the re-indexing extremely fast.
#include <map>
#include <set>
#include <vector>
struct MyClass { };
struct store
{
std::uint64_t add_value(MyClass* p, std::uint64_t count = 0)
{
add_index(_map.emplace(p, count).first);
return count;
}
std::uint64_t increment(MyClass* p)
{
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return add_value(p, 1);
}
else {
remove_index(it);
++it->second;
add_index(it);
return it->second;
}
}
std::uint64_t query(MyClass* p) const {
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return 0;
}
else {
return it->second;
}
}
std::vector<std::pair<MyClass*, std::uint64_t>> top_n(std::size_t n)
{
std::vector<std::pair<MyClass*, std::uint64_t>> result;
result.reserve(n);
for (auto idx = _value_index.begin(), idx_end = _value_index.end() ;
n && idx != idx_end ;
++idx, --n) {
result.emplace_back((*idx)->first, (*idx)->second);
}
return result;
}
private:
using map_type = std::map<MyClass*, std::uint64_t>;
struct by_count
{
bool operator()(map_type::const_iterator l, map_type::const_iterator r) const {
// note: greater than orders by descending count
return l->second > r->second;
}
};
using value_index_type = std::multiset<map_type::iterator, by_count>;
void add_index(map_type::iterator iter)
{
_value_index.emplace(iter->second, iter);
}
void remove_index(map_type::iterator iter)
{
for(auto range = _value_index.equal_range(iter);
range.first != range.second;
++range.first)
{
if (*range.first == iter) {
_value_index.erase(range.first);
return;
}
}
}
map_type _map;
value_index_type _value_index;
};
I want to fill some structure while finding minimum element. Pl find the code below
tyoedef struct Point
{
double x, y;
}Point;
I have a vector of points - std::vector<Point> V in which i have few thousand points.
There is another struct I have
typedef struct cart
{
Point pt;
double val_1; // computed using only Pt
double val_2; // computer using only Pt
}cart;
Now I have two tasks:
I need to find minimum element from structure V.
Fill the structure cart, which is directly dependent on V.
I can do this using following code.
std::vector<cart> vCart;
for(unsigned i = 0; i < V.size(); ++i)
{
cart thsElement;
thsElement.pt = V[i];
thsElement.val_1 = compute_val_1(V[i]);
thsElement.val_2 = compute_val_2(V[i]);
vCart.push_back(thsElement)
}
auto it = std::min_element(vCart.begin(), vCart.end(), lex_sort);
bool lex_sort(cart const &a, cart const &b)
{
if(a.pt.x < b.pt.x) return true;
if(a.pt.x == b.pt.x) return (a.pt.y < b.pt.y);
}
Now there is an evident problem with this implementation.
There are two loops. One for filling the structure and other for finding the min element (std::min_element() has to have a loop to iterate over all the values). I am fighting for few miliseconds' improvement. So this is not a good code. Moreover, this seems so C_style
So I came up with following code.
std::vector<cart> vCart;
std::iterator <vCart> st_ite;
auto it = std::min_element(V.begin(), V.end(), boost::bind(FillStruct_LexSort, st_ite, _1, _2)); // V is a vector of Point
bool FillStruct_LexSort(std::insert_iterator< std::vector<Cart>> vcpInput, const Point &a, const Point &b)
{
Cart thsPt;
if(a.x() < b.x())
{
thsPt.pt = b;
thsPt.val_1 = compute_val_1(b);
thsPt.val_2 = compute_val_2(b);
(*vcpInput++) = (thsPt);
return true;
}
if (a.x() == b.x())
{
if(a.y() < b.y())
{
thsPt.pt = b;
thsPt.val_1 = compute_val_1(b);
thsPt.val_2 = compute_val_2(b);
(*vcpInput++) = (thsPt);
return true;
}
}
thsPt.pt = a;
thsPt.val_1 = compute_val_1(b);
thsPt.val_2 = compute_val_2(b);
(*vcpInput++) = (thsPt);
return false;
}
Now, the problem is - I get segmentation fault. I do not know how should I use iterator to insert a value. I tried passing reference to vCart, but vCart is empty after calling min_element(..). I even tried insert_iterator, but with no success.
So pl suggest.
It seems you want something like:
bool lex_sort(const Point& lhs, const Point& rhs)
{
return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
}
and then
auto it = std::min_element(V.begin(), V.end(), &lex_sort);
if (it == V.end()) {
// V is empty.
} else {
Cart thsPt;
thsPt.pt = it;
thsPt.val_1 = compute_val_1(*it);
thsPt.val_2 = compute_val_2(*it);
return thsPt;
}
Note that if val_1/val_2 always depend of pt, you can add a constructor for Cart which take a Point
I have such code:
std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;
auto i = futOrders.find(orderId);
if (i == futOrders.end()) {
LimitOrder& newOrder = futOrders[orderId];
// work
} else {
LimitOrder& futOrder = i->second;
// another work
}
Here I execute "find" twice:
first time: auto i = futOrders.find(orderId);
second time: LimitOrder& newOrder = futOrders[orderId];
Can i rewrite it somehow to avoid "double find"?
You can perform an emplace, and check the return value to know whether the item was inserted or not:
std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;
auto i = futOrders.emplace(
std::piecewise_construct, std::tie(orderId), std::make_tuple());
if (i.second) {
LimitOrder& newOrder = i.first->second;
// work
} else {
LimitOrder& futOrder = i.first->second;
// another work
}
How about using size() to realize if an element was inserted, like this:
auto old_size = futOrders.size();
LimitOrder& order = futOrders[orderId];
if (old_size < futOrders.size()) {
LimitOrder& newOrder = order;
// work
} else {
LimitOrder& futOrder = order;
// another work
}
Assuming there is a way to "determine if an order is empty", you could do:
LimitOrder& anOrder = futOrders[orderId];
if (anOrder.empty())
{
// New order, do stuff that only new orders need.
}
else
{
// Old order, update it.
}
The empty method could of course be something like if (anOrder.name == "") or if (anOrder.orderId == 0), etc.
You can use this overload of insert instead:
std::pair<iterator,bool> insert( const value_type& value );
Example:
std::unordered_map<int, std::string> m { {0, "A"}, {1, "B"}, {2, "C"} };
int orderId = 1;
// attempt to insert with key you have and default constructed value type
auto p = m.insert( std::make_pair(orderId, std::string()) );
if (p.second) {
// the element was inserted
} else {
// the element was not inserted
std::cout << p.first->second; // will print "B"
}
In both cases, p.first is the iterator to the element you search for (or just got inserted).