Error in function sort - c++

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];
}

Related

C++ recursive struct comparator

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.

Sorting a Vector of a custom class with std::sort() causes a segmentation fault

I'd like to sort a vector of a custom class using std::sort() and overloading the < operator. Following the answer here: Sorting a vector of custom objects, I tried the following code:
class Evnt {
private:
int Day, Month;
string mydata;
public:
friend ifstream& operator >>(ifstream &in,Evnt &E){
char junk;
in >>junk>>E.Month>>junk>>E.Day;
getline(in,E.mydata);
return in;
}
bool operator<(const Evnt &E) const{
if(Month < E.Month)
return true;
else if(Day < E.Day)
return true;
return false;
}
};
int main(){
ifstream inpt("inputfile.txt")
Vector <Evnt> v;
Evnt tmpevnt;
while(intp>>tmpevnt)
v.push_back(tmpevent)
sort(v.begin(), v.end());
return 0;
}
The last line somewhat erratically causes segmentation faults. I followed the various examples fairly closely, and so am having issues figuring out what the problem is. It seems to only occur if I read a large number (~20+) items in.
std::sort requires a comparison operation that imposes Strict Weak Ordering.
That means that if a < b returns true, b < a must not return true.
Fix your comparison operator.
bool operator<(const Evnt &E) const{
if(Month < E.Month)
return true;
else if(Month == E.Month && Day < E.Day)
return true;
return false;
}
See this similar question for more information.

qsort and std::sort behaving differently

I am very amazed that sorting via qsort and std::sort can produce different results. I need help explaining the behavior of the following snippets:
using qsort:
// the following comparator has been used in qsort.
// if l<r : -1, l==r : 0 , l>r 1
int cmpre(const void *l, const void *r) {
if ((*(tpl *)l).fhf < (*(tpl *)r).fhf)
return -1;
else
if ((*(tpl *)l).fhf == (*(tpl *)r).fhf) {
if ((*(tpl *)l).nhf == (*(tpl *)r).nhf)
return 0;
else
if ((*(tpl *)l).nhf > (*(tpl *)r).nhf)
return 1;
else
return -1;
} else
return 1;
}
// and sort statement looks like :
qsort(tlst, len, sizeof(tpl), cmpre);
Complete Code link =>
http://ideone.com/zN87tX
Using sort:
// the following comparator was used for sort
int cmpr(const tpl &l, const tpl &r) {
if (l.fhf < r.fhf)
return -1;
else
if (l.fhf == r.fhf) {
if (l.nhf == r.nhf)
return 0;
else
if (l.nhf > r.nhf)
return 1;
else
return -1;
} else
return 1;
}
// and sort statement looks like :
sort(tlst, tlst + len, cmpr);
Complete code link at =>
http://ideone.com/37Dc2S
You can see the output on the link, after and before sorting operation and may wish to check out the compr and compre methods used to compare two tuples. I do not understand why sort is not able to sort the array whereas qsort is able to do so.
Rewrite cmpr() as
bool cmpr(const tpl &l, const tpl &r){
if(l.fhf != r.fhf) return l.fhf < r.fhf;
return l.nhf < r.nhf;
}
Or, you may also reuse cmpre() to implement cmpr().
bool cmpr(const tpl &l, const tpl &r) {
return (cmpre(&l, &r) < 0);
}

C++ sorting array => vector iterators incompatible

I have a simple struct
struct test
{
std::vector<Data> data;
void sort()
{
std::sort(data.begin(), data.end());
}
}
Data is a struct as well and has only simple fields and implements a < operator like following:
EDIT: changes according to feedbacks are included
struct Data
{
// ADAPTION 1 - comparator works with reference now
bool operator<(const Data &data) const
// bool operator<(const Data data)
{
// 1) sortieren nach Typ
if (type < data.type)
return true;
else if (type > data.type)
return false;
// 2) nach name sortieren
if(strlen(strName) > 0)
{
if (strncmp(strName, data.strName, 50) < 0)
return true;
else if (strncmp(strName, data.strName, 50) > 0)
return false;
}
// ADAPTION 2 - added:
else if (data.strName[0]) // at least 1 character...
return true;
// 3) nach Spezialtyp sortieren
if(strlen(typeSpecial)>0)
{
if (strncmp(typeSpecial, data.typeSpecial, 50) < 0)
return true;
else if (strncmp(typeSpecial, data.typeSpecial, 50) > 0)
return false;
}
// ADAPTION 3 - added:
else if (data.strName[0]) // at least 1 character...
return true;
return false;
}
}
That's it. How can I get the vector iterators incompatible error that way? I'm not copying any vector, I'm directly calling the sort function with the vector...
In Visual Studio 2005, I've never had a problem, in Visual Studio 2012 this problem appeared and I don't know why and how to avoid it
There're a few issues with your code:
bool operator<(const Data data)
...should be...
bool operator<(const Data& data) const
Then:
if(strlen(strName) > 0)
{
if (strncmp(strName, data.strName, 50) < 0)
return true;
else if (strncmp(strName, data.strName, 50) > 0)
return false;
}
...needs...
else if (data.strName[0]) // at least 1 character...
return true;
This is required to ensure strict weak ordering, which is a requirement of std::sort's and means that when a < b, !(b < a).
Similarly:
if(strlen(typeSpecial)>0)
{
if (strncmp(typeSpecial, data.typeSpecial, 50) < 0)
return true;
else if (strncmp(typeSpecial, data.typeSpecial, 50) > 0)
return false;
}
...needs...
else if (data.typeSpecial[0])
return true;
Your string comparisons would be much cleaner if you used std::strings. If you do stick with ASCIIZ data, it would be better to use e.g. sizeof typeSpecial instead of 50 etc.. You can improve performance and code concision by doing less comparisons and trusting strncmp to handle empty strings appropriately (which it will):
if (type < data.Type) return true;
if (type > data.Type) return false;
int d = strncmp(strName, data.strName, sizeof strName);
if (d == 0)
d = strncmp(typeSpecial, data.typeSpecial, sizeof typeSpecial);
return d < 0;

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);
}