Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing array elements by name

Tags:

c++

I'm writing code that passes around a lot of multi-parameter variables. For example, I might pass an "orientation," which is six doubles (three Cartesian coordinates and three rotations about the axes). It would be reasonable to define an Orientation struct and use that as an argument type. However, due to API limitations, these parameters must be stored as, and often passed to functions as, pointers to arrays of parameters:

// The sane version, using a struct
double distance_from_origin(const Orientation & o) {
  return sqrt(o.x * o.x + o.y * o.y + o.z * o.z);
}

// The version I must write due to API constraints
double distance_from_origin(const double * const p) {
  return sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
}

Obviously, this is error-prone. I have three potential solutions, with one favorite.

Solution 1

I can use #define or const globals, in a header somewhere, to alias names to indexes.

const size_t x = 0;
const size_t y = 1;
const size_t z = 2;

double distance_from_origin(const double * const p) {
  return sqrt(p[x] * p[x] + p[y] * p[y] + p[z] * p[z]);
}

This makes sure x is always consistent, but pollutes the global namespace. I could hide it in a namespace, but then it's more awkward to use.

Solution 2

An idea previously mentioned here:

struct Orientation {double x, y, z, rot_x, rot_y, rot_z};

Orientation& asOrientation(double * p) {
  return *reinterpret_cast<Orientation*>(p);
}

double distance_from_origin(const double * const p) {
  Orientation& o = asOrientation(p)  
  return sqrt(o.x * o.x + o.y * o.y + o.z * o.z);
}

This has nicer syntax, but relies on the rules of C/C++ struct packing. I think that it's safe as long as Orientation is a POD. I'm nervous about relying on that.

Solution 3

struct Orientation {
  Orientation(double * p): x{p[0]}, y{p[1]}, z{p[2]}, rot_x{p[3]}, 
    rot_y{p[4]}, rot_z{p[5]} {}

  double &x, &y, &z, &rot_x, &rot_y, &rot_z
};

double distance_from_origin(const double * const p) {
  Orientation o{p};
  return sqrt(o.x * o.x + o.y * o.y + o.z * o.z);
}

This no longer relies on struct-packing rules, and has nice syntax. However, it relies on compiler optimizations to ensure that it has zero overhead.

Solution 4

Based on this comment by GManNickG.

constexpr double& x(double * p) {return p[0];}
constexpr double& y(double * p) {return p[1];}
constexpr double& z(double * p) {return p[2];}
// ... etc.

double distance_from_origin(const double * const p) {
  return sqrt(x(p) * x(p) + y(p) * y(p) + z(p) * z(p));
}

Questions

  1. Solution 3 seems like the best to me. Does it have a potential downside that I'm missing, beyond reliance on compiler optimization?

  2. Is there another solution that's superior to any of these three?

like image 509
sw001 Avatar asked Sep 03 '26 20:09

sw001


1 Answers

Firstly, I wouldn't discount just copying the parameters from the array into a simple struct. Copying 6 doubles into a struct will be very quick.

Otherwise, I suggest wrapping the array in a class and expose the parameters as member functions:

class Orientation {
    const double *p_;
public:
    Orientation(const double *p) : p_(p) {}
    double x() const { return p_[0]; }
    double y() const { return p_[1]; }
    double z() const { return p_[2]; }
    double rot_x() const { return p_[3]; }
    double rot_y() const { return p_[4]; }
    double rot_z() const { return p_[5]; }
};

With your Solution 3 I doubt a compiler can optimize the size of your Orientation struct, it will have the size to contain 6 references. With Solution 3 it will not be assignable due to the references.

like image 175
Chris Drew Avatar answered Sep 06 '26 11:09

Chris Drew



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!