How use std::multiset with multiple comparator function? - c++

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.

Related

Hash function for a smart pointer class as key for an unordered map

So, I have a pointer wrapper class which stores only a pointer and I need to use this class instance as a key in an unordered map. I currently have a similar setup with this pointer wrapper instances as keys to a std::map by overriding bool operator< but for an unordered map setup I would need to override two other operators == and (). I've figured how == operator implementation would be. but not sure about an implementation for the () operator. what sort of hash implementation setup should I be doing in this case? I've checked in places and most examples cover non pointer cases and they use two Key items and form hash for each and compare them for () implementation.
template <class T>
class PointerWrap{
public:
T* pointer;
bool operator<(const PointerWrap& other)const{return *pointer < *other.pointer;}
bool operator==(const PointerWrap& other)const{return *pointer == *other.pointer;}
//size_t operator()(const PointerWrap& other)const{return (*pointer)(*other.pointer);}
};
class VarType{
bool operator<(const VarType& other)const{return this < &other;}
bool operator==(const VarType& other)const{return this == &other;}
size_t operator()(const VarType& other)(.?.?.}
};
//Desired setup.
std::unordered_map<PointerWrap<VarType>,Value> mymap;
Since you seem to need a hash function only for using PointerWrappers in an unordered map, the hash function in the standard library should serve you well. (But these are not cryptographically secure hash functions so don't use them for anything else). Here is some code to show how to do this:
#include <unordered_map>
#include <iostream>
template <class T>
class PointerWrap {
public:
T* pointer;
bool operator<(const PointerWrap& other)const { return *pointer < *other.pointer; }
bool operator==(const PointerWrap& other)const { return *pointer == *other.pointer; }
size_t operator()(const PointerWrap& other) const {return (*pointer)(*other.pointer);}
};
class VarType {
public: // PointerWrap has no access to these operators without a public access specifier
bool operator<(const VarType& other)const { return this < &other; }
bool operator==(const VarType& other)const { return this == &other; }
// Pointless to hash a object without any data
std::size_t operator()(const VarType& other)const {
return 0;
}
};
// Specialization of std::hash for PointerWrap<T>
template<typename T>
class std::hash<PointerWrap<T>> {
public:
size_t operator()(PointerWrap<T> v) const{
return std::hash<T*>()(v.pointer);
}
};
int main() {
// your desired setup compiles.
std::unordered_map<PointerWrap<VarType>, int> mymap;
PointerWrap<VarType> a;
mymap[a] = 5;
std::cout << mymap[a] << std::endl;
return 0;
}

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

How do I establish a comparator for a set c++

I have a set, and for this set, I need two different comparators. For example, for a set frontier I need to sort by cost, but I have another set board which needs to be sorted by coordinates. I know you can define a comparator for each set using the comparator as the second argument, but I have tried this and it gave me an error.
The code I tried to use:
struct tile {
int id;
int xCord;
int yCord;
int cost;
...
bool operator<(const tile& Rhs) const {
if (cost < Rhs.cost) {
return true;
}
else if (cost < Rhs.cost) {
return false;
}
else {
if (id < Rhs.id) {
return true;
}
else
return false;
}
}
...
};
The other struct that I'm using for the comparator (I know this is most likely incorrect, which is why I'm asking for help.):
struct costComp {
int id;
int xCord;
int yCord;
int cost;
costComp() {}
costComp(int a, int b, int c, int d = 0) :
id(a),
xCord(b),
yCord(c),
cost(d) {}
bool operator<( const tile& Rhs) const {
if (xCord < Rhs.xCord)
return true;
else if (xCord < Rhs.xCord)
return false;
else {
if (yCord < Rhs.yCord)
return true;
else if (yCord < Rhs.yCord)
return false;
else
return false;
}
}
};
Then, I define the set as:
set<tile,costComp> startBoard;
The error I got:
c2064: term does not evaluate to a function taking 2 arguments
Any help is greatly appreciated.
the Compare parameter in std::set is intended to be some callable type that can be invoked with (const tile&, const tile&). This means you can use a functor that overloads operator(), for example, like this:
struct Comp {
bool operator()(const tile& lhs, const tile& rhs) const {
if (lhs.id < rhs.id) return true;
if (lhs.id > rhs.id) return false;
if (lhs.xCord < rhs.xCord) return true;
if (lhs.xCord > rhs.xCord) return false;
if (lhs.yCord < rhs.yCord) return true;
if (lhs.yCord > rhs.yCord) return false;
return lhs.cost < rhs.cost;
}
// or maybe, if this logic already exists:
bool operator()(const tile& lhs, const tile& rhs) const {
return lhs < rhs; // invoke tile::operator<(const tile&)
}
};
...
std::set<tile, Comp> myset;
This way, the comparator struct doesn't need to keep track of the details of any one tile object, and the redundant members of costComp can be removed.
If you want the comparator to be configurable, you can add members to the Comp struct definition and initialize them in a constructor call when you instantiate the set:
struct Comp {
Comp(bool use_cost = false /* default behavior */) : m_use_cost(use_cost) {}
bool operator()(const tile& lhs, const tile& rhs) const {
if (m_use_cost){
return lhs.cost < rhs.cost;
} else {
...
}
}
private:
const bool m_use_cost;
};
...
// default comparison, won't use cost
std::set<tile, Comp> setA;
// specify custom behaviour
std::set<tile, Comp> setB {Comp{true /* right here */}};
Obviously, the configurability is not limited to one or more bools. It might make sense to have some enum with values like SortByCost, SortByXcoord. Alternatively, you could have a separate functor struct that does each, but this means that sets with different comparators will have different types and will not be inter-copyable or moveable.

Multiple Overloading of Operators

as you can see from the code I want to overload the < operator twice. 1 to sort by dist and the other by nodeID. I would like to check if there is any way to call the different overloaded methods. For example in the compLoc method, when I use the sort() method I want it to be sorted by nodeID but in other methods I want it to be sorted by dist.
struct AttSet{
int nodeID;
double dist;
bool operator < (const AttSet & str) const{
return (dist < str.dist);
}
/*
bool operator <(const AttSet & str){
return (nodeID < str.nodeID);
*/
bool operator == (const AttSet & str){
return nodeID == str.nodeID;
}};
void compLoc(Edge *edge, vector<Node*> &vertices){
int l = edge->length;
int vl = edge->head->nodeID;
int vr = edge->tail->nodeID;
/*
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end());
sort(vertices[vr]->attSet.begin(), vertices[vr]->attSet.end());
vector<AttSet> vInterSec;
set_intersection(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), vertices[vr]->attSet.begin(), vertices[vr]->attSet.end(), back_inserter(vInterSec));
*/}
You cannot have overloads that have the same signature. This holds for any function. How would you try to decide which version to use?
If you want sort the object based on different criteria you should use the sort version that takes a custom comparer function as the third argument.
Edit:
Of course you need to provide the comparer. I would suggest providing the comparers as static functions of the class if you have such power. This way you will not pollute enclosing namespace and you can access privates of the class with out exposing any getters. Since your properties are public the lambda would suffice, and probably be the best/cleanest approach.
Feeling adventurous I made a simple c++11 exercise program. For what it's worth, if you ever decided to go for proper encapsulation, I've shown both approaches:
#include <iostream>
#include <algorithm>
#include <vector>
#include <initializer_list>
#include <cassert>
using namespace std;
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v){
for(const auto& el : v){
out << el << '\n';
}
return out;
}
class A {
int a;
int b;
public:
A(std::initializer_list<int> l){
assert(l.size() == 2);
auto i = l.begin();
a = *i;
++i;
b = *i;
}
friend std::ostream& operator<<(std::ostream& stream, const A& e){
return stream << e.a << ' ' << e.b;
}
static bool compareViaA(const A& lhs, const A& rhs){
return rhs.a > lhs.a;
}
static bool compareViaB(const A& lhs, const A& rhs){
return rhs.b > lhs.b;
}
};
int main() {
std::vector<A> v {{2,3}, {3,2}, {1,4}, {4,1}};
//sort(v.begin(), v.end(), [](const A& a, const A& b){return a.a > b.a;}) // fails because of privacy violation
sort(v.begin(), v.end(), A::compareViaA);
std::cout << v << '\n';
sort(v.begin(), v.end(), A::compareViaB);
std::cout << v << '\n';
return 0;
}
Live: http://ideone.com/lDMujx.
I think you can implement this by using functor and take the comparator(operator< overload) outside the AttSet.
Here is a simple example:
struct AtrComparator {
bool distcmp;
AttrComparator(bool distcmp): distcmp(distcmp) {}
bool operator() (const AttSet &s1, const AttSet &s2) {
if(distcmp) {
return s1.dist < s2.dist;
} else {
return s1.nodeID < s2.nodeID;
}
}
}
And then you can do the sort through different feed, dist or nodeID.
.e.g:
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), AttComparator(true));
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), AttComparator(false));
You can't do that. They have the same signature exactly.
Use a functor or a lambda and pass it to whatever algorithm you want.
std::sort(std::begin(container), std::end(container),
[](const element_type& lhs, const element_type& rhs) { return ...; });
Another way to do this:
struct compare_by_node_id {
bool operator()(const AttSet& lhs, const AttSet& rhs) const {
return lhs.nodeID < rhs.nodeID;
}
};
struct compare_by_dist {
bool operator()(const AttSet& lhs, const AttSet& rhs) const {
return lhs.dist < rhs.dist;
}
};
And you could pass that to the algorithm like:
std::sort(std::begin(container), std::end(container), compare_by_node_id());
you cannot do that because compiler doesn't see difference between:
bool operator < (const AttSet & str) const; //this const doesn't allow to override any property of object(instance of AttSet) if I remember
and
bool operator < (const AttSet & str);
there're the same same return type, same parameter (same signature)
compiler cannot choose which one is better
There's not a great way to do this as far as I am aware, since the compiler will see these as the exact same and will throw an error. If you need to do this, use the < operator as whatever will occur the most often, and then write a method that you can call to compare two object. Something like this:
bool operator< (const Blah &blah) const {
return (most often operation)
}
bool Blah::other_operation(const Blah &blah) const {
return (other operation)
}

Is it possible to define the length of the type to sort in STXXL at run time?

I have an application that requires a built-in sort and I'm hoping to replace the existing sort mechanism with the sort provided by STXXL. I have successfully tested it using STXXL, but my problem is that, although a specific run of the sort needs to operate on fixed length strings, the length is determined at run-time and can be anywhere between 10 bytes and 4000 bytes. Always allowing for 4000 bytes will obviously be grossly inefficient if the actual length is small.
For those not familiar with STXXL, I believe the problem roughly equates to defining a std::vector without knowing the size of the objects at compilation time. However, I'm not a C++ expert - the application is written in C.
In my test this is the type that I am sorting:
struct string80
{
char x[80];
};
and this is the type definition for the STXXL sorter:
typedef stxxl::sorter<string80, sort_comparator80> stxxl_sorter80;
The problem is that I don't want to hard-code the array size to '80'.
The only solution I can come up with, is to define a number of structures of varying lengths and pick the closest at run-time. Am I missing a trick? Am I thinking in C rather than C++?
What if we store objects (records) of size n in a flat stxxl::vector of chars. Then, define a custom iterator based on stxxl::vector::iterator that merely skips n bytes on each increment. This will work with std::sort and even tbb::sort, when used std::vector instead of STXXL's. I see that STXXL's ExtIterator has a lot of additional traits. Is it possible to define them correctly for such an iterator?
#include <vector>
#include <cassert>
#include <cstdlib>
#include <stxxl.h>
#include <iostream>
#include <algorithm>
typedef std::vector<char>::iterator It;
class ObjectValue;
//This class defines a reference object that handles assignment operations
//during a sorting
class ObjectReference
{
public:
ObjectReference() : recordSize_(0) {}
ObjectReference(It ptr, size_t recordSize) : ptr_(ptr), recordSize_(recordSize) {}
void operator = (ObjectReference source) const
{
std::copy(source.ptr_, source.ptr_ + recordSize_, ptr_);
}
void operator = (const ObjectValue & source) const;
It GetIterator() const
{
return ptr_;
}
size_t GetRecordSize() const
{
return recordSize_;
}
private:
It ptr_;
size_t recordSize_;
};
//This class defines a value object that is used when a temporary value of a
//record is required somewhere
class ObjectValue
{
public:
ObjectValue() {}
ObjectValue(ObjectReference prx) : object_(prx.GetIterator(), prx.GetIterator() + prx.GetRecordSize()) {}
ObjectValue(It ptr, size_t recordSize) : object_(ptr, ptr + recordSize) {}
std::vector<char>::const_iterator GetIterator() const
{
return object_.begin();
}
private:
std::vector<char> object_;
};
//We need to support copying from a reference to an object
void ObjectReference::operator = (const ObjectValue & source) const
{
std::copy(source.GetIterator(), source.GetIterator() + recordSize_, ptr_);
}
//The comparator passed to a sorting algorithm. It recieves iterators, converts
//them to char pointers, that are passed to the actual comparator tha handles
//object comparison
template<class Cmp>
class Comparator
{
public:
Comparator() {}
Comparator(Cmp cmp) : cmp_(cmp) {}
bool operator () (const ObjectReference & a, const ObjectReference & b) const
{
return cmp_(&*a.GetIterator(), &*b.GetIterator());
}
bool operator () (const ObjectValue & a, const ObjectReference & b) const
{
return cmp_(&*a.GetIterator(), &*b.GetIterator());
}
bool operator () (const ObjectReference & a, const ObjectValue & b) const
{
return cmp_(&*a.GetIterator(), &*b.GetIterator());
}
bool operator () (const ObjectValue & a, const ObjectValue & b) const
{
return cmp_(&*a.GetIterator(), &*b.GetIterator());
}
private:
Cmp cmp_;
};
//The iterator that operates on flat byte area. If the record size is $n$, it
//just skips $n$ bytes on each increment operation to jump to the next record
class RecordIterator : public std::iterator<std::random_access_iterator_tag, ObjectValue, size_t, RecordIterator, ObjectReference>
{
public:
RecordIterator() : recordSize_(0) {}
RecordIterator(It ptr, size_t recordSize) : ptr_(ptr), recordSize_(recordSize) {}
ObjectReference operator * () const
{
return ObjectReference(ptr_, recordSize_);
}
ObjectReference operator [] (size_t diff) const
{
return *(*this + diff);
}
It GetIterator() const
{
return ptr_;
}
size_t GetRecordSize() const
{
return recordSize_;
}
RecordIterator& operator ++()
{
ptr_ += recordSize_;
return *this;
}
RecordIterator& operator --()
{
ptr_ -= recordSize_;
return *this;
}
RecordIterator operator ++(int)
{
RecordIterator ret = *this;
ptr_ += recordSize_;
return ret;
}
RecordIterator operator --(int)
{
RecordIterator ret = *this;
ptr_ -= recordSize_;
return ret;
}
friend bool operator < (RecordIterator it1, RecordIterator it2);
friend bool operator > (RecordIterator it1, RecordIterator it2);
friend bool operator == (RecordIterator it1, RecordIterator it2);
friend bool operator != (RecordIterator it1, RecordIterator it2);
friend size_t operator - (RecordIterator it1, RecordIterator it2);
friend RecordIterator operator - (RecordIterator it1, size_t shift);
friend RecordIterator operator + (RecordIterator it1, size_t shift);
private:
It ptr_;
size_t recordSize_;
};
bool operator < (RecordIterator it1, RecordIterator it2)
{
return it1.ptr_ < it2.ptr_;
}
bool operator > (RecordIterator it1, RecordIterator it2)
{
return it1.ptr_ > it2.ptr_;
}
bool operator == (RecordIterator it1, RecordIterator it2)
{
return it1.ptr_ == it2.ptr_;
}
bool operator != (RecordIterator it1, RecordIterator it2)
{
return !(it1 == it2);
}
RecordIterator operator - (RecordIterator it1, size_t shift)
{
return RecordIterator(it1.ptr_ - shift * it1.recordSize_, it1.recordSize_);
}
RecordIterator operator + (RecordIterator it1, size_t shift)
{
return RecordIterator(it1.ptr_ + shift * it1.recordSize_, it1.recordSize_);
}
size_t operator - (RecordIterator it1, RecordIterator it2)
{
return (it1.ptr_ - it2.ptr_) / it1.recordSize_;
}
namespace std
{
//We need to specialize the swap for the sorting to work correctly
template<>
void swap(ObjectReference & it1, ObjectReference & it2)
{
ObjectValue buf(it1.GetIterator(), it1.GetRecordSize());
std::copy(it2.GetIterator(), it2.GetIterator() + it2.GetRecordSize(), it1.GetIterator());
std::copy(buf.GetIterator(), buf.GetIterator() + it1.GetRecordSize(), it2.GetIterator());
}
}
//Finally, here is the "user"-defined code. In the example, "records" are
//4-byte integers, although actual size of a record can be changed at runtime
class RecordComparer
{
public:
bool operator ()(const char * aRawPtr, const char * bRawPtr) const
{
const int * aPtr = reinterpret_cast<const int*>(aRawPtr);
const int * bPtr = reinterpret_cast<const int*>(bRawPtr);
return *aPtr < *bPtr;
}
};
int main(int, char*[])
{
size_t size = 100500;
//Although it is a constant, it is easy to change to in runtime
size_t recordSize = sizeof(int);
std::vector<int> intVector(size);
std::generate(intVector.begin(), intVector.end(), rand);
const char * source = reinterpret_cast<const char*>(&intVector[0]);
std::vector<char> recordVector;
std::copy(source, source + recordVector.size(), &recordVector[0]);
RecordIterator begin(recordVector.begin(), recordSize);
RecordIterator end(recordVector.end(), recordSize);
//Sort "records" as blocks of bytes
std::sort(begin, end, Comparator<RecordComparer>());
//Sort "records" as usual
std::sort(intVector.begin(), intVector.end());
//Checking that arrays are the same:
for (; begin != end; ++begin)
{
size_t i = begin - RecordIterator(recordVector.begin(), recordSize);
It it = (*(begin)).GetIterator();
int* value = reinterpret_cast<int*>(&(*it));
assert(*value == intVector[i]);
}
return 0;
}
There is no good solution here, at least not with STXXL.
The STXXL sorter is highly optimized, and the code requires the data type's size to be provided at compile time via template parameters. I don't see that this will, or even should change.
The method of instantiating classes for many different parameters is not nice, but pretty common practise. Just think of all the different std::vector instances used in simple C++ programs, which could all be handled via void* functions in C.
Depending on how much code you want to roll out, try instanciating powers of two, and then more fine grain for your common parameters.