I would like to write a wrapper class around STL heaps that allows the user to provide their own comparator. I would like to have the comparators be functors so that they may close over some state.
For example, consider maintaining a sorted list of 2D points. The sorting criterion is distance from a given point. I'd like to provide a default comparator that sorts based on distance from the origin, but also give the user the option to compare based on distance from an arbitrary point.
My problem is: I don't know how to properly structure the functor inheritance to make this work in a flexible manner. Here is the sorted points example to illustrate what I want:
struct Point {
int x, y;
Point(int xx, int yy) : x(xx), y(yy) {}
static float dist(const Point &a, const Point &b) {
const int dx = a.x - b.x, dy = a.y - b.y;
return sqrtf(dx*dx + dy*dy);
}
};
// Abstract Point comparison base class.
class Comparator {
public:
virtual bool operator()(const Point& lhs, const Point& rhs) = 0;
};
// Sorts Points according to distance from the origin.
class DefaultComparator : public Comparator {
public:
virtual bool operator()(const Point& lhs, const Point& rhs) {
const Point zero(0,0);
const float dl = Point::dist(zero, lhs), dr = Point::dist(zero, rhs);
return dl < dr;
}
};
// Sorts Points according to distance from a given Point.
class RelativeComparator : public Comparator {
public:
RelativeComparator(Point p) : _point(p) {}
virtual bool operator()(const Point& lhs, const Point& rhs) {
const float dl = Point::dist(_point, lhs), dr = Point::dist(_point, rhs);
return dl < dr;
}
private:
const Point _point;
};
class SortedPoints {
public:
SortedPoints(Comparator &comp) : _comp(comp) {}
void push(Point p) {
_points.push_back(p);
std::push_heap(_points.begin(), _points.end(), _comp);
}
bool pop(Point &p) {
if (_points.empty()) {
return false;
} else {
std::pop_heap(_points.begin(), _points.end(), _comp);
p = _points.back();
_points.pop_back();
return true;
}
}
private:
typedef std::vector<Point> PointList;
Comparator &_comp;
PointList _points;
};
int main() {
DefaultComparator defaultComp;
RelativeComparator relativeComp(Point(100,100));
SortedPoints list1 = SortedPoints(defaultComp);
SortedPoints list2 = SortedPoints(relativeComp);
Point p(0,0);
list1.push(Point(15,15));
list1.push(Point(13,13));
list1.push(Point(5,5));
printf("List one (relative to 0,0):\n");
while (list1.pop(p)) {
printf("%d,%d\n", p.x, p.y);
}
list2.push(Point(15,15));
list2.push(Point(13,13));
list2.push(Point(5,5));
printf("List two (relative to 100,100):\n");
while (list2.pop(p)) {
printf("%d,%d\n", p.x, p.y);
}
return 0;
}
Due to the way the inheritance is structured, I'm getting a compilation error when the STL heap implementation tries to instantiate a Comparator (because it's an abstract class). The precise error is:
sortedpoints.cpp: In member function ‘void SortedPoints::push(Point)’:
sortedpoints.cpp:51: error: cannot allocate an object of abstract type ‘Comparator’
sortedpoints.cpp:17: note: because the following virtual functions are pure within ‘Comparator’:
sortedpoints.cpp:19: note: virtual bool Comparator::operator()(const Point&, const Point&)
/usr/include/c++/4.2.1/bits/stl_heap.h: In function ‘void std::push_heap(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Point*, std::vector<Point, std::allocator<Point> > >, _Compare = Comparator]’:
sortedpoints.cpp:51: instantiated from here
/usr/include/c++/4.2.1/bits/stl_heap.h:203: error: cannot allocate an object of abstract type ‘Comparator’
sortedpoints.cpp:17: note: since type ‘Comparator’ has pure virtual functions
/usr/include/c++/4.2.1/bits/stl_heap.h: In function ‘void std::__adjust_heap(_RandomAccessIterator, _Distance, _Distance, _Tp) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Point*, std::vector<Point, std::allocator<Point> > >, _Distance = long int, _Tp = Point]’:
/usr/include/c++/4.2.1/bits/stl_heap.h:238: instantiated from ‘void std::__pop_heap(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator, _Tp) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Point*, std::vector<Point, std::allocator<Point> > >, _Tp = Point]’
/usr/include/c++/4.2.1/bits/stl_heap.h:265: instantiated from ‘void std::pop_heap(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Point*, std::vector<Point, std::allocator<Point> > >]’
sortedpoints.cpp:58: instantiated from here
What is the proper way to accomplish this sort of task? If my Comparator inheritance strategy is a bad one, I'd like to know that too: this is just the first method I tried.
If you look at the documentation for push_heap, you'll see that it takes the comparator by value, so it's going to try to copy your Comparator object.
template <class RandomAccessIterator, class Compare>
void push_heap (RandomAccessIterator first, RandomAccessIterator last,
Compare comp);
Instead of holding a reference to the Comparator object in SortedPoints, you could create a std::function object that matched the Comparator function signature and pass that into push_heap (or boost::function if you're stuck on C++03).
For your code, you could try something like this:
class SortedPoints
{
public:
typedef std::function<bool (const Point& lhs, const Point& rhs)> MyComparator; // <-- Add this typedef
SortedPoints(MyComparator comp) : _comp(comp) {} // <-- Use MyComparator instead of Comparator&
void push(Point p) {
_points.push_back(p);
std::push_heap(_points.begin(), _points.end(), _comp);
}
bool pop(Point &p) {
if (_points.empty()) {
return false;
} else {
std::pop_heap(_points.begin(), _points.end());
p = _points.front();
_points.pop_back();
return true;
}
}
private:
typedef std::vector<Point> PointList;
MyComparator _comp; // <-- Use MyComparator instead of Comparator&
PointList _points;
};
I figured out a way to do what I want using regular OOP paradigms. Using the C++11 functional features suggested by #pzed is a good idea, however the rest of my codebase is not C++11 and I'd like to stick with consistent paradigms.
The strategy is to have the base Comparator class close over a subclass instance, and simply pass through the comparison to the subclass.
For example, the three classes from above become:
class Comparator {
public:
Comparator(Comparator &c) : _comparator(c) {}
virtual bool operator()(const Point& lhs, const Point& rhs) {
return _comparator(lhs, rhs);
}
private:
Comparator& _comparator;
};
// Sorts Points according to distance from the origin.
class DefaultComparator : public Comparator {
public:
DefaultComparator() : Comparator(*this) {}
virtual bool operator()(const Point& lhs, const Point& rhs) {
const Point zero(0,0);
const float dl = Point::dist(zero, lhs), dr = Point::dist(zero, rhs);
return dl < dr;
}
};
// Sorts Points according to distance from a given Point.
class RelativeComparator : public Comparator {
public:
RelativeComparator(Point p) : Comparator(*this), _point(p) {}
virtual bool operator()(const Point& lhs, const Point& rhs) {
const float dl = Point::dist(_point, lhs), dr = Point::dist(_point, rhs);
return dl < dr;
}
private:
const Point _point;
};
And the rest of the code stays the same, for example we can now do:
RelativeComparator relativeComp(Point(100,100));
SortedPoints list1 = SortedPoints(relativeComp);
as before, and it works.
Hopefully this isn't considered an abuse of inheritance.
I got this working by making 3 changes:
Removed the polymorphism in favor of templates
Added the comparator to the pop_heap call as well.
Added const to the operator ()
I get this:
// Sorts Points according to distance from the origin.
class DefaultComparator {
public:
virtual bool operator()(const Point& lhs, const Point& rhs) const {
const Point zero(0, 0);
const float dl = Point::dist(zero, lhs), dr = Point::dist(zero, rhs);
return dl < dr;
}
};
// Sorts Points according to distance from a given Point.
class RelativeComparator {
public:
RelativeComparator(Point p) : _point(p) {}
virtual bool operator()(const Point& lhs, const Point& rhs) const {
const float dl = Point::dist(_point, lhs), dr = Point::dist(_point, rhs);
return dl < dr;
}
private:
const Point _point;
};
template <class C>
class SortedPoints
{
public:
SortedPoints(C &comp) : _comp(comp) {}
void push(Point p) {
_points.push_back(p);
std::push_heap(_points.begin(), _points.end(), _comp);
}
bool pop(Point &p) {
if (_points.empty()) {
return false;
}
else {
std::pop_heap(_points.begin(), _points.end(), _comp);
p = _points.front();
_points.pop_back();
return true;
}
}
private:
typedef std::vector<Point> PointList;
C &_comp;
PointList _points;
};
int main()
{
DefaultComparator defaultComp;
RelativeComparator relativeComp(Point(100, 100));
SortedPoints<DefaultComparator> list1 = SortedPoints<DefaultComparator>(defaultComp);
Point p(0, 0);
list1.push(Point(15, 15));
list1.push(Point(13, 13));
list1.push(Point(5, 5));
printf("List one (relative to 0,0):\n");
while (list1.pop(p)) {
printf("%d,%d\n", p.x, p.y);
}
SortedPoints<RelativeComparator> list2 = SortedPoints<RelativeComparator>(relativeComp);
list2.push(Point(15, 15));
list2.push(Point(13, 13));
list2.push(Point(5, 5));
printf("List two (relative to 100,100):\n");
while (list2.pop(p)) {
printf("%d,%d\n", p.x, p.y);
}
return 0;
}
Related
I have this piece of code:
class ISerializable
{
public:
virtual bool operator==(const ISerializable* /*value*/) const { return false;};
virtual bool operator!=(const ISerializable* /*value*/) const { return true;};
};
class Point2I : public ISerializable
{
public:
bool operator==(const Point2I& value)
{
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value)
{
return !(*this == value);
}
public:
int x;
int y;
};
class Coordinate : public ISerializable
{
public:
virtual bool operator==(const Coordinate& value) const;
virtual bool operator!=(const Coordinate& value) const;
};
It is causing me -Woverloaded-virtual warning on gcc compiler.
I understand this warning due to that function declaration in Point2I hides virtual functions from ISerializable.
But I am not sure if just missing const in Point2I can cause this warning.
Can you please help me understand if it is const which is causing this warning or something else? Warning description from gcc didn't mention anything specifically.
Update:
I found another class Coordinate in my code base which was already overriding this and gcc not throwing warning for this. Only difference in Point2I and Coordinate is I didn't declare it virtual with const in Point2I. It appears just const is hiding base class declaration.
if it is const which is causing this warning or something else?
I'd say that it's something else, namely that you are not actually overriding the base class methods, even if you add const.
The argument const ISerializable* is not the same as const Point2I&.
One solution could be to override the base class methods, using const ISerializable& as the argument, and cast in the overridden methods:
class ISerializable {
public:
// add virtual destructor if you need to delete objects through
// base class pointers later:
virtual ~ISerializable() = default;
virtual bool operator==(const ISerializable&) const { return false; }
virtual bool operator!=(const ISerializable&) const { return true; }
};
class Point2I : public ISerializable {
public:
bool operator==(const ISerializable& value) const override {
auto rhs = dynamic_cast<const Point2I*>(&value);
// rhs will be nullptr if the cast above fails
return rhs && (x == rhs->x && y == rhs->y);
}
bool operator!=(const ISerializable& value) const override {
return !(*this == value);
}
private:
int x = 0;
int y = 0;
};
Example usage:
#include <iostream>
class Foo : public ISerializable { // another ISerializable
public:
};
int main() {
Point2I a, b;
std::cout << (a == b) << '\n'; // true - using Point2I::operator==
Foo f;
std::cout << (a == f) << '\n'; // false - using Point2I::operator==
std::cout << (f == a) << '\n'; // false - using ISerializable::operator==
// this makes the default implementation in ISerializable utterly confusing:
std::cout << (f == f) << '\n'; // false - using ISerializable::operator==
}
Another possible solution could be using CRTP but this would not work if you want to compare different types derived from ISerializable<T>:
template<class T>
class ISerializable {
public:
virtual ~ISerializable() = default;
virtual bool operator==(const T&) const = 0;
virtual bool operator!=(const T&) const = 0;
};
class Point2I : public ISerializable<Point2I> {
public:
bool operator==(const Point2I& value) const override {
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value) const override {
return !(*this == value);
}
public:
int x;
int y;
};
There are two problems.
The first one is different types of parameters
In these functions the parameters have the pointer type const ISerializable*
virtual bool operator==(const ISerializable* /*value*/) const { return false;};
virtual bool operator!=(const ISerializable* /*value*/) const { return true;};
and in these functions the parameters have the referenced type const Point2I&
bool operator==(const Point2I& value)
{
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value)
{
return !(*this == value);
}
The second one is that the first functions are constant member functions while the second functions are not constant member functions.
I hope to create a static bool template function that multiple classes may use. I am using this function as a comparator to sort a vector of points. This is what i've done so far:
class.h
class Point2D
{
protected:
int x;
int y;
public:
int getX();
int getY();
Point2D();
Point2D(int x, int y);
template< typename T>
T sortAscending(T a, T b )
{
return a.getX() < b.getX();
}
static bool sortAscending(Point2D a, Point2D b);
}
Inside main.cpp
// my vector contains objects of Point2D that i wish to
//sort according to the value of x coordinates.
sort(p2Vec.begin(),p2Vec.end(),Point2D::sortAscending);
Gives me error:
error: no matching function for call to
‘sort(std::vector::iterator, std::vector::iterator,
)’
Does anyone know what i am doing wrong?
Use a lambda function here like following :
std::sort(p2Vec.begin(),p2Vec.end(),
[](const Point2D & p1, const Point2D & p2) {
return Point2D::sortAscending( p1, p2);
});
See here
Syntax would be:
std::sort(p2Vec.begin(), p2Vec.end(), &Point2D::sortAscending<Point2D>);
and requires the method to be static.
But better create a struct outside:
struct LessByGetX
{
template <typename T>
bool operator () (const T& lhs, const T& rhs) const
{
return lhs.getX() < rhs.getX();
}
};
and use it:
std::sort(p2Vec.begin(), p2Vec.end(), LessByGetX{});
Alternatively, you might use directly lambda:
std::sort(p2Vec.begin(), p2Vec.end(), [](const T& lhs, const T& rhs)
{
return lhs.getX() < rhs.getX();
});
I have a templated class MatchBase with a function for the operator == as such
template<typename Element>
class MatchBase{
virtual bool operator ==(const MatchBase<Element>& m) const{
if(_v1 == m.getFirst() && _v2 == m.getSecond()){
return true;
}
return false;
}
I know have a daughter class Match that is template specialized. The class Place used for the specialization does not have an operator== to do the comparison. Thus I'm trying to override the operator== to work with the class Place.
On the things I have tried :
class Match : public betterGraph::MatchBase<graphmatch::Place>{
public :
Match(const graphmatch::Place& v, const graphmatch::Place& vv) :
betterGraph::MatchBase<graphmatch::Place>(v, vv)
{};
virtual bool operator ==(const Match& m) const{
if(_v1.mass_center == m.getFirst().mass_center && _v2.mass_center == m.getSecond().mass_center){
return true;
}
return false;
}
};
I also tried
virtual bool operator ==(const betterGraph::MatchBase<graphmatch::Place>& m) const{
if(_v1.mass_center == m.getFirst().mass_center && _v2.mass_center == m.getSecond().mass_center){
return true;
}
return false;
}
But I always hit an error of the type :
error: no match for ‘operator==’ (operand types are ‘const AASS::graphmatch::Place’ and ‘const AASS::graphmatch::Place’)
if(_v1 == m.getFirst() && _v2 == m.getSecond()){
Because it tries to compile the method from the Base class.
Is there any way for me to override this function of the base class in the daughter class ? I've read the question here but here it's the method that is specialized while my class is specialized so I don't see how to do a forward declaration :/.
The function may be virtual but it's still initialized when you inherit your base class.
This is essential as you might write something like this:
MatchBase<Place> test = Match(p1,p2);
MatchBase<Place> is the base class of Match yet they are not the same.
Calling MatchBase<Place>::operator==() will still call the function defined in your template base class.
You have now multiple option:
- make the function in the base class a pure virtual
- implement Place::operator==()
- pass a comperator as argument to your base class as argument
The first two should be clear (if not please ask). For the third this might be a one possible way to do it:
template<typename Element, typename Less = std::less<Element>>
class MatchBase {
protected:
Element _v1;
Element _v2;
public:
MatchBase(const Element& v, const Element& vv) : _v1(v), _v2(vv)
{}
virtual bool operator ==(const MatchBase<Element, Less>& m) const {
Less less;
bool v1Equal = !less(_v1, m.getFirst()) && !less(m.getFirst(), _v1);
bool v2Equal = !less(_v2, m.getSecond()) && !less(m.getSecond(), _v2);
return v1Equal && v2Equal;
}
const Element& getFirst() const { return _v1; }
const Element& getSecond() const { return _v2; }
};
struct Place
{
int mass_center;
};
struct PlaceLess
{
bool operator()(const Place& p1, const Place& p2)
{
return p1.mass_center < p2.mass_center;
};
};
class Match : public MatchBase <Place, PlaceLess>
{
public:
Match(const Place& v, const Place& vv) :
MatchBase<Place, PlaceLess>(v, vv)
{};
};
Another way might be to specialize std::less<T> in this context. So you won't need to pass it as template parameter.
template<typename Element>
class MatchBase {
protected:
Element _v1;
Element _v2;
public:
MatchBase(const Element& v, const Element& vv) : _v1(v), _v2(vv)
{}
virtual bool operator ==(const MatchBase<Element>& m) const {
std::less<Element> less;
bool v1Equal = !less(_v1, m.getFirst()) && !less(m.getFirst(), _v1);
bool v2Equal = !less(_v2, m.getSecond()) && !less(m.getSecond(), _v2);
return v1Equal && v2Equal;
}
const Element& getFirst() const { return _v1; }
const Element& getSecond() const { return _v2; }
};
struct Place
{
int mass_center;
};
template<>
struct std::less<Place>
{
bool operator()(const Place& p1, const Place& p2)
{
return p1.mass_center < p2.mass_center;
};
};
class Match : public MatchBase <Place>
{
public:
Match(const Place& v, const Place& vv) :
MatchBase<Place>(v, vv)
{};
};
Of course you could merge these ways so you might override the Less template parameter if needed.
If you don't plan on using predefined types (thinking of int, std::string, etc...) you could also make sure that the class passed as Element must inherit a class/struct that enforces that operator== is implemented:
template <typename T>
struct IComparable
{
virtual bool operator==(const T& other) const = 0;
};
template<typename Element>
class MatchBase {
static_assert(std::is_base_of<IComparable<Element>, Element>::value, "Element must implement comparable");
protected:
Element _v1;
Element _v2;
public:
MatchBase(const Element& v, const Element& vv) : _v1(v), _v2(vv)
{}
virtual bool operator ==(const MatchBase<Element>& m) const {
return _v1 == m._v1 && _v2 == m._v2;
}
};
struct Place : public IComparable<Place>
{
int mass_center;
bool operator==(const Place& other) const
{
return mass_center == other.mass_center;
};
};
class Match : public MatchBase <Place>
{
public:
Match(const Place& v, const Place& vv) :
MatchBase<Place>(v, vv)
{};
};
I want to use priority_queue and pass it to a function:
// compare points according their distance to some target point
struct MyComparator {
Point target;
MyComparator(Point t) : target(t) {}
bool operator() (const Point& p1, const Point& p2) {
return distance(target, p1) < distance(target, p2);
}
};
typedef priority_queue<Point, vector<Point>, MyComparator> myque;
void myfunc(const Point& target, myque& que) { ... }
// call myfunc
Point target = ...;
myque queue(MyComparator(target));
myfunc(target, queue);
// error :
no matching function for call to ‘myfunc(const Point&, myque (&)(MyComparator))’
How can I resolve this error?
Thanks.
class Point
{
int x;
}
static Point referencePoint;
struct Comparator
{
bool AbsComparator(const Point& p1, const Point& p2)
{
return abs(p1.x - referencePoint.x) < abs(p2.x - referencePoint.x);
}
};
list<Point> points;
points.sort(Comparator::AbsComparator);
But I CANNOT use a static referencePoint for multithreading safe, is there any other way??
Thanks.
Make it part of Comparator:
struct Comparator
{
Point referencePoint;
Comparator(Point referencePoint): referencePoint(referencePoint) {}
Comparator(const Comparator& rhs) {
referencePoint = rhs.referencePoint;
}
bool operator()(const Point& p1, const Point& p2) const
{
return abs(p1.x - referencePoint.x) < abs(p2.x - referencePoint.x);
}
};
...
points.sort(Comparator(refP));
Why not simply save the referencePoint or rather the integer value inside Point as a member inside your Comparator, that way you always access it in the comparator function.
Avoid global variables. That is start of writing multi-threaded code.
Use local variables instead:
Point referencePoint(10,20); //use local variable!
points.sort(Comparator(referencePoint));
Or simpy this,
points.sort(Comparator(Point(10,20)));
where Comparator is a functor defined as:
struct Comparator
{
Point referencePoint;
explicit Comparator(const Point & pt) : referencePoint(pt) {}
bool operator() (const Point& p1, const Point& p2) const
{
return abs(p1.x - referencePoint.x) < abs(p2.x - referencePoint.x);
}
};
You're done!
Notice the implementation of operator(). This makes the class a functor.
You can simply store this variable, either in a destructured or structured manner.
// destructured (only store what you need)
class ReferenceComparator {
public:
explicit ReferenceComparator(int x): _x(x) {}
explicit ReferenceComparator(Point const& p): _x(p.x) {}
bool operator()(Point const& left, Point const& right) const {
return abs(left.x - _x) < abs(right.x - _x);
}
private:
int _x;
}; // class ReferenceComparator
And then use it as:
list.sort(ReferenceComparator(myReferencePoint));
I would advise not using a list if you need sorting through. list are not good at it...