there is a structure like this:
std::list<std::list<std::string>> data;
I need to go throu the top level list and append internal lists against some criteria. something like this:
std::for_each(data.begin(), data.end(),
[<some variable required for the logic>]
(const std::list<std::string>& int_list) {
if(...)
int_list.push_back(...);
});
you see this code is not valid, because for_each can't modify the sequence.
what would you recommend me to perform what I need (without modifying initial data structure)?
You can use a C++11 ranged based for loops like:
std::list<std::list<std::string>> data;
for (auto & e : data)
{
if (some_condition)
e.push_back(some_data)
}
Related
I have a global list of items (each with a few properties) in a module of my program. It's immutable and statically defined in the code, so no worries there.
For instance let's say I have vegetables, which are just an alias defining them to an immutable tuple with name (string), code (ubyte) and price (ushort).
I'd like to be able to access those either by name or by code ; so I thought since the list of vegetables is known at compile-time, I could probably construct associative arrays with references to these vegetables (so string=>vegetable and ubyte=>vegetable)
Here's the kind of thing I am trying to achieve :
static struct instructions
{
// list of Veggies
immutable instr[] list = [
Veggie("Potato" , 0xD0, 2),
Veggie("Carrot" , 0xFE, 5),
];
// genByCode and genByName being pure functions that get CTFE'd
// and return the desired associative array
immutable instr[ubyte] byCode = genByCode(list);
immutable instr[string] byName = genByName(list);
// overloaded function returns the right Veggie
instr get(string name) const
{ return byName[name]; }
instr get(ubyte code) const
{ return byCode[code]; }
}
With those generator functions (separated for clarity) of the form
pure instr[ubyte] genByCode(immutable Veggie[] list)
{
instr[ubyte] res;
foreach (i ; list)
res[i.code] = i;
return res;
}
I spent quite some time messing around but I couldn't it to work. Of course it would be trivial to construct at runtime, but clearly it should be possible to do it at compile time.
At first I thought it was an issue of mutability, so I tried marking everything (vegetables and vegetable lists) as immutable (as they should be anyway), but then I ran into issues which I think regard immutable tuples, and feel too lost to keep going.
Could I get help from someone with a clearer overview of the mechanisms at play here ? Thanks !
The data is already there, no need to construct a compile-time associative array.
Just iterate over it statically:
static auto get(int code)(){
static foreach(veggie; list)
static if(veggie.code == code)
return veggie;
}
...
void main(){
writeln(instructions.get!0xD0);
}
It may be slower than access through a hash map, but that's the life of CTFE.
To make sure it evaluates at compile time, you can use this:
template get(int code){
static foreach(veggie; list)
static if(veggie.code == code)
alias get = veggie;
}
I want to sort my objects according to some criteria (according to how big the items are)
My funcSort in multiset slows down calculations and makes the solution does not scale. How can I make it faster?
To avoid it I tried to use vector, sort it (should go quicker?) and change it into multiset. My solution however does not work, I am not sure what I do wrong?
Function arguments:
void deliver(const std::set<MyItem::Ptr> items, MyItem::Ptr item)
(Typedef of shared_ptr):
typedef boost::shared_ptr<MyItem> Ptr;
sort function:
auto funcSort = [item](MyItem::Ptr lhs, MyItem::Ptr rhs){
return lhs->howFar(item->howBig()) < rhs->howFar(item->howBig());
};
Original with multiset (SLOW when using funcSort):
std::multiset<MyItem::Ptr, decltype(funcSort)> sortedItems(funcSort);
for (MyItem::Ptr item : items){
sortedItems.insert(item);
}
My vector attempt (Error message):
std::vector<MyItem::Ptr> sortedItems;
for (MyItem::Ptr item : items)
{
sortedItems.push_back(item);
}
std::sort(sortedItems.begin(), sortedItems.end(), funcSort());
std::multiset<MyItem::Ptr> ms(sortedItems.begin(), sortedItems.end());
Error message:
__lambda1
auto funcSort = [item](MyItem::Ptr lhs, MyItem::Ptr rhs)
candidate expects 2 arguments, 0 provided
You got the sort call wrong. You just want to pass the funcSort, not call it.
Try it like this:
std::sort(sortedItems.begin(), sortedItems.end(), funcSort);
I have a question about modifying elements in boost::multi_index container.
What I have is the structure, containing some pre-defined parameters and
a number of parameters, which are defined at run-time, and stored in a map.
Here is a simplified version of the structure:
class Sdata{
QMap<ParamName, Param> params; // parameters defined at run-time
public:
int num;
QString key;
// more pre-defined parameters
// methods to modify the map
// as an example - mock version of a function to add the parameter
// there are more functions operating on the QMAP<...>, which follow the same
// rule - return true if they operated successfully, false otherwise.
bool add_param(ParamName name, Param value){
if (params.contains(name)) return false;
params.insert(name, value);
return true;
}
};
Now, I want to iterate over different combinations of the pre-defined parameters
of Sdata. To do this, I went for boost::multi_index:
typedef multi_index_container<Sdata,
indexed_by <
// by insertion order
random_access<>,
//by key
hashed_unique<
tag<sdata_tags::byKey>,
const_mem_fun<Sdata, SdataKey, &Sdata::get_key>
>,
//by TS
ordered_non_unique<
tag<sdata_tags::byTS>,
const_mem_fun<Sdata, TS, &Sdata::get_ts>
>,
/// more keys and composite-keys
>//end indexed by
> SdataDB;
And now, I want to access and modify the parameters inside the QMap<...>.
Q1 Do I get it correctly that to modify any field (even those unrelated to
the index), one needs to use functors and do something as below?
Sdatas_byKey const &l = sdatas.get<sdata_tags::byKey>();
auto it = l.find(key);
l.modify(it, Functor(...))
Q2 How to get the result of the method using the functor? I.e., I have a functor:
struct SdataRemoveParam : public std::unary_function<Sdata, void>{
ParamName name;
SdataRemoveParam(ParamName h): name(h){}
void operator ()(Sdata &sdata){
sdata.remove_param (name); // this returns false if there is no param
}
};
How to know if the remove_param returned true or false in this example:
Sdatas_byKey const &l = sdatas.get<sdata_tags::byKey>();
auto it = l.find(key);
l.modify(it, SdataRemoveParam("myname"));
What I've arrived to so far is to throw an exception, so that the modify
method of boost::multi_index, when using with Rollback functor will return
false:
struct SdataRemoveParam : public std::unary_function<Sdata, void>{
ParamName name;
SdataRemoveParam(ParamName h): name(h){}
void operator ()(Sdata &sdata){
if (!sdata.remove_param (name)) throw std::exception("Remove failed");
}
};
// in some other place
Sdatas_byKey const &l = sdatas.get<sdata_tags::byKey>();
auto it = l.find(key);
bool res = l.modify(it, SdataRemoveParam("myname"), Rollback);
However, I do not like the decision, because it increases the risk of deleting
the entry from the container.
Q3 are there any better solutions?
Q1 Do I get it correctly that to modify any field (even those
unrelated to the index), one needs to use functors and do something as
below?
Short answer is yes, use modify for safety. If you're absolutely sure that the data you modify does not belong to any index, then you can get by with an ugly cast:
const_cast<Sdata&>(*it).remove_param("myname");
but this is strongly discouraged. With C++11 (which you seem to be using), you can use lambdas rather than cumbersome user-defined functors:
Sdatas_byKey &l = sdatas.get<sdata_tags::byKey>(); // note, this can't be const
auto it = l.find(key);
l.modify(it, [](Sdata& s){
s.remove_param("myname");
});
Q2 How to get the result of the method using the functor?
Again, with lambdas this is very simple:
bool res;
l.modify(it, [&](Sdata& s){
res=s.remove_param("myname");
});
With functors you can do the same but it requires more boilerplate (basically, have SdataRemoveParam store a pointer to res).
The following is just for fun: if you're using C++14 you can encapsulate the whole idiom very tersely like this (C++11 would be slightly harder):
template<typename Index,typename Iterator,typename F>
auto modify_inner_result(Index& i,Iterator it,F f)
{
decltype(f(std::declval<typename Index::value_type&>())) res;
i.modify(it,[&](auto& x){res=f(x);});
return res;
}
...
bool res=modify_inner_result(l,it, [&](Sdata& s){
return s.remove_param("myname");
});
Let Action be a class with a is_finished method and a numeric tag property.
Let this->vactions be a std::vector<Action>
The intent is to iterate the vector and identify those Actions who are finished,
store their tags in a std::vector<unsigned int> and delete the actions.
I tried to play with lambdas and a little and came up with a little
code that read nicely but caused memory corruptions. The "extended" version,
on the other hand, works as expected.
I suspect foul play in the remove_if part, but for the life of me I can't figure
out what's wrong.
Here's the example code.
This causes memory corruptions
std::vector<unsigned int> tags;
auto is_finished=[p_delta](Action& action) -> bool {return action.is_finished();};
//This is supposed to put the finished actions at the end of the vector and return
//a iterator to the first element that is finished.
std::vector<Action>::iterator nend=remove_if(this->vactions.begin(), this->vactions.end(), is_finished);
auto store_tag=[&tags](Action& action)
{
if(action->has_tag())
{
tags.push_back(action->get_tag());
}
};
//Store the tags...
for_each(nend, this->vactions.end(), store_tag);
//Erase the finished ones, they're supposed to be at the end.
this->vaction.erase(nend, this->vaction.end());
if(tags.size())
{
auto do_something=[this](unsigned int tag){this->do_something_with_tag(tag);};
for_each(tags.begin(), tags.end(), do_something);
}
This, on the other side, works as expected
std::vector<Action>::iterator ini=this->vactions.begin(),
end=this->vactions.end();
std::vector<unsigned int> tags;
while(ini < end)
{
if( (*ini).is_finished())
{
if((*ini).has_tag())
{
tags.push_back((*ini).get_tag());
}
ini=this->vaction.erase(ini);
end=this->vaction.end();
}
else
{
++ini;
}
}
if(tags.size())
{
auto do_something=[this](unsigned int tag){this->do_something_with_tag(tag);};
for_each(tags.begin(), tags.end(), do_something);
}
I am sure there's some rookie mistake here. Can you help me spot it?.
I thought that the for_each could be updating my nend iterator but found
no information about it. What if it did? Could the vector try to erase beyond the "end" point?.
std::remove_if does not preserve the values of the elements that are to be removed (See cppreference). Either get the tag values before calling remove_if - as you do in the second case - or use std::partition instead.
I'm writing a search algorithm in C++, and one of the things I need to do is have a few if statements that check cells above, below, left of, and right of.
Each time a cell is found to be open and added to the stack, I want it added to a list of cells already checked.
I want to be able to say in the if statement if(thisCell is not in checkedCells).
Any simple ideas?
For this purpose it's better to use the std::set container, because it provides you with the ability to search for items faster than a list. Then you can write:
std::set<itemType> myset;
...
if (myset.find(item) != myset.end()) {
// item is found
}
A larger example can be found by googling. For example, here.
If the number of items are in the hundreds, you can use simple, sequential search. This algorithm is built-into C++ as the find() function:
#include <algorithm> // for find()
typedef std::vector<Cell> CellList;
CellList checked_cells;
// .....
Cell cellToSearch;
if (is_in_checked_cells (cellToSearch, cells))
{
// .....
}
// Makes a sequential search using find().
static bool
is_in_checked_cells (const Cell &cell, const CellList &cells)
{
CellList::const_iterator end = cells.end ();
CellList::const_iterator item = std::find (cells.begin (), end, cell);
return (item != end);
}
Make sure Cell has operator< overridden.
If the list is very large, you may want to use binary search, which also comes bundled with C++:
#include <algorithm> // for sort() and binary_search()
CellList checked_cells;
// Make sure the cells are sorted.
checked_cells.sort (checked_cells.begin (), checked_cells.end ());
Cell cellToSearch;
if (is_in_checked_cells (cellToSearch, cells))
{
// .....
}
// Searches using binary_search().
static bool
is_in_checked_cells (const Cell &cell, const CellList &cells)
{
return std::binary_search (cells.begin (), cells.end (), cell);
}