Keeping std::map balanced when using an object as key - c++

I am writing some code where I am storing lots of objects that I want to get back based on set criteria. So to me it made sense to use a map with an object as a key. Where the object would contain the "set criteria".
Here is a simplified example of the kind of objects i am dealing with:
class key
{
int x,y,w,h;
}
class object
{
...
}
std::map<key, object, KeyCompare> m_mapOfObjects;
Quite simple, the first thought was to create a compare functions like this:
struct KeyCompare
{
bool operator()(const key &a, const key &b)
{
return a.x < b.x || a.y < b.y || a.w < b.w || a.h < b.h;
}
}
but then i thought the chances of this returning true are quite high. So I figured this would lead to a very unbalanced tree and therefore slow searching.
My main worry is that as I understand it, std::map uses that one function in this way:
if( keyCompare(a,b) )
{
//left side
}
else if (keyCompare(b,a))
{
//right side
}
else
{
//equal
}
So i can't just use a.x < b.x, because then anything with the same x would be considered equal, which is not what i want. I would not mind it ordering it in this way but its the "equal" bit i just can't seem to solve without making it unbalanced.
I figure multiplying them all together is a no no for obvious reasons.
So the only solution i could come up with was to create a "UID" base on the info:
typedef long unsigned int UIDType;
class key
{
private:
UIDType combine(const UIDType a, const UIDType b)
{
UIDType times = 1;
while (times <= b)
times *= 10;
return (a*times) + b;
}
void AddToUID(UIDType number)
{
if(number < m_UID)
{
m_UID = combine(number, m_UID);
}
else
{
m_UID = combine(m_UID, number);
}
}
UIDType UID;
public:
int x,y,w,h;
key()
{
AddToUID(x);
AddToUID(y);
AddToUID(w);
AddToUID(h);
}
}
struct KeyCompare
{
bool operator()(const key &a, const key &b)
{
return a.UID < b.UID;
}
}
But not only does that feel a little hacky, "long unsigned int" isn't big enough to hold the potential numbers. I could put it in a string, but speed is an issue here and I assumed an std::string < is expensive. Overall though the smaller i can make this object the better.
I was wondering if anyone has any suggestions for how to do this better. Perhaps i need to use something other then a std::map or perhaps there is another overload. Or perhaps there is something glaringly obvious that i'm missing here. I really feel like i'm over-complicating this, perhaps im really barking up the wrong tree with a map.
As i was writing this it occurs to me that divide is another way to get a "unique" number but that could also equal very large numbers

All you need is to implement a strict weak ordering, which you can easily achieve using std::tie, which has a less than comparison operator< which performs a lexicographical comparison:
#include <tuple>
struct KeyCompare
{
bool operator()(const key& a, const key& b) const
{
return std::tie(a.x, a.y, a.w, a.h) < std::tie(b.x, b.y, b.w, b.h);
}
}
If you do not have the required C++11 support, you can use std::tr1::tie from <tr1/tuple> or equivalent versions from the boost libraries.

I feel juanchopanza has a very good solution, for those who do not have C++11 support or boost libraries
I found a very simple solution on:
What's the simplest way of defining lexicographic comparison for elements of a class?
This solution works for my particular problem a little better then tuple would (as i also have an array of values that i would like to consider). But I would highly recommend considering tuple in future, as will I.
struct keyCompare
{
bool operator()(const key &a, const key&b)
{
if(a.x != b.x) return a.x < b.x;
if(a.y != b.y) return a.y < b.y;
if(a.w != b.w) return a.w < b.w;
if(a.h != b.h) return a.h < b.h;
return false; //they must be equal
}
}
thanks to juanchopanza for his answer and to anyone else who had a look in

Related

C++ Sort a vector of pointers in which the objects represent matrix coordinates

I read a file in which a matrix is filled with 0, 1 and 2.
When I find a 1 I create a BlueCar, when 2 I create RedCar:
class BlueCar : public Car
{
public:
BlueCar(){};
BlueCar(int x, int y);
void move();
virtual ~BlueCar();
};
class RedCar : public Car
{
public:
RedCar(){};
RedCar(int x, int y);
void move();
virtual ~RedCar();
};
class Car
{
public:
Car();
Car(int x, int y);
virtual ~Car();
virtual void move() = 0;
private:
int x,y;
};
With this objects I fill two vectors:
std::vector<BluCar*> *sparseBlu;
std::vector<RedCar*> *sparseRed;
Considering that I need to move the cars of the matrix, and that Blue ones move downward and Red ones move rightward, I think the best approach is to sort this vectors. In that way I can see quickly if the position next to the car I'm considering is empty.
Since Blue cars move downward I think it's better to sort "sparseBlu" first by column and then by row, instead "sparseRed" first by row and then by column.
How can I achieve this result?
It's better (in terms of performance) to sort the vector immediately when I fill it car by car, right?
Short answer:
std::sort(std::begin(*sparseBlu), std::end(*sparseBlu),
[](const BlueCar* lhs, const BlueCar* rhs) -> bool {
return lhs->get_x() < rhs->get_x() ||
(lhs->get_x() == rhs->get_x() && lhs->get_y() < rhs->get_y());
});
std::sort(std::begin(*sparseRed), std::end(*sparseRed),
[](const RedCar* lhs, const RedCar* rhs) -> bool {
return lhs->get_y() < rhs->get_y() ||
(lhs->get_y() == rhs->get_y() && lhs->get_x() < rhs->get_x());
});
Please reconsider, if using pointers really is what you need here. Without pointers you have less noise.
std::vector<BluCar> sparseBlu;
std::vector<RedCar> sparseRed;
std::sort(std::begin(sparseBlu), std::end(sparseBlu),
[](const BlueCar& lhs, const BlueCar& rhs) -> bool {
return lhs.get_x() < rhs.get_x() ||
(lhs.get_x() == rhs.get_x() && lhs.get_y() < rhs.get_y());
});
std::sort(std::begin(sparseRed), std::end(sparseRed),
[](const RedCar& lhs, const RedCar& rhs) -> bool {
return lhs.get_y() < rhs.get_y() ||
(lhs.get_y() == rhs.get_y() && lhs.get_x() < rhs.get_x());
});
When that kind of ordering is natural in your application you might also consider to overload operator < (). That makes the calls to sort() much more explicit:
std::sort(std::begin(sparseBlu), std::end(sparseBlu), std::less<BlueCar>);
std::sort(std::begin(sparseRed), std::end(sparseRed), std::less<RedCar>);
An almost declarative programming style.
If you decide to stick with pointers for whatever reason, please consider to use std::unique_ptr<> or std::shared_ptr<> instead of raw pointers, to manage the objects lifetime correctly. Remember, that there is no garbage collection in C++.
std::sort has an overloaded version with a comparator - a custom function to compare two items: http://en.cppreference.com/w/cpp/algorithm/sort, so you can specify any comparison.
Also you can consider storing your cars in a sparse matrix (std::vector<std::vector<Car>>) where empty cells are just empty. So you don't need to sort and can just look at corresponding cell if it's empty.

Best way to iterate through a for loop, and then iterate through it again backwards?

I found that when writing animations I sometimes run into having to go through a for loop once, then iterate the value down afterwards. This was generally used for jump animations, or disappear then appear again animations.
Here's an example of what I had done -
// Make the sprite slowly disappear
for (int i = 256; i > 0; --i)
{
sprite.opacity(i);
draw();
}
// Make the sprite slowly appear again
for (int i = 0; i < 256; ++i)
{
sprite.opacity(i);
draw();
}
Every time I did this I had a deep feeling that it was too much. What would be a nicer way of going about this? I'm not entirely sure what would be best practice. I imagine I could use reverse_iterator, but I'm also not sure how I would implement it.
Consider the use of <cmath> abs() function:
for( int i = -255; i <= 255; i++)
use( abs( i ) );
You can use the absolute value function abs() defined in <cmath>. It will halve the code written in your case.
for(int i=0; i<512; ++i)
{
sprite.opacity( abs(256-i) );
draw();
}
I believe in the situation you are describing, you have to iterate through the sprites to set the opacity of each sprite. Whether you use a for loop, or a reverse_iterator, the time spent is going to be the same. Any implementation of the reverse_iterator will still have to iterate through each sprite. There might be ways to make it easier to read, but in the end the algorithm will come down to the same. For example, you could take advantage of the stack and call the sprites recursively to increase the opacity and then decrease on the way back out; however, I see no gain in doing so the algorithm time would still end up being the same.
In some cases, you just need to bite the bullet and spend the time doing things in a way that may seem like (or even be) brute force.
That's a great way to iterate through a loop both forward and "in reverse" - one commonly used by C++ programmers.
For your sprite, it appears that the 256 range (you might consider setting a const int RGB_RANGE equal to 256 - or a more appropriate identifier) is all that is needed; however, were the size of your object dynamic, you could also consider using the .size() function (something like an ArrayList or a vector - here is where something like an iterator would be useful):
for (i = 9; i < RGB_RANGE; i++)
{
// CODE
}
The above code being an example of the first const suggestion. Remember, simple code is never a bad thing - it means you are doing something right.
If you don't want to use abs, I'd go with something like :
template<typename Func>
void animate (size_t step_count, Func && f)
{
size_t step;
for (step = step_count ; step > 0 ; --step)
f(step - 1);
for (step = 1 ; step < step_count ; ++step)
f(step);
}
Use case :
animate(256, [](size_t step)
{
sprite.opacity(step);
draw();
});
If you wish to just iterate a range up and down again, you can go the very crazy route and just define a "container" (or range, in boost lingo) that provides iterators (well, technically they are more almost-iterators) which allow you to express exactly what you intend to do:
for(auto i : down_and_up(3)) ::std::cout << i << "\n";
For example should print
3
2
1
0
1
2
Sadly, there is not much support in the standard library for types like this, although boost provides boost::iterator_range, boost::counting_iterator, and boost::join that, in concert with std::reverse_iterator, can provide down_and_up. Writing one yourself if fairly simple (although verbose), as long as you do not completely abuse it:
struct down_and_up
{
size_t from;
down_and_up(size_t const from) : from(from) { }
struct iterator : public ::std::iterator<::std::forward_iterator_tag, size_t> {
size_t cur;
bool down;
iterator(size_t cur, bool down) : cur(cur), down(down) { }
size_t operator*() const { return cur; }
iterator& operator++()
{
if(down)
{
--cur;
if(0 == cur) down = false;
}
else ++cur;
return *this;
}
friend bool operator==(iterator const& lhs, iterator const& rhs) { return lhs.down == rhs.down && lhs.cur == rhs.cur; }
friend bool operator!=(iterator const& lhs, iterator const& rhs) { return lhs.down != rhs.down || lhs.cur != rhs.cur; }
};
iterator begin() const { return iterator{ from, true }; }
iterator end() const { return iterator{ from, false }; }
};
Note: If you wish, you can easily extend it with more container capabilities, like a value_type member typedef, but this definition is enough for the above example.
P.S.: The boost way, for your entertainment:
boost::iterator_range<boost::counting_iterator<size_t>> up(boost::counting_iterator<size_t>(0), boost::counting_iterator<size_t>(3));
boost::iterator_range<std::reverse_iterator<boost::counting_iterator<size_t>>> down(
std::reverse_iterator<boost::counting_iterator<size_t>>(boost::counting_iterator<size_t>(4)),
std::reverse_iterator<boost::counting_iterator<size_t>>(boost::counting_iterator<size_t>(1)));
for(auto i : boost::join(down, up)) ::std::cout << i << "\n";

Object comparison with Sorting. C++

I'm having a hard time trying to understand other people's codes here.
I would really appreciate if someone helps me.
Let's say there is an array of object : vpair_list and this vpair_list has a type of class of vpair. So, it would be like this:
class vpair
{
public:
int vid;
int vlabel;
};
bool operator < (const vpair& x, const vpair& y);
vpair* vpair_list;
vpair_list = new vpair[25];
..
sort(vpair_list, vpair_list+j);
What I know from that is sort() compares each element of array vpair_list and sorts them.
The thing is that I just can't understand how that sorting works since the object vpair has two different properties.
Does the sorting work like comparing each property(vid and vlabel) or....? What I thought was the sorting was supposed to be done by comparing specific field or property (either vid or vlabel here).
But this code hasn't got anything to do with that and seems like it just compares the whole object. Could someone tell me how that works?
Thank you in advance.
The standard approach:
class vpair
{
public:
int vid;
int vlabel;
};
bool operator < (vpair const& x, vpair const& y)
{
return std::tie(x.vid, x.vlabel) < std::tie(y.vid, y.vlabel);
}
Of course, the operator can be a member:
class vpair
{
int vid;
int vlabel;
public:
bool operator < (vpair const& y) const
{
return std::tie(vid, vlabel) < std::tie(y.vid, y.vlabel);
}
};
Sort, by default, compares with the operator<. You can implement this operator for your class like so:
public:
bool operator < (const vpair& other) const
{
return (vid < other.vid); // Uses vid but this can be vlable or something else.
}
If you don't have an overload for the operator< with the class you're using, you can always pass in a comparison function as std::sort's third argument:
bool compare_func(vpair i,vpair j) { return (i.vid < j.vid); }
sort(vpair_list, vpair_list+j, compare_func);
Does the sorting work like comparing each property(vid and vlabel) or....?
It happens exactly how you want it to happen.
By default as people have mentioned, the < operator is used by various sort algorithms to arrange elements in ascending order of that operator. However for classes/structs there is no default way to compare them meaning you the programmer has to code it in.
That is what
bool operator < (const vpair& x, const vpair& y);
is. It is just a declaration to the definition of the function the programmer has provided to compare 2 vpair order. The programmer uses his rules to decide and ultimately returns true or false. This is used to sort.
So you can decide exactly how you want it to sort.
bool operator < (const vpair& x, const vpair& y)
{
if(x.vid != y.vid)
return x.vid<y.vid;
return x.vlabel <y.vlabel;
}
This would sort by ascending order of ID, if they are equal, It then sorts by ascending order of vlabel.

Double map insertion problem

I have an
stl::map that has the key defined as an object I defined, and int. The use of the map is as follows:
I have a list of the specific object and I want to count how many identical objects I have. So I insert the objects into the map.if the object already exists in the map I increase it's value (hence the counter). The object has all the basic operators defined. The object consist of 5 strings. The == operator defined as the comparison of all 5 strings, and logically is meaningfull in the context. The problem is that the operator < has no logic meaning in the context. I care only if the objects are equal. I can't define which of two different objects is bigger.so for the sake of stl map defined this operator as the result of if else ladder and in each if I compared with "<" another string of the five. If true return true else, if.... And the last else returns false. In a specific case of the object , where I had three identical instances, I got the map containing two identical objects as keys, one of them had the counter of 1 and the other had 2.
i can't understand what is the problem and how could it happen.
For those who requested some code examples - for reason i can't explain - i can't post the code itself, but i will write a good example of it(please ignore little things like missing ';' - i wrote it in 5 minutes):
class Example
{
private:
string one;
string two;
string three;
string four;
string five;
public:
inline Example (string a_one,string a_two, string a_four, string a_five) :
one(a_one),two(a_two),three(a_three),four(a_four),five(a_five)
{}
inline bool operator == (const Example& other) const
{
if (one == other.one)
{
if (two == other.two)
{
if (three == other.three)
{
if (four == other.four)
{
if (five == other.five)
{
return true;
}
}
}
}
}
return false;
}
inline bool operator < (const Example& other) const
{
if (one < other.one)
{
return true;
}
else if (two < other.two)
{
return true;
}
else if (three < other.three)
{
return true ;
}
else if (four < other.four)
{
return true;
}
else if (five < other.five)
{
return true;
}
else
{
return false;
}
}
}
void CountExample(Example& example,std::map<Example,int>& counters);
void main()
{
std::map<Example,int> counters;
std::list<Example> examples = GetExamples();
//GetExamples defined elsewhere, and initializes examples with a long list of instances of Example
std::list<Example>::const_iterator Iter;
for (Iter = examples.begin();Iter != examples.end();Iter++)
{
CountExample(*Iter);
}
PrintCounters(counters);//PrintCounters defined elsewhere and prints the map to a file
}
void CountExample(Example& example,std::map<Example,int>& counters)
{
std::map<Example,int>::const_iterator Iter;
Iter = counters.find(example);
if (Iter ==counters.end()) //means the specific Example is not in the map
{
counters.insert(std::pair<Example,int>(example,1));
}
else
{
counters[example] += 1;
{
}
If you have a reasonably modern compiler, that ladder of comparisons can be replaced with a single comparison between two std::tie()'d tuples:
#include <tuple>
...
bool operator== (const Example& other) const
{
return std::tie(one, two, three, four, five)
== std::tie(other.one, other.two, other.three, other.four, other.five);
}
bool operator < (const Example& other) const
{
return std::tie(one, two, three, four, five)
< std::tie(other.one, other.two, other.three, other.four, other.five);
}
Incidentally, it may be simpler to use a std::multiset to count the number of times a particular element is stored in an associative container, that simplifies CountExample to a one-liner
void CountExample(const Example& example, std::multiset<Example>& counters)
{
counters.insert(example);
}
Although printing becomes a bit more tricky:
void PrintCounters(const std::multiset<Example>& counters)
{
for(auto i=counters.begin(); i!=counters.end(); i = counters.upper_bound(*i))
std::cout << *i << ":" << counters.count(*i) << '\n';
}
Test on ideone: http://ideone.com/uA7ao
To make a comparison with multiple elements, each element that you compare will have three outcomes: less than, greater than, or equivalent. You must account for all of these cases.
bool LessThan(const MyClass & left, const MyClass right)
{
if (left.one < right.one)
return true;
else if (right.one < left.one)
return false;
// equivalent in one
if (left.two < right.two)
return true;
else if (right.two < left.two)
return false;
// equivalent in one and two
...
return false;
}
You need to provide an operator< for you type. This can be pretty tedious to write, but you can simply it by using a Boost.Tuple - that way, the tuple handles the comparisons, leaving your code easier to read, write and understand.
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <string>
struct Object
{
std::string a;
std::string b;
std::string c;
};
bool operator<(const Object& obj1, const Object& obj2)
{
return (boost::tie(obj1.a, obj1.b, obj1.c) <
boost::tie(obj2.a, obj2.b, obj2.c));
}
Edit: After thinking about the problem some more, I've decided to removed my older answer since it did not seem pertinent to the current problem being experienced. Your operator< method does seem to be fulfilling the requirements for a strict weak ordering, so I think the problem lies somewhere else, and so I'm leaving the following alternate solution below ...
It seem you're having issues creating a total order for your map, so you might want to look at std::unordered_map as an alternative that will directly apply your operator== for detecting equality, rather than using your operator< for a strict weak ordering ... you'll have to provide a hash-function for your class, but otherwise the use of the hash-table based std::unordered_map container is pretty straight-forward.

std::map< MyClass, std::vector<MyClass> > Segment fault. Oddness

SOLVED:
Thanks figured it out thanks to dominic hamon.
It all boils down to trying to call a function on a kinda null object. It could use parts of the object but not others.
I had no idea that this could even happen.
Question
I have experienced an odd segmentation fault with a
std::map< IntVector3, std::vector<IntVector3> >.
In my MyClass.hpp file I make this a private property:
std::map< IntVector3, std::vector< IntVector3 > > recurData;
In my MyClass.cpp file in the constructor for MyClass i can run
std::vector< IntVector3 > pt;
pt.push_back(IntVector3(1,2,3));
recurData[IntVector3(1,2,3)] = pt;
This runs correctly and i don't get a fault.
Later in the program i call a function in MyClass that does the same thing, altering of recurData. Not in the constructor. This causes a segmentation fault. There is no other change to my knowledge that affects recurData.
The implementable of IntVector3 is:
http://pastebin.com/Hc83xapk
There is a lot of extra operators that i added that are unneeded (>=,<=...).
(I tried to follow the rule of 3)
The only real oddness is the < operator. This uses a std::string to compare. This quick hack should work for all x,y,z if they are under 99.
Thanks for the help, this has been driving me crazy.
Using a string to achieve a comparison function is (a) massively inefficient, and (b) broken. It will not provide you a strict-weak ordering, which is what is required for std::map. In particular, it is not going to be transitive, i.e. if a < b and b < c, it won't necessarily give you that a < c. This is going to totally mess up std::map.
A typical implementation of < would be something like:
bool operator< (const IntVector3 &a, const IntVector3 &b)
{
if (a.z < b.z) { return true; }
if (a.z > b.z) { return false; }
if (a.y < b.y) { return true; }
if (a.y > b.y) { return false; }
return (a.x < b.x);
}