std::set custom comparator for non null strings - c++

As the title suggests am trying to implement comparison functor for my defined structure. Here is the sample snippet
#include<set>
struct testData
{
char * data;
int size;
};
class compare
{
public:
bool operator()(const testData & lhs,
const testData & rhs) const noexcept
{
return memcmp(lhs.data, rhs.data, lhs.size<rhs.size?lhs.size:rhs.size) < 0;
}
};
int main()
{
std::set<testData,compare>S;
....
return 0;
}
Issue in the comparison function is since am taking lesser size,this
case fails
suppose there is already this data present {"test",4},and i am trying
to find {"test1",5}.it will say as matched.How can i modify comparison
to overcome this?
Update:
changed to this
class compare
{
public:
bool operator()(const testData & lhs,
const testData & rhs) const noexcept
{
if (lhs.size == rhs.size)
return memcmp(lhs.data, rhs.data, lhs.size) < 0;
return lhs.size < rhs.size;
}
};
will this work?

You'd use std::lexicographical_compare.
Lexicographical comparison is a operation with the following properties:
Two ranges are compared element by element.
The first mismatching element defines which range is lexicographically less or greater than the other.
If one range is a prefix of another, the shorter range is lexicographically less than the other.
If two ranges have equivalent elements and are of the same length, then the ranges are lexicographically equal.
An empty range is lexicographically less than any non-empty range.
Two empty ranges are lexicographically equal.
class compare
{
public:
bool operator()(const testData & lhs,
const testData & rhs) const noexcept
{
return std::lexicographical_compare(lhs.data, lhs.data + lhs.size, rhs.data, rhs.data + rhs.size);
}
};

the reason it failes is because you compare only the length of the smaller string.
In your example only the "test" part of "test1" will be compared.
You can try this:
bool operator()(const testData & lhs,
const testData & rhs) const noexcept
{
int ret = memcmp(lhs.data, rhs.data, lhs.size<rhs.size?lhs.size:rhs.size);
return ret == 0 ? lhs.size < rhs.size : ret < 0 ;
}

Related

Writing a custom comparator for a C++ map which has a custom defined key

I'm trying to write a custom comparator for a C++ map which has a custom defined key.
struct key { int year; int no; };
map<key, detail, compare> details_map;
if the year values are equal, it must compare the no values.
I'm trying to figure out a way to write a comparator that can compare both values. So far, I am only able to write a comparator which compares one value.
struct Compare{bool operator()(const key &lhs,const key &rhs)const{return lhs.year<rhs.year;}}
Can someone please explain how a comparator works in a map?
Also, is it possible to write the comparator as a function?
Inside your operator(), simply compare the no values if the year values are equal:
struct Compare {
bool operator()(const key &lhs, const key &rhs) const {
if (lhs.year == rhs.year) {
return lhs.no < rhs.no;
}
return lhs.year < rhs.year;
}
};
And yes, a comparator can be implemented as a standalone function instead:
bool Compare (const key &lhs, const key &rhs) {
if (lhs.year == rhs.year) {
return lhs.no < rhs.no;
}
return lhs.year < rhs.year;
}
Alternatively, you can have your comparator use std::tie() to compare your key fields. See #Jarod42's answer.
Though, it would make more sense to implement operator< for your key type instead:
struct key {
int year;
int no;
bool operator<(const key &rhs) const {
if (year == rhs.year) {
return no < rhs.no;
}
return year < rhs.year;
}
};
Or
struct key {
int year;
int no;
};
bool operator<(const key &lhs, const key &rhs) {
if (lhs.year == rhs.year) {
return lhs.no < rhs.no;
}
return lhs.year < rhs.year;
}
Then you don't need a separate comparator:
map<key, detail> details_map;
std::tie allows simple lexicographical comparison:
struct Compare {
bool operator()(const key& lhs, const key& rhs) const {
return std::tie(lhs.year, lhs.no) < std::tie(rhs.year, rhs.no);
}
};
Method/function as_tuple might be interesting to avoid some repetitions:
struct key { int year; int no; };
auto as_tuple(const key& k) { return std::tie(k.year, k.no); }
struct Compare {
bool operator()(const key& lhs, const key& rhs) const {
return as_tuple(lhs) < as_tuple(rhs);
}
};

<set> with custom struct contains duplicates

I've been learning c++. I am stuck with this problem.
I have set that contains a custom struct that contains two long int's a & b. I have a custom comparer struct that compares the numbers and returns true if either a or b is different.
typedef long int li;
struct number {
number(li a1,li b1): a(a1), b(b1) {}
li a, b;
};
struct compare {
bool operator() (const number &lhs, const number& rhs) const{
return lhs.a != rhs.a || lhs.b != rhs.b;
}
};
int main() {
set<number, compare> nums;
nums.insert(number(1, 2));
nums.insert(number(1, 1));
nums.insert(number(2, 1));
nums.insert(number(1, 2));
for (auto &i : nums) {
cout << i.a << " " << i.b << endl;
}
return 0;
}
The output here is
1 2
2 1
1 1
1 2
It has two entries of 1 2. Any clarification would be appreciated.
Your comparison function should return whether some element is smaller than another, not whether or not they are equal. (More formally, it must define a "Strict weak ordering" on the elements of your set.)
Use something like
struct compare {
bool operator() (const number &lhs, const number& rhs) const{
return std::tie(lhs.a, lhs.b) < std::tie(rhs.a, rhs.b);
}
};
If you don't care about ordering, you may want to define a suitable hash function for your type and use std::unordered_set.
To avoid future problems like this, make sure to read the docs. They clearly explain what your comparison function is supposed to do.
For reference: std::tie as used above constructs tuples of references to its arguments which can then be compared lexicographically with <. This is an easy, generic and fast way to build some ordering for collections of less-than-comparable stuff.
Your comparison function needs to meet strict/weak ordering requirements.
(I actually prefer the answer using std::tie, but this may be more illustrative for newcomers)
bool compare(const number& lhs, const number& rhs)
{
if(lhs.a < rhs.a)
return true;
else if(lhs.a > rhs.a)
return false;
else
return lhs.b < rhs.b;
}

C++ implementation of ORDER BY on struct

i searched a lot here and on other sites as well but i have not found something satisfying.
what i need is quite simple task - substantially to construct ORDER BY operator in c++. this means i have struct with a number of various data type members and i need a comparator for it with members and orderings configurable. here is my pseudocode idea:
comparator.add(&MyStruct::member1, std::less);
comparator.add(&MyStruct::member2, std::greater);
std::sort(my_vector.begin(), my_vector.end(), comparator);
and i get data sorted by member1 and if it is equal member2 decides, and so on.
i am not too good in stl and templates, but i can read and decipher some code and found this as very appropriate solution: https://stackoverflow.com/a/11167563
unfortunately in my work i have to use c++ builder with faulty 32bit compiler that refuses to compile this correct code. it does support almost nothing from c++11, it has boost 1.39 available.
does someone have any solution that could work for me with my resources available? thank you in advance
EDIT:
i got very specialised solutions with hard-written comparison operators which i am aware of and which do not work here too good. i missed this in my question. my struct has at least 15 members and as i wrote, i need to often change individual sort directions for members/columns (asc, desc). too, i need to often change set of sorted members, just like in order by operator in sql, for example. also i cannot use something like stable_sort as i am just writing comparator for something like OnCompare event of some class.
It's not too difficult. First, consider the "canonical"
ordering relationship:
struct Compare
{
bool operator()( C const& lhs, C const& rhs ) const
{
return lhs.a < rhs.a
|| ( !(rhs.a < lhs.a) && lsh.b < rhs.b )
|| ( !(rhs.a < lhs.a) && !(rhs.b < lhs.b) && lhs.c < rhs .c )
|| ...
}
};
Obviously, no one would actually write something like this, but
it corresponds exactly to the formal definition of what is
needed.
Of course, if we can imagine the data members as an array, we
could rewrite this as a loop, taking advantage of the previously
established !(rhs[i-1] < lsh[i-1] in each case:
struct Compare
{
bool operator()( C const& lhs, C const& rhs ) const
{
int i = 0;
while ( i != N && !(lhs[i] < rhs[i]) && !(rhs[i] < lhs[i]) ) {
++ i;
}
return i != N && lhs[i] < rhs[i];
}
};
Or, if all of the elements are fully ordered, so that == is
also defined on them, and we can assume that it corresponds to
the equivalence relationship established by the weak partial
ordering:
struct Compare
{
bool operator()( C const& lhs, C const& rhs ) const
{
int i = 0;
while ( i != N && !(lhs[i] == rhs[i]) ) {
++ i;
}
return i != N && lhs[i] < rhs[i];
}
};
All that remains is to somehow translate this into something
that can process an arbitrary ordering of elements of arbitrary
types. There's an old saying that the solution to every problem
is an additional level of indirection, and it applies here.
First, we need some means of handling the different types of
each element. Polymorphism seems appropriate (although
templates could be made to work if the order in which the
elements were evaluated were fixed at compile time):
struct CompareOneElementOfC
{
virtual bool isLessThan( C const& lhs, C const& rhs) const = 0;
virtual bool isEqual( C const& lhs, C const& rhs) const = 0;
};
template <typename T, T C::*ptr>
struct ConcreteCompareOneElementOfC : public CompareOneElementOfC
{
virtual bool isLessThan( C const& lhs, C const& rhs) const
{
return lhs.*ptr < rhs.*ptr;
}
virtual bool isEqual( C const& lhs, C const& rhs) const
{
return lhs.*ptr == rhs.*ptr;
}
};
Depending on the types of the elements, you may need to hand
write specific concrete instances. And if any of the elements
doesn't support total ordering, you will have to omit the
isEqual, and modify the following code accordingly.
Having got this far, we need exactly one static instance of each
concrete Compare:
ConcreteCompareOneElementOfC<int, &C::a> const c1;
ConcreteCompareOneElementOfC<double, &C::b> const c2;
// ...
Finally, put the addresses of these instances in a table:
CompareOneElementOfC const* const cmp[] = { &c1, &c2 ... };
You can have different tables for different orderings. If there
are only a few, define static tables for each, and be done with
it. If the orderings can be arbitrary, create the table on the
fly before each sort, in the desired order.
Finally:
class Compare
{
CompareOneElementOfC const* const* begin;
CompareOneElementOfC const* const* end;
public:
template< size_t N >
Compare( CompareOneElementOfC const* const (&cmp)[N] )
: begin( cmp )
, end( cmp + N )
{
}
bool
operator()( C const& lhs, C const& rhs ) const
{
auto current = begin;
while ( current != end && (*current)->isEqual( lhs, rhs ) ) {
++ current;
}
return current != end && (*current)->isLessThan( lhs, rhs );
}
}
(Please note that I haven't actually tested this code, so there
are probably typos and other errors. Still, the basic idea
should be there.)
I think just overloading operator < will work for you.
struct Struct {
int member1;
int member2;
bool operator<(const Struct& rhs) const {
if (member1 != rhs.member1)
return member1 < rhs.member1
else
return member2 > rhs.member2
}
};
This way whenever any 2 instances of Struct are compared, they will be compared by the comparison function defined in operator <.
So a simple std::sort(vec.begin(), vec.end()) will just work!
EDIT:
Otherwise you can always define a functor which can be used to compare each element. This is just a class with an overloaded operator () which is used for comparison.
class ComparisonClass {
public:
bool operator()(const Struct& lhs, const Struct& rhs) {
if (lhs.member1 != rhs.member1)
return lhs.member1 < rhs.member1
else
return lhs.member2 > rhs.member2
}
};
You can additionally define some member values of the ComparisonClass which define the order of comparisons.
Using it would be calling it like so std::sort(vec.begin(), vec.end(), ComparisonClass());
EDIT2:
Slightly more elaborate code -
class ComparisonClass {
public:
bool operator()(const Struct& lhs, const Struct& rhs) {
for(int i=0; i<m_comparisonOrder.size(); i++) {
int pos = m_comparisonOrder[i];
if (lhs[pos] != rhs[pos]) {
if (m_comparisonType[pos])
return lhs[pos] < rhs[pos];
else
return lhs[pos] > rhs[pos];
}
}
}
std::vector<int> m_comparisonOrder.
std::vector<bool> m_comparisonType;
};
Here I'm assuming that Struct has an operator [] which returns the appropriate member variable.
Why not have a specialized comparator function which first checks member1 and if equal then checks member2?
Like
bool comparator(const MyStruct& s1, const MyStruct& s2)
{
if (s1.member1 == s2.member1)
return s1.member2 > s2.member2;
else
return s1.member1 < s2.member1;
}

How can I insert custom objects into a std::map?

I've read through the map::map reference at cplusplus.com and I'm still not sure how to get this to work. All I want to do is create a map like the following:
std::map<TriSpec, unsigned int> TriSpecMap;
Then I want to insert into it as follows:
result = TriSpecMap.insert(std::make_pair(triSpecObject, anUnsignedInt));
The following is a short example of my TriSpec header and .cpp:
//TriSpec.h
#ifndef TRISPEC_H
#define TRISPEC_H
class TriSpec
{
public:
TriSpec(void);
~TriSpec(void);
unsigned int m_position;
};
bool operator< (const TriSpec& lhs, const TriSpec& rhs);
#endif
//TriSpec.cpp
#include "TriSpec.h"
TriSpec::TriSpec(void){}
TriSpec::~TriSpec(void){}
bool operator< (const TriSpec& lhs, const TriSpec& rhs)
{
if (lhs.m_position < rhs.m_position) return true;
else return false;
}
Am I overloading the correct operator? Is the function/formatting I'm using correct? When I look at result.second, it is always true, even when I know the object being inserted should already exist in the map.
I do not see any problem with your code except that bool operator should be friend method (it does not even compile without friend keyword):
bool friend operator< (const TriSpec& lhs, const TriSpec& rhs)
{
return (lhs.m_position < rhs.m_position);
}
Then it works as expected:
int main(int argc, _TCHAR* argv[])
{
std::map<TriSpec, unsigned int> TriSpecMap;
TriSpec triSpecObject1;
triSpecObject1.m_position = 1;
TriSpec triSpecObject2;
triSpecObject2.m_position = 1;
TriSpec triSpecObject3;
triSpecObject3.m_position = 3;
std::pair<std::map<TriSpec, unsigned int>::iterator, bool> retVal =
TriSpecMap.insert(std::make_pair(triSpecObject1, 1));
retVal = TriSpecMap.insert(std::make_pair(triSpecObject2, 1));
retVal = TriSpecMap.insert(std::make_pair(triSpecObject3, 1));
return 0;
}
The result of first insertion is true, the result of second is false a the third is true again - as it should be. The map container contains two objects then - triSpecObject1 a triSpecObject3.
That doesn't look like it would compile - your operator< doesn't return a value in all cases. (Edit: you've fixed this, thanks.) You can simplify it quite a bit:
bool operator< (const TriSpec& lhs, const TriSpec& rhs)
{
return (lhs.m_position < rhs.m_position);
}
Since you don't show the complete code that inserts into the map, it's impossible to say why the return value .second always returns true.
Since C++11 you can also use a lambda expression instead of providing an operator< for your class. As a result, you can create your map with only two lines of code as follows:
int main()
{
auto comp = [](const TriSpec& t1, const TriSpec& t2) { return t1.m_position < t2.m_position; };
std::map<TriSpec, unsigned int, decltype(comp)> TriSpecMap(comp);
TriSpec t1, t2, t3;
t1.m_position = 1;
t2.m_position = 3;
t3.m_position = 5;
auto retVal = TriSpecMap.emplace(t1, 4);
retVal = TriSpecMap.emplace(t2, 2);
retVal = TriSpecMap.emplace(t3, 6);
for (auto const &kv : TriSpecMap)
std::cout << kv.first.m_position << ": " << kv.second << std::endl;
return 0
}
Output:
1: 4
3: 2
5: 6
Code on Ideone

How use std::multiset with multiple comparator function?

Good afternoon, I have a C++ class Range which implements a operator < for use by std::multiset<Range> ranges_type.
Since the multiset constructor don't specify a a custom comparator functor, it uses the std::less operator <.
However, I need to use a second comparator functor for std::multiset ranges_type. Specifically, I would specify a second comparator:
std::multiset<Range, PointerCompare> where struct PointerCompare looks this :
struct PointerCompare{
bool operator()(const Range& a, const Range& b) const {
return (a.mPtr == b.mPtr)
}
Is it possible to use std:multiset with multiple comparator functions or is there a workaround? Thank you
The class Range looks this:
class Range {
public:
explicit Range(int item){
mLow = item;
mHigh = item;
mPtr = 0;
}
Range(int low, int high, char* ptr = 0,char* mapptr = 0){
mLow = low;
mHigh = high;
mPtr = ptr;
}
Range(void){
mLow = 0;
mHigh = 0;
mPtr = 0;
}
Range(const Range& r):
mLow(r.mLow),
mHigh(r.mHigh),
mPtr(r.mPtr)
{
}
bool operator==(const Range& rhs) const{
return (mLow <= rhs.mLow && mHigh >= rhs.mHigh);
}
bool operator<(const Range& rhs) const{
return mHigh < rhs.mHigh;
}
int low() const { return mLow; }
int high() const { return mHigh; }
char* getPtr() const { return mPtr; }
private:
int mLow;
int mHigh;
char* mPtr;
}; // class Range
Sounds almost like you'd be better if you used something from Boost::MultiIndex rather than trying to force several different comparator functions onto a std::multiset. They have a bunch of different container types (see here.) In particular I'd look at the ordered_indices versions.
I may have found a workaround for multiple comparator functions: Here it is:
Range targetRange = Range(PreviousNCopy,PreviousN, TmpPrevMapPtr);
bool Found = std::binary_search( ranges_type.begin(), ranges_type.end(),
targetRange, MyComparator() );
where: MyComparator is a struct :
struct MyComparator {
bool operator () ( const Range& d1, const Range& d2 ) const
{
return d1.getPtr() < d2.getPtr();
}
};
std::binary_search take o(log n) time but the std::multiset ranges_type must always remain sorted. Thank you.