C++ recursive struct comparator - c++

I have created a struct to use as a key in a map to avoid having duplicate elements.
The struct contains pointers to children and siblings of its own type.
For the map, I have created a custom comparator that is supposed to recursively look at the element, the children and the siblings until a difference is found to make sure the elements are the same.
However, for some reason it is not working and Im still getting duplicates. After checking them out in the debugger, I concluded that they are indeed the exact same through and through so the problem must probably be somewhere in there.
This is the struct.
struct controlIdentifier
{
DWORD m_dwID;
DWORD m_dwDefaultID;
DWORD m_dwDisableID;
BYTE m_bType;
int m_nWidth;
int m_nHeight;
int m_nMargineH;
int m_nMargineV;
shared_ptr<controlIdentifier> m_pCHILD;
shared_ptr<controlIdentifier> m_pNEXT;
bool operator<(const controlIdentifier& id) const
{
if (m_dwDefaultID < id.m_dwDefaultID)
return true;
if (m_dwDisableID < id.m_dwDisableID)
return true;
if (m_bType < id.m_bType)
return true;
if (m_nWidth < id.m_nWidth)
return true;
if (m_nHeight < id.m_nHeight)
return true;
if (m_nMargineH < id.m_nMargineH)
return true;
if (m_nMargineV < id.m_nMargineV)
return true;
if (!m_pCHILD && id.m_pCHILD)
return true;
if (m_pCHILD && !id.m_pCHILD)
return false;
if (!m_pNEXT && id.m_pNEXT)
return true;
if (m_pNEXT && !id.m_pNEXT)
return false;
bool smaller = false;
if (m_pCHILD && id.m_pCHILD)
smaller = *m_pCHILD < *id.m_pCHILD;
if (!smaller)
{
if (m_pNEXT && id.m_pNEXT)
return *m_pNEXT < *id.m_pNEXT;
}
else
return smaller;
return false;
}
};
And this is how it's used.
struct cmpBySharedPtr {
bool operator()(const shared_ptr<controlIdentifier>& a, const shared_ptr<controlIdentifier>& b) const {
return *a < *b;
}
};
std::set<FRAMEDESC_SHAREDPTR> m_curFrames;
std::map<shared_ptr<controlIdentifier>, FRAMEDESC_SHAREDPTR, cmpBySharedPtr> m_serialFrames;
for (auto&& frame : m_curFrames)
{
shared_ptr<controlIdentifier> id;
makeIdentifiers(frame, id);
id->m_dwID = newId;
auto find = m_serialFrames.find(id);
if (find == m_serialFrames.end())
{
m_serialFrames.insert(std::pair(id, frame));
newId++;
}
}
m_dwID is not being compared on purspose.

Consider A = (child = 5, next = 6) and B = (child = 6, next = 5). Now A<B is true as (A.child < B.child) is true and it just returns that. Now consider B<A. B.child < A.child is false, so it checks the next fields.. Now B.next < A.next is true, so your comparison returns true.
So this is nonsensical -> A<B is true and B<A is true. This means your comparator is invalid.
The technical term for this is the comparator requires strict weak ordering - see https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. Your comparator breaks the asymmetry requirement.

You can construct operator < by comparing field by field. But what you did is too little. Basically it shall look like this:
bool operator < (const A& left, const A& right)
{
if (left.firstField < right.firstField) return true;
if (right.firstField < left.firstField) return false; // this case is missing
if (left.secondField < right.secondField) return true;
if (right.secondField < left.secondField) return false; // this case is missing
....
return false;
}
You are missing cases when you can conclude, that for sure, left object is "greater" than right object.

Related

std::sort vector of struct invalid operator<

I have problem with strict weak ordering in the compare function in std::sort. I can't see why this would fail.
I have some nested structs:
struct date{
int day = 1;
int month = 1;
int year = 2017;
};
struct hhmmss{
int hours = 1;
int minutes = 1;
int seconds = 1;
};
struct dateAndTime {
date d;
hhmmss t;
};
struct Trade
{
/*
other unrelevant data
*/
dateAndTime timeClosed;
};
In my code, at some point I have a populated std::vector<Trade> which I want to sort.
My sort function:
void sortTradesByDate(std::vector<Trade>& trades){
std::sort(trades.begin(), trades.end(), compareDateAndTime);
}
My comparison function:
bool compareDateAndTime(const Trade& t1, const Trade& t2){
if (t1.timeClosed.d.year < t2.timeClosed.d.year)
return true;
else if (t1.timeClosed.d.month < t2.timeClosed.d.month)
return true;
else if (t1.timeClosed.d.day < t2.timeClosed.d.day)
return true;
else if (t1.timeClosed.t.hours < t2.timeClosed.t.hours)
return true;
else if (t1.timeClosed.t.minutes < t2.timeClosed.t.minutes)
return true;
else if (t1.timeClosed.t.seconds < t2.timeClosed.t.seconds)
return true;
return false;
}
When running the function, and debuging, my first item passed to compareDateAndTime() passes after returning true on one of the statements (months).
The next item returns true at hours comparison, but then I get a "Debug Assertion Failed!" with "Expression: invalid operator<".
Doing some googling, this has to do with strict weak ordering. But why does this fail when comparing int variables?
Your comparison function isn't implementing strict weak ordering
Consider this scenario:
t1: year=2017, month=2
t2: year=2016, month=5
compareDateAndTime(t1, t2) would return true.
You should proceed to compare month if and only if year is the same.
if (t1.timeClosed.d.year < t2.timeClosed.d.year)
return true;
if (t1.timeClosed.d.year > t2.timeClosed.d.year)
return false;
if (t1.timeClosed.d.month < t2.timeClosed.d.month)
return true;
if (t1.timeClosed.d.month > t2.timeClosed.d.month)
return false;
... and so forth ...
A nice way to leverage the Standard Library:
return std::tie(t1.timeClosed.d.year, t1.timeClosed.d.month) < std::tie(t2.timeClosed.d.year, t2.timeClosed.d.month);
You can add the missing members inside the std::tie's (it's a variadic template). This uses std::tuple's operator<, which is defined to do what you expect.

Best way to check if one of other objects is true or not

I am looking for best way to implement this scenario:
I have 4 objects that have Boolean member that in the flow of the app sometimes they are set to true and sometimes are set to false depending on conditions;
Then I have final function that gets 1 of this objects and needs to check if in the other 3 objects one of them has the member set to true .
The problem is I know how to do the dirty check , and I am searching for cleaner way here is my code for the final function:
class Obj
{
public :
Obj(int _id) : id(_id)
bool status;
int id // only 4 objects are created 0,1,2,3
}
m_obj0 = new Obj(0) ;
m_obj1 = new Obj(1) ;
m_obj2 = new Obj(2) ;
m_obj3 = new Obj(3) ;
bool check(Obj* obj)
{
if(obj->id == 0)
{
if(m_obj1->status || m_obj2->status || m_obj3->status)
{
return true;
}
return false;
}else if(obj->id == 1)(
if(m_obj0->status || m_obj2->status || m_obj3->status)
{
return true;
}
return false;
}else if(obj->id == 2)(
if(m_obj0->status || m_obj1->status || m_obj3->status)
{
return true;
}
return false;
}else if(obj->id == 3)(
if(m_obj0->status || m_obj1->status || m_obj2->status)
{
return true;
}
return false;
}
is there a shorter and cleaner way to accomplish this check function ?
You can set m_obj as an array. Then use a for loop to check
bool check(Obj* obj)
{
for (int i = 0; i < 4; i ++) {
if (obj->id == i) continue;
if (m_obj[i]->status == true)
return true;
}
return false;
}
Or add them together, then subtract m_obj[obj->id]->status。Check the result is zero or not
bool check(Obj* obj)
{
int result = m_obj[0]->status+m_obj[1]->statusm_obj[2]->status
+m_obj[3]->status-m_obj[obj->id]->status;
return (result!=0);
}

Searching first specified element in array

I'm trying to make function that has a loop that checks every member of an array made from boolean variables and exits when it finds the first "true" value.
That's what I have now:
bool solids[50];
int a,i;
//"equality" is a function that checks the equality between "a" and a defined value
solids[0] = equality(a,&value_1);
solids[1] = equality(a,&value_1);
solids[2] = equality(a,&value_1);
solids[3] = equality(a,&value_1);
for (i = 0; solids[i] != true; i++)
{
[...]
}
But I have no idea, what should I put into the loop?
My attempt was
for (i = 0; i <= 50; i++)
{
if (solids[i] == true)
{
return true;
break;
} else {
return false;
}
}
,that should return true after the first found true and return false if the array has no member with true value, but it doesn't seem to work in the code.
Is it wrong? If yes, what is the problem?
PS.: I may count the number of trues with a counter but that's not an optimal solve to the problem, since I just look for the FIRST true value and consequently, the program doesn't have to check all the 50 members. Needley to count, how many unnecesary steps should this solve would mean.
here's a short example usage of std::find() as advised by #chris:
bool find_element_in_array() {
bool solids[50];
int length;
/* ... do many operations, and keep length as the size of values inserted in solids */
bool* location = std::find(solids, length, true);
// if element is found return true
if (location != solids + length)
return true;
// else return false
return false;
}
Once you have solids correctly set (it looks like you're currently setting every value to the same thing), you can make a loop that exits on the first true like this:
for (i = 0; i < 50; i++)
{
if (solids[i] == true)
{
return true;
}
}
return false;
I'd also just move the declaration of i into the for loop body, since it's not used outside, but the above answers your question.
return immediately exits the function, so there is no need to break the loop after.
If it's sufficient to exit the function right after the search, you should write something like:
for (int i = 0; i < 50; i++) {
if (solids[i]) return true;
}
return false;
If you need to use the result of the search in the same function, use additional variable:
bool found = false;
for (int = 0; i < 50; i++) {
if (solids[i]) {
bool = true;
break;
}
}
if (found) { ...

What is the best way to implement operator<?

Sorry if this is a stupid question, but it's something that I'm curious about.
I am overloading the less-than operator for my sort algorithm based on last name, first name, middle name. I realize there is not a right or wrong here, but I'm curious as to which style is written better or preferred among fellow programmers.
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last < key.m_Last)
|| ( (m_Last == key.m_Last) && (m_First < key.m_First) )
|| ( (m_Last == key.m_Last) && (m_First == key.m_First) && (m_Middle < key.m_Middle) )
return true;
return false;
}
or
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last < key.m_Last)
return true;
else if ( (m_Last == key.m_Last) && (m_First < key.m_First) )
return true;
else if ( (m_Last == key.m_Last) && (m_First == key.m_First) && (m_Middle < key.m_Middle) )
return true;
else
return false;
}
or
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last < key.m_Last)
return true;
if (m_Last == key.m_Last)
if (m_First < key.m_First)
return true;
if (m_Last == key.m_Last)
if (m_First == key.m_First)
if (m_Middle < key.m_Middle)
return true;
return false;
}
I prefer:
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last == key.m_Last) {
if (m_First == key.m_First) {
return m_Middle < key.m_Middle;
}
return m_First < key.m_First;
}
return m_Last < key.mLast;
}
Nice and systematic, and it is obvious how new members can be added.
Because these are strings, the repeated comparison may be needlessly inefficient. Following David Hamman's suggestion, here is a version which only does the comparisons once per string (at most):
bool CPerson::operator<(const CPerson& key) const
{
int last(m_Last.compare(key.m_Last));
if (last == 0) {
int first(m_First.compare(key.m_First));
if (first == 0) {
return m_Middle < key.m_Middle;
}
return first < 0;
}
return last < 0;
}
All of your implementations are essentially the same and they are all wrong by any reasonable definition of sort order for people's names. Your algorithm will place Jonathan Abbott Zyzzyk ahead of Jonathan Zuriel Aaron.
What you want is person A's name is less than person B's name if:
The last name of person A is less than the last name of person B or
The two have the same last name and
The first name of person A is less than the first name of person B or
The two have the same first name and the middle name of person A is less than the middle name of person B.
Whether you implement this as a single boolean expression versus a staged if/else sequence is a bit of personal preference. My preference is the single boolean expression; to me that logical expression is clearer than a cluttered if/else sequence. But apparently I'm weird. Most people prefer the if/else construct.
Edit, per request
As a single boolean expression,
bool Person::operator< (const Person& other) const {
return (last_name < other.last_name) ||
((last_name == other.last_name) &&
((first_name < other.first_name) ||
((first_name == other.first_name) &&
(middle_name < other.middle_name))));
}
I find the first one the most difficult to read of the three (although none of them are too difficult) and the first one has unnecessary parentheses. The second one is my personal preference, because the third one seems too long and verbose.
This really is subjective though.
I normally write a comparison function roughly like this:
bool whatever::operator<(whatever const &other) {
if (key1 < other.key1)
return true;
if (other.key1 < key1)
return false;
// compare the second key item because the first ones were equal.
if (key2 < other.key2)
return true;
if (other.key2 < key2)
return false;
// repeat for as many keys as needed
// for the last key item, we can skip the second comparison:
if (keyN < other.keyN)
return true;
return false; // other.keyN >= keyN.
}
Along a slightly different vein, all of the solutions (including my first answer) tend to compare names twice, once for less than and again for equality. Since sort is at best an N*logN algorithm, efficiency can be quite important when sorting a big list of names, and these duplicative comparisons are rather inefficient. The string::compare method provides a mechanism for bypassing this problem:
bool Person::operator< (const Person& other) const {
int cmp = last_name.compare (other.last_name);
if (cmp < 0) {
return true;
} else if (cmp == 0) {
cmp = first_name.compare (other.first_name);
if (cmp < 0) {
return true;
} else if (cmp == 0) {
cmp = middle_name.compare (other.middle_name);
if (cmp < 0) {
return true;
}
}
}
return false;
}
Edit, per request
Elided.
A boolean version of the above will either result in undefined behavior or will use multiple embedded uses of the ternary operator. It is ugly even given my penchant for hairy boolean expressions. Sorry, Mankarse.
I like to reduce this to tuples, which already implement this kind of lexicographical ordering. For example, if you have boost, you can write:
bool Person::operator< (const Person& Rhs) const
{
return boost::tie(m_Last, m_First, m_Middle) < boost::tie(Rhs.m_Last, Rhs.m_First, Rhs.m_Middle);
}

Error in function sort

I'm trying to use the sort function from STL, but it gives me an error during execution.
My compare function returns true if v is smaller then e:
bool smallerThan(VertexEntry &v, VertexEntry &e) {
if(v.v[0] < e.v[0]) return true;
else if(v.v[1] < e.v[1]) return true;
else if(v.v[2] < e.v[2]) return true;
return false;
}
and here is the call:
sort(vertices.begin(),vertices.end(),smallerThan);
The size of the vector is aprox 400 elements.
Can somebody help me solve my problem?
Thank you!!
Your comparison function is incorrect - it doesn't enforce strict weak ordering.
Use this:
bool smallerThan(VertexEntry const & v, VertexEntry const & e) {
if (v.v[0] < e.v[0])
return true;
else if(v.v[0] > e.v[0])
return false;
else if(v.v[1] < e.v[1])
return true;
else if(v.v[1] > e.v[1])
return false;
else if(v.v[2] < e.v[2])
return true;
return false;
}
Your comparison operator doesn't enforce strict weak ordering. If you're able to use boost one trick I've seen is to bind your object to a boost::tuple and use its strict weak operator<.
If you need to write it yourself, something like this should work:
bool smallerThan(const VertexEntry &v, const VertexEntry &e)
{
if(v.v[0] != e.v[0]) return v.v[0] < e.v[0];
else if(v.v[1] != e.v[1]) return v.v[1] != e.v[1];
else return v.v[2] < e.v[2];
}