Functions with many parameters - making them as structs - c++

I'm writing an API for internal needs and so ease of use is one of the top priorities. I wonder if I'm pushing it too far as in the following example.
The function to solve the inverse geodetic problem takes in geocoordinates of two points and calculates distance between them and asimuths (angles) from each of the points to the other.
Ease-of-use-optimised conventional solution might look something like (please don't mind the length of the names, I know there's room for improvement):
class Angle;
class GeoCoordinate;
...
struct InverseGeodeticOut
{
Angle forwardAzimuth;
Angle backAzimuth;
double distance;
};
InverseGeodeticOut inverseGeodetic(const GeoCoordinate &firstPoint,
const GeoCoordinate &secondPoint);
// or
void inverseGeodetic(const GeoCoordinate &firstPoint,
const GeoCoordinate &secondPoint,
InverseGeodeticOut *result);
My question is how reasonable would it be to take it one step further to save user some typing:
class Angle;
class GeoCoordinate;
...
struct InverseGeodetic
{
InverseGeodetic();
InverseGeodetic(const GeoCoordinate &firstPoint,
const GeoCoordinate &secondPoint);
Angle forwardAzimuth;
Angle backAzimuth;
double distance;
};
// so the usages would be
InverseGeodeticOut inverse = inverseGeodetic(firstPoint, secondPoint);
InverseGeodeticOut inverse;
inverseGeodetic(firstPoint, secondPoint, &inverse);
InverseGeodetic inverse(firstPoint, secondPoint);
Perhaps in this particular example the difference is too small to be worth talking about but I wonder if such constructs are okay in general.

I like your 2nd code example, though I find the public constructor a bit confusing. Especially if there are other ways to construct an InverseGeodetic. I'd rather use a static factory method to construct it. That way you can give a more meaningful name to the method:
struct InverseGeodetic
{
Angle forwardAzimuth;
Angle backAzimuth;
double distance;
static InverseGeodetic FromTwoPoints(const GeoCoordinate &, const GeoCoordinate &);
};
// usage
auto inverse = InverseGeodetic::FromTwoPoints(a, b);
But then that might be my C# background bleeding through.

Related

Inheritance and friend functions, accessing protected members from base class

Let say that I have a big class Circle with a lot of members and functions. To proceed a large amount of data I decided to create class PotentialCirlce (with only 3 members - x, y, r), do most of preprocessing based on PotentialCirlce and in the last stage create objects Circle.
a) is it correct approach? do It influence on performance or rather should I use only Circle.
It seems to me that I can use inheritance:
class potentialCircle {
protected:
point_t center;
unsigned int radius;
public:
potentialCircle(int a, int b, unsigned int r) : center{ point_t(a,b) }, radius{ r } {}
potentialCircle() = delete;
potentialCircle(const potentialCircle&) = default;
potentialCircle(potentialCircle&&) = default;
potentialCircle& operator=(const potentialCircle&) = default;
potentialCircle& operator=(potentialCircle&&) = default;
virtual ~potentialCircle() = default;
};
class Circle : public potentialCircle {
// members detected based on Hough Circle Transform
//point_t center; // coordinates of center point
point_t alternative_center; // needed when center is out of frame
//unsigned int radius; // radius
// members calculated based on Flood Fill algorithm (more realistic)
unsigned int area = 0;
float diameter = 0;
float perimeter = 0;
....
};
b) where should I put method which needs to compare two difference objects? one object of type Circle and one of PotentialCirle?
currently, I have defined below function as part of Circle
bool Circle::is_greater(const std::pair<potentialCircle, int>& point_pair) const;
but I don't have access to protected data members of potentialCircle, although Circle is inheriting from potentialCircle.
Maybe I should defined is_greater() as part of namepsace and make it a friend to Circle and potentialCircle.
Do you have better idea?
There are not really a good approach to compare objects of different types as it make little sense in practice. What would be the purpose of such comparisons.
Now even if you have a single class, if the ordering is not intransic to the type, it would be better to use an external class for sorting.
class CircleDiameterLess
{
public:
bool operator()(const Circle &lhs, const Circle &rhs)
{
return lhs.diameter < rhs.diameter;
}
};
That way, you can have multiple ways to sort data and it play nice with STL.
Another problem with your code if that it make little sense to have a class circle with a diameter that derives from a class potentialCircle with a radius. Your code will be hard to maintain because it is hard to understand.
You want to store either the diameter or the radius and compute the other one.
unsigned int get_diameter() const { return radius * 2; }
Member like alternative_center make no sense. A circle has only one center. If your class does not respect basic expectations, it will make the code hard to maintain as nobody would known that a circle has 2 centers including you in 3 months!
In a case like yours, it make make sense to add public accessors.
class potentialCircle
{
public:
unsigned int get_radius() const { return radius; }
....
};
That way, you can still make data private (or sometime protected) while having read only access to it. That way, you can write you comparison function as you wish. And in practice, if you have a class that represent a circle, you usually want at least being able to get basic properties like radius, aread, bounding rectangle by the way of a function.
Another thing is that public derivation as your (from potentialCircle) would only make senses if you have other classes that derives from it. However, if this is the case, then how would you compare the other kind of circles?
Notes:
With C++ 20, three way comparison would be even better.

How can my class be provided some information about its environment?

Here's a code sample I wrote with encapsulation and composition in mind:
class Bullet {
private:
Vector2 position;
Vector2 speed;
public:
void move(float time_delta) {
position += speed * time_delta;
}
};
Basically, there's just a projectile moving in nowhere. However, a bullet can actually e. g. ricochet off a wall, having its speed changed significantly. Is there a good way of considering such interactions? I neither want my Bullet to know about "higher-rank" classes (which are supposed to use it themselves) nor write a single-use solution like this one:
template<typename F> void move(float time_delta, F collision_checker);
UPDATE: worth reading if you want this question narrowed. Here's a simplified example of the wished logic for moving Bullets (I don't exactly mean the Bullet::move() member function!) and their interactions with other entities:
Vector2 destination = bullet.position + bullet.speed * time_delta;
if (std::optional<Creature> target = get_first_creature(bullet.position, destination)) {
// decrease Bullet::speed depending on the target (and calculate the damage)
} else if (std::optional<Wall> obstacle = get_first_wall(bullet.position, destination)) {
// calculate the ricochet changing Bullet::position and Bullet::speed
}
All pieces of code represented by comments are supposed to use some properties of the Creature and Wall classes.
From a design point of view, it is probably best if your bullet doesn't know how to detect when it's ... passing_through an obstacle (scnr). So it might be better to turn your Bullet class in to a struct, i.e. have it behave like a thing that is acted upon instead of a thing that acts.
You can still add your convenience function but have it be non-mutating:
struct Bullet {
Vector2 position;
Vector2 speed;
Vector2 move(float time_delta) const {
return position + speed * time_delta;
}
};
This way you can compute the collisions from the calling scope:
auto dest = bullet.move(dt);
while (std::optional<Collision> const col = detectCollision(bullet.position,dest)) {
bullet.position = col->intersectPoint;
bullet.speed = col->reflectedSpeed;
dest = col->reflectDest;
}
bullet.position = dest;
Here detectCollision checks whether the line from the bullet's current position to the new position dest intersects with any obstacle and computes the parameters of the reflection. Effectively you zig-zag your way to the destination that will result from all successive ping-pongs of the bullet with potential obstacles.

CGAL - Proper way of adding data to Triangle class

I have begun using CGAL and encountered an issue I am not able to solve. I load a model as a list of triangles and want to store additional information for each triangle - a color. I also want to use the AABB tree from CGAL but I have noticed that I have basically two options:
class DataTriangle {
public:
Point_3 x;
Point_3 y;
Point_3 z;
int color;
}
Follow the "AABB_custom_example.cpp" example and create a functor that converts my DataTriangle to a CGAL::Simple_cartesian::Triangle_3 every time. This wastes computation time because it constructs new Triangle_3 object every time.
I can make my own triangle and include Triangle_3 as a member:
class MyTriangle {
Triangle mTriangleCgal;
int color;
const Triangle& getTri() const { return mTriangleCgal; }
}
Then again declare a My_triangle_primitive functor, define
typedef K::Point_3 Point;
typedef K::Triangle_3 Datum;
typedef const K::Triangle_3& Datum_reference;
and reference the underlying triangle as such.
Datum_reference datum() { return tri->getTri(); }
Is this solution okay? I feel it should not waste time by constructing any new objects. Am I right?
The third idea I came up with is simply creating a custom iterator that will go through the vector of MyTriangles and instead of the class itself, return the Triangle_3 class. I should be able to pass this iterator into the AABB tree constructor and get the tree constructed from plain Triangle_3 objects, which should work just fine.
Is there another way of approach that I missed that is prefered to these three? Is number 2 okay to use if there is not another way?
Thank you

C++ function with different return types

Let's say I want to write a function that takes a plane and a line in R^3 and returns their intersection. Obviously, I have to distinguish between three possible cases: i) the line intersects the plane in a single point, ii) the line is a subset of the plane, iii) the line is parallel to the plane (and not a subset of it). This results in three possible return types for the function.
I've been working a lot with OCaml recently, which would allow me to distinguish between these different types very explicitly by returning a variant type from my function. How do people deal with this kind of issue in C++?
One idea that comes to mind is to use a tuple of {bool, bool, vector} as my return type, where the first boolean says whether the line and the plane have a non-empty intersection, the second boolean says whether they intersect in a single point if the first boolean is true (and is meaningless otherwise), and the vector returns the unique intersection if both booleans are true (and is meaningless otherwise). However, this feels very inelegant and hacky, I have to inform users of the function of the meaning of the tuple entries using comments, I return variables which can be meaningless, etc.
What is the best way to deal with this problem?
Here are several generic (i.e. not limited to geometrical lines and points) ways to cope with the problem.
std::variant (or its older sibling boost::variant for those who cannot run C++17).
Plain old union (tagged):
struct LinePlaneIntersection {
enum { IsLine, IsPlane } intersection_type;
union {
Point p;
Line l;
};
};
If Point and Line have not-trivial constructors and/or destructors, you'd need to add ctors and dtors to the above scheme.
Plain old inheritance.
class LinePlaneIntersection { ... };
class NoIntersection : public LinePlaneIntersection { ... };
class OnePointIntersection : public LinePlaneIntersection { ... };
class OneLineIntersection : public LinePlaneIntersection { ... };
Return a LinePlaneIntersection* (or better and much preferable std::unique_ptr<LinePlaneIntersection>) from your function. Then there's of course the problem of what to do with the returned value. You may want to use the Visitor pattern here.
Continuation passing. Don't return anything, accept a continuation instead. In this case, three continuations:
void intersect (Line line, Plane plane,
std::function<void(Line)> onLine,
std::function<void(Point)> onPoint,
std::function<void()> onNothing);
I would do something like:
struct Point3D
{
double x;
double y;
double z;
};
struct Line
{
Point3D p1;
Point3D p2;
};
struct Plan {
Point3D p;
Point3D orthogonalDir;
};
std::optional<std::variant<Point3D, Line>>
ComputeIntersection(const Line& line, const Plan& plan);
Although there are nice new shiny ways of dealing with this (std::tuple, std::variant, &c.), the tried-and-tested way is to design a class (or even a set of related classes) that can represent the various states and return an instance of that.
It's this approach that always seems to scale up best as your project evolves. So much so, that the committee behind Java has never emitted a tuple or a variant type into their language and libraries.
Why not return a struct with an enum type? Someone using the function could then first check the type of intersection before attempting to use the data.
enum IntersectType {
INTERSECTION_NONE,
INTERSECTION_POINT,
INTERSECTION_LINE,
};
struct Point3D {
double x;
double y;
double z;
}
struct LinePlaneIntersect {
IntersectType type;
std::vector<Point3D> intersect; //since you mentioned vector
};
//Check within another function
struct LinePlaneIntersect intersection = fun(line, plane);
if (intersection.type == INTERSECTION_POINT) {
// do something
}

Calling constructors of certain classes based upon a common parameter c++

I'm currently working on class inheritance/polymorphism in my classes and I can't figure out this problem. Ok here it is: Let's say I have 2 mock classes, and I let the user choose a value that corresponds to the last parameter in the constructor:
class Planets {
private:
int x,y,z;
string a;
public:
Planets(string name, int diameter, int mass, int planet_kind) : a(name), x(diameter), y(mass), z(planet_kind) { }
Planets() { a="", x=0, y=0, z=0; }
//get and set functions to manipulate data
virtual void planet_creation(Planets& p1);
//function I want to modify depending on the planet
}
The thing to pay attention to is the planet_kind variable. I want the parent class to be a baseline for the others for example, gas giants would be 2, planets with life would be 1, etc... They would all have their own classes and constructors. For example in another class:
class Gas_giant : public Planets {
private:
int x,y,z;
string a;
public:
Gas_giant(string name, int diameter, int mass, int planet_kind) : a(name), x(diameter), y(mass), z(planet_kind) { }
Gas_giant() { a="Gas", x=0, y=0, z=2; }
//get and set functions to manipulate data
void planet_creation(Gas_giant& g);
//function I want to modify depending on the planet
//example: gas giants would be larger when created,have no solid mass,high gravity
}
basically I want the user to be able to input the kind of planet and the name, then depending on the kind they choose, call certain types of planets to be randomly generated in different ways. The functions aren't the problem, the issue I'm having is getting my program to choose different constructors based upon a parameter in a base constructor.
I don't want my program to create any "Type 0 " planets, it's just a class I'm trying to derive all the rest from.
Thanks in advance, and sorry if this is a dumb question.
There are languages in which a constructor can return a derived type, but C++ is not one of them. In C++ a constructor always constructs precisely its own type.
Anyway, it's not obvious that using a "planet_kind" integer is superior to using a different constructor. The different constructor option is likely to be quite a bit more readable:
Planet* henry = new GasGiant("Henry", ...);
Planet* bob = new Asteroid("Bob", ...);
...
If you really needed to use an enum for some reason (such as reading the data from a file), then you'll need a case statement:
for (;;) {
// read data into name, size, mass, kind
planets.emplace_back( make_planet(name, size, mass, kind) );
}
...
Planet* make_planet(std::string name, double radius, double mass, enum PlanetKind kind) {
switch (kind) {
case GIANT : return new GasGiant(name, radius, mass);
case ASTEROID: return new Asteroid(name, radius, mass);
// ...
default: // write an error message
}
}