iterating through multiset of structs - c++

I'm not getting the syntax right. Lets say I have this...
#include <set>
...
struct foo{
int bar;
string test;
};
struct comp{
inline bool operator()(const foo& left,const foo& right){
return left.bar < right.bar;
}
};
int main(){
std::multiset<foo,comp> fooset;
std::multiset<foo,comp>::iterator it;
...//insert into fooset
for (it = fooset.begin(); it != fooset.end(); it++){
//how do i access int bar and string test of each element?
}
return 0;
}
How do i access int bar and string test of each element inside the for loop?
Thanks!

There is a good mnemonic rule that an iterator is a safe C++ abstraction for pointer.
So basically you access the elements through dereferencing syntax:
(*it).bar = 0;
it->test = "";

for (it = fooset.begin(); it != fooset.end(); it++)
{
foo const & f = *it; //const is needed if it is C++11
//use f, e.g
std:: cout << f.bar <<", " << f.test << std::endl;
}
In C++11, you could do this instead:
for(foo const & f : fooset)
{
//use f, e.g
std:: cout << f.bar <<", " << f.test << std::endl;
}

Related

How can I access the element of my own array class by the square brackets overload?

Take a look at this simplified version of what I'm coding and you'll understand the problem...
#include <iostream>
template<typename T, int N = 2>
class Array
{
T m_arr[N]{ 0 };
int m_size = N;
public:
T& operator[](const int& index) { return m_arr[index]; }
};
int main()
{
Array<int, 10>* thing = new Array<int, 10>();
thing[2] = 5;
std::cout << thing[2] << std::endl;
}
Now, obviously the first thing that comes to mind is, hey, just like I overloaded the [] operator, lets overload the = and the << operators, but that wouldn't work, since that is only valid for doing something like this...
thing = whatever;
std::cout << thing << std::endl;
But how could I do it for...
thing[num] = whatever;
std::cout << thing[num] << std::endl;

Sort just one member of the classes in a vector, leaving the other members unchanged

There are tons of answers for sorting a vector of struct in regards to a member variable. That is easy with std::sort and a predicate function, comparing the structs member. Really easy.
But I have a different question. Assume that I have the following struct:
struct Test {
int a{};
int b{};
int toSort{};
};
and a vector of that struct, like for example:
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
I do not want to sort the vectors elements, but only the values in the member variable. So the expected output should be equal to:
std::vector<Test> tvSorted{ {1,1,5},{2,2,6},{3,3,7},{4,4,8},{5,5,9} };
I wanted to have the solution to be somehow a generic solution. Then I came up with a (sorry for that) preprocessor-macro-solution. Please see the following example code:
#include <iostream>
#include <vector>
#include <algorithm>
struct Test {
int a{};
int b{};
int toSort{};
};
#define SortSpecial(vec,Struct,Member) \
do { \
std::vector<decltype(Struct::Member)> vt{}; \
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [](const Struct& s) {return s.Member; }); \
std::sort(vt.begin(), vt.end()); \
std::for_each(vec.begin(), vec.end(), [&vt, i = 0U](Struct & s) mutable {s.Member = vt[i++]; }); \
} while (false)
int main()
{
// Define a vector of struct Test
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
// Call sort macro
SortSpecial(tv, Test, toSort);
std::cout << "\n\nSorted\n";
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
Since macros shouldn't be used in C++, here my questions:
1. Is a solution with the algorithm library possible?
2. Or can this be achieved via templates?
To translate your current solution to a template solution is fairly straight forward.
template <typename T, typename ValueType>
void SpecialSort(std::vector<T>& vec, ValueType T::* mPtr) {
std::vector<ValueType> vt;
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [&](const T& s) {return s.*mPtr; });
std::sort(vt.begin(), vt.end());
std::for_each(vec.begin(), vec.end(), [&, i = 0U](T& s) mutable {s.*mPtr = vt[i++]; });
}
And we can call it by passing in the vector and a pointer-to-member.
SpecialSort(tv, &Test::toSort);
Somewhow like this (You just need to duplicate, rename and edit the "switchToShort" funtion for the rest of the variables if you want):
#include <iostream>
#include <vector>
struct Test {
int a{};
int b{};
int toSort{};
};
void switchToShort(Test &a, Test &b) {
if (a.toSort > b.toSort) {
int temp = a.toSort;
a.toSort = b.toSort;
b.toSort = temp;
}
}
//void switchToA(Test& a, Test& b) { ... }
//void switchToB(Test& a, Test& b) { ... }
inline void sortMemeberValues(std::vector<Test>& data, void (*funct)(Test&, Test&)) {
for (int i = 0; i < data.size(); i++) {
for (int j = i + 1; j < data.size(); j++) {
(*funct)(data[i], data[j]);
}
}
}
int main() {
std::vector<Test> tv { { 1, 1, 9 }, { 2, 2, 8 }, { 3,3 ,7 }, { 4, 4, 6 }, { 5, 5, 5} };
sortMemeberValues(tv, switchToShort);
//sortMemeberValues(tv, switchToA);
//sortMemeberValues(tv, switchToB);
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
With range-v3 (and soon ranges in C++20), you might simply do:
auto r = tv | ranges::view::transform(&Test::toSort);
std::sort(r.begin(), r.end());
Demo

how to print std::set of std::maps

Here is the map with a string key and a structure value
1. Firstly I am creating a map of integer and a structure as value
std::map<int,struct value>; and then I am adding all these map objects to a set
std::set<std::map<int,struct value>> and I would like to understand how to loop through this set
I am not able to access the maps that are the part of this set, please suggest
struct values
{
std::string a;
std::string b;
values():a("milepost"),b("dummyval"){};
values( std::string ab, std::string bc)
{
a=ab;
b=bc;
};
bool operator<(const values& other) const {
return (a< other.a && b < other.b) ;
}
friend std::ostream& operator<<(std::ostream& os, const values& val);
};
std::ostream& operator<< (std::ostream& os , const values& val)
{
os << val.a <<"\t"<< val.b;
return os;
}
typedef std::map<std::string,values> myWsData;
main()
{
values a;
myWsData et_Data1,pt_Data2;
et_Data2.insert(std::make_pair("780256", a));
pt_Data2.insert(std::make_pair("780256", a));
std::set<myWsData> myet_pt_data;
myet_pt_data.insert(et_Data1);
myet_pt_data.insert(pt_Data2);
for (auto &i:myet_pt_data)
{
std::cout<<i<<"\n";
}
}
You have to use two loops, like so:
for (auto const& it1 : myet_pt_data)
{
for (auto const& it2 : it1)
{
std::cout << it2.first << '\t' << it2.second << std::endl;
}
}
When you use auto const& in your range-based for loops you avoid copying the set and all its content.
The types are as follows:
decltype(it1) is std::map<std::string,values> const&, and
decltype(it2) is std::pair<std::string const,values> const&
For completeness, note that the std::string in it2 (the std::pair) is const.
How about this:
#include <iostream>
#include <map>
#include <set>
#include <algorithm>
using namespace std;
int main(int argc, char *argv[])
{
set<map<int,string> > list;
//fill list
std::for_each(list.begin(), list.end(), [](auto set_el){
std::for_each(set_el.begin(),set_el.end(),[](auto map_el) {
std::cout<<map_el.first<<"\t"<<map_el.second<<std::endl;
});
});
cout << "Hello World!" << endl;
return 0;
}
You are probably missing a inner loop:
// iterates through all elements of the set:
for (const auto& the_map : myet_pt_data)
{
// the_map takes all values from the set
// the_map actual type is const std::map<std::string,values>&
for (const auto& the_value : the_map)
{
// the_value takes all value of the current map (the_map)
// the_value actual type is const std::pair<std::string,values>&
std::cout << the_value.first << " value is " << the_value.second << std::endl;
}
}

Error:expression must have class type

I'm trying to display a vector contents named l_anMarking, but I'm getting this error message:
error : expression must have class type while trying to match the argument list '(std::ostream, std::vector<long,std::allocator<_Ty>>)
I don't understand why I'm having this error. This is my code:
Header file:
class SPSIM_EXPORT ParaStochSimulator : public StochasticSimulator
{
private:
protected:
VectorLong m_anCurrentMarking;
long m_nMinTransPos;
public:
void first_reacsimulator();
void ParaStochSimulator::broad_cast(long);
}
cpp:
void ParaStochSimulator::first_reacsimulator()
{
if (mnprocess_id==0)
{
broad_cast(m_anCurrentMarking);
}
}
void ParaStochSimulator::broad_cast(long j)
{
std::cout << "i'm broad_casting" << std::endl;
double val;
//Get manipulated places
VectorLong l_nMinplacesPos = (*m_pcTransitionsInfo)[j]->GetManipulatedPlaces();
double* l_anMarking=new double [l_nMinplacesPos.size()];
//l_anMarking.clear();
//double var = l_nMinplacesPos.size();
int i = 0;
for (auto lnpos : l_nMinplacesPos)
{
val = m_anCurrentMarking[lnpos];
l_anMarking[i++] = val;
}
std::vector<VectorLong>::iterator it;
for (it = l_anMarking.begin(); it < l_anMarking.end(); it++) //here
{
std::cout << *it << std::endl;
}
MPI_Bcast(&l_anMarking, sizeof l_nMinplacesPos, MPI_DOUBLE, 0, MPI_COMM_WORLD);
delete[] l_anMarking;
}
int main()
{
((spsim::ParaStochSimulator*)l_pcStochSolver)->first_reacsimulator();
}
Your l_anMarking variable is declared as a raw double* pointer, but you are trying to treat it as an STL container by calling begin() and end() on it. Thus the compiler error.
You have two choices:
Change the second for loop to use indexes instead of iterators.
for (int k = 0; k < i; ++k)
{
std::cout << l_anMarking[k] << std::endl;
}
Change l_anMarking to be a std::vector<double> instead (and adjust MPI_Bcast()) accordingly):
std::vector<double> l_anMarking;
l_anMarking.reserve(l_nMinplacesPos.size());
for (auto lnpos : l_nMinplacesPos)
{
val = m_anCurrentMarking[lnpos];
l_anMarking.push_back(val);
}
for (auto marking : l_anMarking)
{
std::cout << marking << std::endl;
}
The compiler error is referring to not being able to print the VectorLong to std::cout
std::vector<VectorLong>::iterator it;
for (it = l_anMarking.begin(); it < l_anMarking.end(); it++) //here
{
std::cout << *it << std::endl; // need an operator<< for your VectorLong
}
You should add something like this:
std::ostream & operator<<(std::ostream &os, const VectorLong& v)
{
os << "VectorLong: ";
for (const auto &l : v)
os << l << " ";
return os;
}

STL non-copying wrapper around an existing array?

Is it possible to create an STL-like container, or even just an STL-style iterator, for an existing array of POD-type elements?
For example, suppose I have an array of ints. It would be convenient to be able to call some of the STL functions, such as find_if, count_if, or sort directly on this array.
Non-solution: copying the entire array, or even just references to the elements. The goal is to be very memory- and time-saving while hopefully allowing use of other STL algorithms.
You can call many of the STL algorithms directly on a regular C style array - they were designed for this to work. e.g.,:
int ary[100];
// init ...
std::sort(ary, ary+100); // sorts the array
std::find(ary, ary+100, pred); find some element
I think you'll find that most stuff works just as you would expect.
You can use an inline function template so that you don't have to duplicate the array index
template <typename T, int I>
inline T * array_begin (T (&t)[I])
{
return t;
}
template <typename T, int I>
inline T * array_end (T (&t)[I])
{
return t + I;
}
void foo ()
{
int array[100];
std::find (array_begin (array)
, array_end (array)
, 10);
}
All the STL algorithms use iterators.
A pointer is a valid iterator into an array of objects.
N.B.The end iterator must be one element past the end of the array. Hence the data+5 in the following code.
#include <algorithm>
#include <iostream>
#include <iterator>
int main()
{
int data[] = {4,3,7,5,8};
std::sort(data,data+5);
std::copy(data,data+5,std::ostream_iterator<int>(std::cout,"\t"));
}
You can use Boost.Array to create a C++ array type with STL semantics.
using arrays:
int a[100];
for (int i = 0; i < 100; ++i)
a[i] = 0;
using boost.arrays:
boost::array<int,100> a;
for (boost::array<int,100>::iterator i = a.begin(); i != a.end(); ++i)
*i = 0;
Update: With C++11, you can now use std::array.
A pointer is a valid model of an iterator:
struct Bob
{ int val; };
bool operator<(const Bob& lhs, const Bob& rhs)
{ return lhs.val < rhs.val; }
// let's do a reverse sort
bool pred(const Bob& lhs, const Bob& rhs)
{ return lhs.val > rhs.val; }
bool isBobNumberTwo(const Bob& bob) { return bob.val == 2; }
int main()
{
Bob bobs[4]; // ok, so we have 4 bobs!
const size_t size = sizeof(bobs)/sizeof(Bob);
bobs[0].val = 1; bobs[1].val = 4; bobs[2].val = 2; bobs[3].val = 3;
// sort using std::less<Bob> wich uses operator <
std::sort(bobs, bobs + size);
std::cout << bobs[0].val << std::endl;
std::cout << bobs[1].val << std::endl;
std::cout << bobs[2].val << std::endl;
std::cout << bobs[3].val << std::endl;
// sort using pred
std::sort(bobs, bobs + size, pred);
std::cout << bobs[0].val << std::endl;
std::cout << bobs[1].val << std::endl;
std::cout << bobs[2].val << std::endl;
std::cout << bobs[3].val << std::endl;
//Let's find Bob number 2
Bob* bob = std::find_if(bobs, bobs + size, isBobNumberTwo);
if (bob->val == 2)
std::cout << "Ok, found the right one!\n";
else
std::cout << "Whoops!\n";
return 0;
}