Related
I have a class ShapeDisplay that stores a set of Rectangles. I would like to store them sorted, therefore I use a std::set. My intention is to provide a custom comparator, which compares the origin (x, y) of the rectangle to a reference point (x, y) in the display.
However, in order to achieve this, the comparator needs access to m_reference. How do I use a custom comparator, that needs access to the class members? Is my design flawed? I know there are newer ways to provide the comparator as in this link, but that doesn't solve my access issue.
Alternatively, I could just have a std::vector that I keep sorted, such that each new Rectangle is inserted in the right position. But since std::set::insert() should do that automatically with a custom comparator, I would prefer that.
Thank you.
struct Point
{
int x;
int y;
};
struct Rectangle
{
int x;
int y;
int width;
int height;
};
class ShapeDisplay
{
void insertShape(Rectangle rect)
{
m_shapes.insert(rect);
}
void setReference(Point reference)
{
m_reference = reference;
}
private:
struct CenterComparator
{
bool operator() (const Rectangle & a, const Rectangle & b) const
{
double distA = std::sqrt(std::pow(a.x - m_reference.x, 2)
+ std::pow(a.y - m_reference.y, 2));
double distB = std::sqrt(std::pow(b.x - m_reference.x, 2)
+ std::pow(b.y - m_reference.y, 2));
return distA < distB;
}
};
std::set<Rectangle, CenterComparator> m_shapes;
Point m_reference;
};
CenterComparator isn't related to ShapeDisplay, it isn't aware of its members and it isn't derived from ShapeDisplay. You need to provide CenterComparator with its own reference Point. You then need to provide an instance of CenterComparator whose reference point is set.
Note that if you change that comparator's reference point in any way you will break std::set's sorting resulting in Undefined Behavior if you try to use it. So whenever setReference is called, you need to create a new set with a new comparator and copy over the old set.
Here is your code, adapted with these changes. I assumed you meant setReference and insertShape to be part of the public interface.
#include <cmath>
#include <set>
struct Point
{
int x;
int y;
};
struct Rectangle
{
int x;
int y;
int width;
int height;
};
class ShapeDisplay
{
public:
void insertShape(Rectangle rect)
{
m_shapes.insert(rect);
}
void setReference(Point reference)
{
m_reference = reference;
// Create a comparator using this new reference
auto comparator = CenterComparator{};
comparator.reference = m_reference;
// Create a new set
auto new_shapes = std::set<Rectangle, CenterComparator>(
std::begin(m_shapes), std::end(m_shapes), // Copy these shapes
comparator); // Use this comparator
m_shapes = std::move(new_shapes);
}
private:
struct CenterComparator
{
bool operator() (const Rectangle & a, const Rectangle & b) const
{
double distA = std::sqrt(std::pow(a.x - reference.x, 2)
+ std::pow(a.y - reference.y, 2));
double distB = std::sqrt(std::pow(b.x - reference.x, 2)
+ std::pow(b.y - reference.y, 2));
return distA < distB;
}
Point reference;
};
std::set<Rectangle, CenterComparator> m_shapes;
Point m_reference;
};
This is a follow up from an older question found here: Chaining Function Calls and user Mooing Duck provided me with an answer that works through the use of Proxy Class and Proxy functions. I have managed to template this class and it appears to be working. I'm getting completely different results between float and double...
Here are the non templated versions of the classes and application for floats and doubles:
Just replace all floats with doubles within the classes, functions, and proxy functions... The main program won't change except for the arguments.
#include <cmath>
#include <exception>
#include <iostream>
#include <utility>
namespace pipes {
const double PI = 4 * atan(1);
struct vec2 {
float x;
float y;
};
std::ostream& operator<<(std::ostream& out, vec2 v2) {
return out << v2.x << ',' << v2.y;
}
vec2 translate(vec2 in, float a) {
return vec2{ in.x + a, in.y + a };
}
vec2 rotate(vec2 in, float a) {
// convert a in degrees to radians:
a *= (float)(PI / 180.0);
return vec2{ in.x*cos(a) - in.y*sin(a),
in.x*sin(a) + in.y*cos(a) };
}
vec2 scale(vec2 in, float a) {
return vec2{ in.x*a, in.y*a };
}
// proxy class
template<class rhst, vec2(*f)(vec2, rhst)>
class vec2_op1 {
std::decay_t<rhst> rhs; // store the parameter until the call
public:
vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
vec2 operator()(vec2 lhs) { return f(lhs, std::forward<rhst>(rhs)); }
};
// proxy methods
vec2_op1<float, translate> translate(float a) { return { a }; }
vec2_op1<float, rotate> rotate(float a) { return { a }; }
vec2_op1<float, scale> scale(float a) { return { a }; }
// lhs is the object, rhs is the operation on the object
template<class rhst, vec2(*f)(vec2, rhst)>
vec2& operator|(vec2& lhs, vec2_op1<rhst, f>&& op) { return lhs = op(lhs); }
} // namespace pipes
int main() {
try {
pipes::vec2 a{ 1.0, 0.0 };
pipes::vec2 b = (a | pipes::rotate(90.0));
std::cout << b << '\n';
} catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
Output for float:
-4.37114e-08,1
Output for double:
6.12323e-17,1
Here is the templated version...
#include <cmath>
#include <exception>
#include <iostream>
#include <utility>
namespace pipes {
const double PI = 4 * atan(1);
template<typename Ty>
struct vec2_t {
Ty x;
Ty y;
};
template<typename Ty>
std::ostream& operator<<(std::ostream& out, vec2_t<Ty> v2) {
return out << v2.x << ',' << v2.y;
}
template<typename Ty>
vec2_t<Ty> translate(vec2_t<Ty> in, Ty a) {
return vec2_t<Ty>{ in.x + a, in.y + a };
}
template<typename Ty>
vec2_t<Ty> rotate(vec2_t<Ty> in, Ty a) {
// convert a in degrees to radians:
a *= (Ty)(PI / 180.0);
return vec2_t<Ty>{ in.x*cos(a) - in.y*sin(a),
in.x*sin(a) + in.y*cos(a) };
}
template<typename Ty>
vec2_t<Ty> scale(vec2_t<Ty> in, Ty a) {
return vec2_t<Ty>{ in.x*a, in.y*a };
}
// proxy class
template<class rhst, typename Ty, vec2_t<Ty>(*f)(vec2_t<Ty>, rhst)>
class vec2_op1 {
std::decay_t<rhst> rhs; // store the parameter until the call
public:
vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
vec2_t<Ty> operator()(vec2_t<Ty> lhs) { return f(lhs, std::forward<rhst>(rhs)); }
};
// proxy methods
template<typename Ty>
vec2_op1<Ty, Ty, translate<Ty>> translate(Ty a) { return { a }; }
template<typename Ty>
vec2_op1<Ty, Ty, rotate<Ty>> rotate(Ty a) { return { a }; }
template<typename Ty>
vec2_op1<Ty, Ty, scale<Ty>> scale(Ty a) { return { a }; }
// overloaded | operator for chaining function calls to vec2_t objects
// lhs is the object, rhs is the operation on the object
template<class rhst, typename Ty, vec2_t<Ty>(*f)(vec2_t<Ty>, rhst)>
vec2_t<Ty>& operator|(vec2_t<Ty>& lhs, vec2_op1<rhst, Ty, f>&& op) { return lhs = op(lhs); }
} // namespace pipes
// for double just instantiate with double...
int main() {
try {
pipes::vec2_t<float> a{ 1.0f, 0.0f };
pipes::vec2_t<float> b = (a | pipes::rotate(90.0f));
std::cout << b << '\n';
} catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
The output for floats:
-4.37114e-08,1
The output for doubles:
6.12323e-17,1
This goes to show that the conversion of my class to a class template appears to be working. I understand that there may be a bit of precision lost due to conversion from double to float or widening from float to double when casting, however, I can't seem to wrap my mind around why there is such a difference in output values from one to the other...
The rotation of the point or vector {1,0} at 90 degrees or PI/2 radians should be {0,1}. I understand how floating-point arithmetic works and that the generated output for the x values is relatively close to 0 so they should be considered 0 for all tense and purposes and I can include the use an epsilon checking function to test if it is close enough to 0 to set it directly to 0 which is not an issue...
What intrigues my curiosity is why is it -4.3...e-8 for float and +6.1...e-17 for double? In the float case, I'm getting negative values, and for the double case, I'm getting positive values. In both cases yes they are extremely small and close to 0 which is fine, but opposite signs, that has me scratching my head?
I'm seeking clarity to get a better insight as to why these values are being generated the way they are... Is it coming from the type-conversion or is it due to the trig function that is being used? Or a combination of both? Just trying to pinpoint where the divergence of signs is coming from...
I need to be aware of what is causing this subtle difference as it will pertain to my usage of this class and its generated outputs when precision is preferred over good enough estimations.
Edit
When working with the instantiation of these function templates, specifically for the rotate function and I started to test <int> type for my vector objects... I started to get some compiler errors... The translate and scale functions were fine, I only had an issue with the rotate function due to similar reasons loss of data, narrowing and widening conversions, etc...
I had to change my rotate function's implemenation to this:
template<typename Ty>
vec2_t<Ty> rotate(vec2_t<Ty> in, Ty a) {
// convert a in degrees to radians:
auto angle = (double)(a * (PI / 180.0));
return vec2_t<Ty>{ static_cast<Ty>( in.x*cos(angle) - in.y*sin(angle) ),
static_cast<Ty>( in.x*sin(angle) + in.y*cos(angle) )
};
}
Here I'm forcing the angle to always be a double regardless of the type Ty. The rotate function still expects the same type for its argument as the type of the vec2_t object that is being instantiated. The issue was with the initialization of the vec2_t object that was being created and returned from the calculations. I had to explicitly static_cast the x and y coordinates to Ty. Now when I try the same program above for vec2_t<int> passing in a rotation value of 90 I am getting exactly 0,1 for my output.
Another interesting fact by forcing the angle to always be double and always casting the calculated values back to Ty, when I instantiate my vec2_t as either a double or float I'm always getting the positive 6.123...e-17 result back for both cases... This should also allow me to simplify the design of the is_zero() function to test if these values are close enough to 0 to set them explicitly to 0.
TL;DR: Small numbers are close to zero whatever their sign. The numbers you got are "almost zero" given the circumstances.
I'd call this "sign obsession". Two very small numbers are similar even if their signs differ. Here you're looking at numbers at the edge of accuracy of the computations you performed. They are both equally "small", given their types. Other answer(s) give hints about where exactly is the clbuttic mistake :)
Your problem is in the line:
a *= (Ty)(PI / 180.0);
For the float case, this evaluates to 1.570796371
For the double case, this evaluates to 1.570796327
I would like to have an insight about whenever I should be using references or pointers.
Let's take the example of a Polygon class using a Rectangle class for its internal bounding box.
Polygon.h
class Polygon {
private:
std::list<Point> _points;
Rectangle _boundingBox;
public:
Polygon(const std::list<Point> &);
public:
const std::list<Point> &getPoints() const;
const Rectangle &getBoundingBox() const;
private:
void setBoundingBox();
};
Polygon.cpp
#include <iostream>
#include "Polygon.h"
Polygon::Polygon(const std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1) {
throw std::invalid_argument("A polygon is composed of at least 3 sides.");
}
if (points.front() != points.back()) {
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");
}
std::list<Point>::const_iterator it;
for (it = ++points.begin(); it != points.end(); ++it) {
this->_points.push_back(*it);
}
this->setBoundingBox();
}
void Polygon::translate(const std::array<float, 2> &vector)
{
std::list<Point>::iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it) {
(*it).setX((*it).getX() + vector[0]);
(*it).setY((*it).getY() + vector[1]);
}
Point topLeft = this->_boundingBox->getTopLeft();
Point bottomRight = this->_boundingBox->getBottomRight();
topLeft.setX(topLeft.getX() + vector[0]);
topLeft.setY(topLeft.getY() + vector[1]);
bottomRight.setX(bottomRight.getX() + vector[0]);
bottomRight.setY(bottomRight.getY() + vector[1]);
}
const std::list<Point> &Polygon::getPoints() const
{
return this->_points;
}
const Rectangle &Polygon::getBoundingBox() const
{
return this->_boundingBox;
}
void Polygon::setBoundingBox()
{
float xMin = this->_points.front().getX();
float xMax = this->_points.front().getX();
float yMin = this->_points.front().getY();
float yMax = this->_points.front().getY();
std::list<Point>::const_iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it)
{
Point point = *it;
if (point.getX() < xMin) {
xMin = point.getX();
}
if (point.getX() > xMax) {
xMax = point.getX();
}
if (point.getY() < yMin) {
yMin = point.getY();
}
if (point.getY() > yMax) {
yMax = point.getY();
}
}
this->_boundingBox = new Rectangle(Point(xMin, yMin), Point(xMax, yMax));
}
std::ostream &operator<<(std::ostream &out, const Polygon &polygon)
{
std::list<Point>::const_iterator it;
for (it = polygon.getPoints().begin(); it != polygon.getPoints().end(); ++it) {
out << (*it);
if (it != polygon.getPoints().end()) {
out << " ";
}
}
return out;
}
Rectangle.h
#pragma once
#include <stdexcept>
#include "Point.h"
class Rectangle {
private:
Point _topLeft;
Point _bottomRight;
public:
Rectangle(const Point &, const Point &);
public:
const Point &getTopLeft() const;
const Point &getBottomRight() const;
float getWidth() const;
float getHeight() const;
};
Rectangle.cpp
#include "Rectangle.h"
Rectangle::Rectangle(const Point &topLeft, const Point &bottomRight)
{
if (topLeft.getX() > bottomRight.getX() || topLeft.getY() > bottomRight.getY()) {
throw std::invalid_argument("You must specify valid top-left/bottom-right points");
}
this->_topLeft = topLeft;
this->_bottomRight = bottomRight;
}
const Point &Rectangle::getTopLeft() const
{
return this->_topLeft;
}
const Point &Rectangle::getBottomRight() const
{
return this->_bottomRight;
}
float Rectangle::getWidth() const
{
return this->_bottomRight.getX() - this->_topLeft.getX();
}
float Rectangle::getHeight() const
{
return this->_bottomRight.getY() - this->_topLeft.getY();
}
Point.h
#pragma once
#include <ostream>
#include <cmath>
class Point {
private:
float _x;
float _y;
public:
Point(float = 0, float = 0);
public:
float distance(const Point &);
public:
float getX() const;
float getY() const;
void setX(float);
void setY(float);
};
std::ostream &operator<<(std::ostream &, const Point &);
bool operator==(const Point &, const Point &);
bool operator!=(const Point &, const Point &);
Point.cpp
#include "Point.h"
Point::Point(float x, float y)
{
this->_x = x;
this->_y = y;
}
float Point::distance(const Point &other)
{
return std::sqrt(std::pow(this->_x - other.getX(), 2) + std::pow(this->_y - other.getY(), 2));
}
float Point::getX() const
{
return this->_x;
}
float Point::getY() const
{
return this->_y;
}
void Point::setX(float x)
{
this->_x = x;
}
void Point::setY(float y)
{
this->_y = y;
}
std::ostream &operator<<(std::ostream &out, const Point &point)
{
out << "(" << point.getX() << ", " << point.getY() << ")";
return out;
}
bool operator==(const Point &p1, const Point &p2)
{
return p1.getX() == p2.getX() && p1.getY() == p2.getY();
}
bool operator!=(const Point &p1, const Point &p2)
{
return p1.getX() != p2.getX() || p1.getY() != p2.getY();
}
A lot of questions come with this snippet of code.
This does not compile because obviously whenever we try to create a Polygon, it ends up trying to create a Rectangle with a default constructor which does not exist.
I can't use initializer list because obviously the bounding box depends on some computed values from my list of points.
I could create a default constructor creating two Point(0, 0) by default for the Rectangle but this does not make much sense.
I could use pointers but then I feel this is not the nicest solution as I tend to think this is mostly used for polymorphism in C++ we should prefer reference whenever possible.
How should I then proceed ?
I feel I am missing something out about C++ and could learn a lot from this.
I think your main question is about how to deal with the problem of needing to initialize both std::list<Point> _points; and Rectangle _boundingBox;, while also doing some validation of _points.
The simplest solution is to just give Rectangle a default constructor (or pass two default Points as initializer). Then once you have validated the points argument in the constructor, you calculate the Rectangle based on the points.
A slightly more complicated alternative is to allow the validation function to be invoked from the ctor-initializer list, e.g.:
Polygon::Polygon(std::list<Point> points)
: _points( validate_point_list(points), std::move(points) ), _boundingBox( calculateBoundingBox(_points) )
{
}
where you have functions (which could be free functions):
void validate_point_list(std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1)
throw std::invalid_argument("A polygon is composed of at least 3 sides.");
if (points.front() != points.back())
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");
// caller must pass in same first and last point, but we only store one of the two
points.erase( points.begin() );
}
and
Rectangle calculateBoundingBox(std::list<Point> const &_points)
{
// whatever logic you have in setBoundingBox, except return the answer
}
Note that the loop in your Polygon constructor is unnecessarily complicated. You could have just written _points = points; and then erased the extra point (which is O(1) for lists).
Note that I have passed by value and then used std::move. The reason is that if the argument given is a rvalue then it can just be moved right on through into where it's being stored; whereas with the const & version, a copy is stored and then the original is destructed.
I would use const & a lot less than you did. Small objects , such as Point and Rectangle, don't suffer a performance penalty from pass-by-value (and might even be more efficient that way). And as mentioned in the previous paragraph; if your function accepts a parameter and it is going to take a copy of that parameter, it's better to pass by value .
Passing by reference is best only when you are using but not storing the values passed. For example, calculateBoundingBox.
Finally, once you get this working, you might want to think about having the Polygon constructor accept an iterator pair of points range, and/or a std::initializer_list.
I would defined a default constructor for Rectangle class as private and I would make Polygon class a friend of Rectangle class:
class Rectangle {
friend class Polygon;
Point _topLeft;
Point _bottomRight;
Rectangle(); // accessible only to friends
public:
Rectangle(Point const&, Point const&);
...
};
And then in setBoundingBox():
void Polygon::setBoundingBox() {
...
_boundingBox._topLeft = Point(xMin, yMin);
_boundingBox._bottomRight = Point(xMax, yMax);
}
Thus, I wouldn't expose the default constructor of Rectangle and at the same time I would have a concrete object which is more efficient in terms of cache performance.
I feel as though you should have a separate class called BoundingBox that
1) Takes a collection of points in its constructor
2) Is inherited from Rectangle
Meanwhile, Rectangle should have a state, along the lines of NOT_A_RECTANGLE or it could throw an exception. Just be sure you clean up when throwing exceptions from a constructor.
Then you would construct the bounding box as part of the construction of the polygon and you can verify that a bounding box is possible as part of your error checking. (probably rather than 3 sides check, but I am no geometry expert)
BoundingBox would remain a member of Polygon.
This would be more RTTI.
It occurs to me though, that if you translate or rotate the polygon, you've also go to translate or rotate the bounding box. You might want to consider making the list of points its own object and sharing them. This would be a more advanced topic. You can for now get away with just recalculating the bounding box on operations performed upon the polygon.
As to whether to use a reference, a pointer, or pass by value, I don't know that there is a black and white list of things to consider for this, but a few are:
Is the object large enough to even worry about it? A rectangle is 4 floats?
Are there interfaces or base classes you will need to cast to, rather than always using the class itself? If so, you've got no choice but to use a pointer of some sort. The pointer could be unique, shared, weak, etc. depending on the situation. You have to ask yourself who owns it, whats the life time, and are there circular references?
Most people will probably use a reference whenever possible rather than a pointer, but only when passing by value doesn't qualify.
IMO, since you are just "GetBoundingBox", I think it would be simple and more maintainable to just return a copy of the bounding box by value rather than some const reference and definitely more than a pointer.
One solution would be to write a programmatic constructor for Rectangle that takes as its argument a const std::list<Point>&. It could traverse the list once, computing the maximum and minimum x and y. Then, your Polygon constructor would become:
Polygon::Polygon(const std::list<Point> &points)
: _points(points),
: _boundingBox(points)
{
// ...
}
An alternative is to move the code to find the bounding box from a list of points to a helper function, then define a move constructor Rectangle::Rectangle( Rectangle&& x ). In that case, your Polygon constructor would be:
Polygon::Polygon(const std::list<Point> &points)
: _points(points),
: _boundingBox( findBoundingBox(points) )
{
// ...
}
Either way, you could update a bounding box with assignment, so you might want an assignment operator like Rectangle& Rectangle::operator= ( Rectangle&& x ) to make that more efficient. You can skip the Rectangle&& versions if a Rectangle is just Plain Old Data. But if you do this a lot, you might overload Rectangle& findBoundingBox( const std::list<Point>& src, Rectangle& dest ) to update in place with no copying.
On a minor side note, I’d discourage you from using identifiers that begin with underscores, since those names are reserved in the global namespace in C++, and your libraries might declare something named _point.
I work in robotics, which means I use a large number of open-source projects dealing with 3D geometry. Since the classes and math tend to be fairly simple, everyone seems to implement their own version of Vector3D, Quaternion, etc., each with slight variations, e.g. vec.x, vec.X, vec.x(). So within one project, one might need to convert between Eigen, ROS, Assimp, Bullet, and other versions of the same basic classes. Is there an easy or elegant way to do this in C++ that doesn't require an n^2 mapping from every library to every other library?
Similar to: This SO question, but I can't edit any of the source libraries.
Example:
namespace a
{
class Vector
{
public:
double x, y, z;
};
} // namespace a
namespace b
{
class Vector
{
public:
double X, Y, Z;
};
} // namespace b
namespace c
{
class Vector
{
public:
double& x() { return mx; }
double& y() { return my; }
double& z() { return mz; }
private:
double mx, my, mz;
};
} // namespace c
int main()
{
a::Vector va;
b::Vector vb;
c::Vector vc = va + vb; // Ideal, but probably unrealistic goal
return 0;
}
EDIT:
If there are ~10 different geometry libraries, a particular project may only use 2-4 of them, so I'd like to avoid introducing a dependency on all the unused libraries. I was hoping for something like static_cast<b::Vec>(a::Vec), or maybe
c::Vec vc = my_cvt<c::Vec>(vb + my_cvt<b::Vec>(va));
but my understanding of templates and type_traits is pretty weak.
If you write three helper functions for each vector type to access X, Y and Z:
double X(const a::Vector& v) { return v.x; }
double Y(const a::Vector& v) { return v.y; }
double Z(const a::Vector& v) { return v.z; }
double X(const c::Vector& v) { return v.x(); }
double Y(const c::Vector& v) { return v.y(); }
//...
then you can easily write template functions that work with any type. e.g:
template<typename V1, typename V2>
V1 operator+(const V1& v1, const V2& v2) {
return {X(v1)+X(v2), Y(v1)+Y(v2), Z(v1)+Z(v2)};
}
template<typename V1, typename V2>
V1 convert(const V2& v) {
return {X(v), Y(v), Z(v)};
}
int main() {
a::Vector va;
b::Vector vb;
auto vc = convert<c::Vector>(va + vb);
}
Live demo.
Well, just define a operator+ function and your 'unrealistic goals' would be achieved:
c::Vector operator+(const a::Vector& a, const b::Vector& b) {
return {a.x+b.X, a.y+b.Y, a.z+b.Z};
}
And your small code snippet will work.
EDIT
If you do not want to define a hell lot of function, and assuming you can't change the Vector version from a and b, modifiy your vector class by adding these constructors:
Vector(a::Vector a) : mx(a.x), my(a.y), mz(a.z) {}
Vector(b::Vector b) : mx(b.X), my(b.Y), mz(b.Z) {}
And then define only one operator dealing only with the c class:
c::Vector operator+(c::Vector a, c::Vector b) {
return {a.x()+b.x(), a.y()+b.y(), a.z()+b.z()};
}
And your code snippet will work with declaring thousands of operator
EDIT 2
If you want your type to be compatible with your library's types you may add conversion operator to your struct, example, if you want your type to be convertible with Vector a, add this function inside your class:
operator a::Vector() const {
// return a a::Vector from our c::Vector
return a::Vector{mx, my, mz};
}
I see that this is an old question but check out Boost QVM.
I want to sort points_vec vector as shown in the pseudocode below. I want to sort this vector, by a coordinate value like x or y or z
class A{
std:vector<double*> points_vec;
void doSomething();
}
Then, in method A::doSomething, I want sort this vector:
void A::doSomething() {
std::sort(points_vec.begin(), points_vec.end(), sortPoints());
}
Can someone please show me syntax for the sortPoints() method.. Preferably I want it to be a method of class A. this post creates a struct to do this, not sure if I should create a similar struct within the class. Is there another way to handle this?
thanks
The simplest way is to provide a functor which is used by the sort algorithm to compare two values. You can write like this:
struct Compare
{
bool operator()(double* first, double* second) const
{
//Compare points here
}
};
And use like:
std::sort(p.begin(), p.end(), Compare());
EDIT for comment by OP: Yes, this sample code compiles fine:
class A
{
public:
struct c
{
bool operator()(int a, int b) const
{
return a < b;
}
};
};
int main()
{
std::vector<int> a1;
a1.push_back(2);
a1.push_back(1);
std::sort(a1.begin(), a1.end(), A::c());
return 0;
}
You have two options for sorting: either pass a function/functor to sort or define the operator< for your class. Now, your class A seems to be more of a wrapper for a set of coordinates. So, create another class for your co-ordinates.
struct Point {
double x_, y_, z_;
Point(double x, double y, double z) : x_(x), y_(y), z_(z) {}
// just an example, you can refine the following as much as you need
bool operator<(Point const& other) {
return x < other.x;
}
};
bool sortOnY(Point const& l, Point const& r) const {
return l.y < r.y;
}
class A {
std::vector<Point> pts_;
void doSomething() {
sort(pts_.begin(), pts_.end());
}
// if sorting on y is also required, you will need
// to use a custom comparator which can be either
// a functor or a function
void doSomeOtherThing() {
sort(pts_.begin(), pts_.end(), sortOnY);
}
};
First of all - what you have will break all your points - as you'll sort by single doubles not by "points consisting of 3 doubles".
The best way to do this I think is:
Store the points as some Point3D class not a couple doubles
Define the less then operator for Point3D
Just call std::sort(points_vec.begin(), points_vec.end() );
If you'd want to sort them by in different ways that's when you'd use the sort functor and create different functors with operators() for different purposes.
I don't think this thread would be complete without a mention of Boost.Bind:
struct Point3D {
double x, y;
Point3D(double x=0., double y=0.) : x(x), y(y) {
}
};
int main() {
std::vector<Point3D> points;
points.push_back(Point3D(-1., 2.));
points.push_back(Point3D( 2., -1.));
points.push_back(Point3D(-2., 0.));
using boost::bind;
std::sort(points.begin(), points.end(),
bind(&Point3D::x, _1) < bind(&Point3D::x, _2));
// points sorted by x coord
std::sort(points.begin(), points.end(),
bind(&Point3D::y, _1) < bind(&Point3D::y, _2));
// points sorted by y coord
}
What a shame std::tr1::bind does not support that. But of course, with a C++0x compiler you'll be able to do this:
std::sort(points.begin(), points.end(),
[](Point3D const & a, Point3D const & b) { return a.x < b.x; });
If you want to sort by x or y or z, those are three different functionalities. Which coordinate to sort by is extra information which doesn't really come from std::sort. You need have an object to pass it on.
struct coord_comparison {
int coord_id; // <= critical information
bool operator()( double (*l)[3], double (*r)[3] ) {
return (*l)[ coord_id ] < (*r)[ coord_id ];
}
coord_comparison( int id ) { coord_id = id; }
};
Create this struct inside your class or outside, but it needs to be a structure and not a free function, and operator() cannot be static. Call:
std::sort(points_vec.begin(), points_vec.end(), compare_points( 1 /*for y*/) );
Sorting by all 3 coords at once:
You have
std:vector<double*> points_vec;
I'm going to presume that the double* points to an array of 3 coordinates. This is cleaner:
std:vector<double(*)[3]> points_vec;
std::sort's third argument is a functor which compares two sequence objects:
bool compare_coords( double(*l)[3], double(*r)[3] ) {
Fortunately, comparing two sequences is already coded for you by std::less:
return std::less( *l, *l + ( sizeof *l/sizeof **l ), r );
(perhaps I did more work than necessary to get the size of the array)
return std::less( *l, *l + 3, r );
}
This function may be useful outside the class, so I'd make it a free function. You need to make it static if it's going to stay inside the class.
Finally, leave off the parens when passing the function to std::sort:
std::sort(points_vec.begin(), points_vec.end(), compare_points );