Home > Net >  Handling multiple data representation for modifying data in vectors in C
Handling multiple data representation for modifying data in vectors in C

Time:08-31

I have the simple data structure describing 2d point in cartesian coordinate system, like below.

struct CartPoint
{
    double x;
    double y;
}

and second strucutre, representing 2d point in polar coordinate system

struct PolarPoint
{
    double r;
    double alpha;
}

and also two functions allowing me to translate from one representation to second one:

void translate(const CartPoint& from, PolarPoint& to) { ... };
void translate(const PolarPoint& from, CartPoint& to) { ... };

I would like to create object (let me call it PointContainer), that allows me to store cartesian 2D points in one vector, but accessing those in either cartesian or polar representation (based on compile-time decision). I was thinking of a class exposing two types of non-const iterators, one for every representation. However I could not find anywhere such a solution and i am not sure whether it is a good idea. I would like to use it like that:

void fillVectorWithCartPts(std::vector<CartPoint>& points)
{
    // fills points-vector with 2d cartesian points
    ...
};

int main()
{
    std::vector<CartPoint> pts{};
    fillVectorWithCartPts(pts);

    PointContainer pc{pts};
    // dummy logic representing use possibilities
    for (CartPoint& _pt : pc.GetIterator<CartPoint>())
    {
        _pt = CartPoint{1.0, 2.0} // modifies points in data via cartesian representation
    }
    // or
    for (PolarPoint& _pt : pc.GetIterator<PolarPoint>())
    {
        _pt = PolarPoint{3.0, 4.0} // modifies points in data via polar representation
    }
    // after modification i can retrive vector in selected representation
    std::vector<PolarPoint> polarRes = pc.Retrive<PolarPoint>();
    std::vector<CartPoint> cartRes = pc.Retrive<CartPoint>();

    return 0;
}

I will be very grateful for any suggestion on design of such a class or proposing other solutions to solve the issue of double representation need for the same data.

CodePudding user response:

It's likely the most idiomatic to create an intermediary class that can be viewed as either CartPoint or PolarPoint. You can do this multiple ways: containment and getters, or inheritance. Then you can store a vector of these points.

CartPoint toCartPoint(const PolarPoint& pp) { ... }
CartPoint toPolarPoint(const CartPoint& cp) { ... }

class GenericPoint {
public:
    GenericPoint(CartPoint  cp)
    : cp(cp), pp(toPolarPoint(cp)) {}

    GenericPoint(PolarPoint pp)
    : cp(toCartPoint(pp)), pp(pp) {}

    GenericPoint(const GenericPoint& gp)
    : cp(gp.cp), pp(gp.pp) {}

    operator const  CartPoint&() const { return cp; }
    operator const PolarPoint&() const { return pp; }

    const  CartPoint& getCartPoint()  const { return cp; }
    const PolarPoint& getPolarPoint() const { return pp; }

    GenericPoint& operator=(const GenericPoint& rhs)
    {
        cp = rhs.cp;
        pp = rhs.pp;
    }

    GenericPoint& operator=(const CartPoint& cp_in)
    {
        cp = cp_in;
        pp = toPolarPoint(cp_in);
    }

    GenericPoint& operator=(const PolarPoint& pp_in)
    {
        cp = toCartPoint(pp_in);
        pp = pp_in;
    }

private:
    CartPoint  cp;
    PolarPoint pp; 
};

CodePudding user response:

I suggest selecting one of them and only convert to the other when absolutely needed. If you select to store CartPoints internally, you could convert back and forth beween CartPoints and PolarPoints if PolarPoints are needed for some calculations. You'd store std::vector<CartPoint> in your container though.

Just keep in mind that floating point math is tricky and you may not get the same result if you do calculations in the PolarPoint domain and convert to CartPoint as if you did the calculations in the CartPoint domain.

It could nevertheless look something like this:

struct CartPoint {
    double x = 0.;
    double y = 0.;

    CartPoint() = default;
    
    CartPoint(double X, double Y) : x{X}, y{Y} {}

    // convert a PolarPoint to a CartPoint
    CartPoint(const PolarPoint& pp) :
        CartPoint(pp.r * std::cos(pp.alpha),
                  pp.r * std::sin(pp.alpha))
    {}

    // comparisons
    auto operator<=>(const CartPoint& rhs) const = default;

    // return a Length proxy object in case the length is only used for comparisons
    Length length() const { return {x*x   y*y}; }

    // convert to a PolarPoint if needed by some function
    operator PolarPoint () const {
        return {std::sqrt(x*x y*y), std::atan2(y, x)};
    }
};

The Length proxy object is there to save us from calls to std::sqrt when we only want to compare the lengths of two CartPoints:

struct Length {
    double len_squared;
    
    // don't use sqrt if only comparing lengths
    auto operator<=>(const Length& rhs) const {
        return len_squared <=> rhs.len_squared;
    }

    // comparisons with actual doubles
    auto operator<=>(double l) const {
        return len_squared <=> l*l; // square the supplied value
    }

    // convert to double only when the actual length is needed
    operator double() const { return std::sqrt(len_squared); }
};

Demo

  • Related