This is a poll for opinions on the most readable way to do something: whether to use a C ++ pointer, a byte offset, or a template functor to determine "select an X element from the foo structure".
I have a type that contains a large vector of structures, and I'm writing a utility function that basically works how to reduce over a certain range of them. Each structure associates a group of dependent variables with a certain point along an independent measurement - in order to come up with a simplified example, imagine that it captures a number of environmental conditions for a room over time:
// all examples are psuedocode for brevity struct TricorderReadings { float time; // independent variable float tempurature; float lightlevel; float windspeed; // etc for about twenty other kinds of data... }
My function simply performs cubic interpolation to guess these conditions for some given point in time between the available samples.
// performs Hermite interpolation between the four samples closest to given time float TempuratureAtTime( float time, sorted_vector<TricorderReadings> &data) { // assume all the proper bounds checking, etc. is in place int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, data[idx-1].tempurature, data[idx+0].time, data[idx+0].tempurature, data[idx+1].time, data[idx+1].tempurature, data[idx+2].time, data[idx+2].tempurature ); }
I would like to generalize this function so that it can be applied in the general case to any term, and not just to temperature. I can come up with three ways to do this, and although they are all easy to code, I’m not sure what will be the most readable for anyone who should use it in a year. Here is what I am considering:
Member Pointer Syntax
typedef int TricorderReadings::* selector; float ReadingAtTime( time, svec<TricorderReadings> &data, selector whichmember ) { int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, data[idx-1].*whichmember, ); }
This seems like the most “C ++ y” way to do this, but it looks weird, and the whole pointer-member syntax is rarely used and therefore is poorly understood by most people on my team. This is technically the “right” way, but also the one for which I receive the most vague letters.
Structure offset
float ReadingAtTime( time, svec<TricorderReadings> &data, int memberoffset ) { int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, *(float *) ( ((char *)(&data[idx-1]))+memberoffset ), ); }
This is functionally identical to the above, but the math of the pointer is explicitly specified. This approach will be immediately familiar and understandable to everyone in my team (who has learned everything from C to C ++), and it is strong, but it just seems not good.
Templatized functor
template <class F> float ReadingAtTime( time, svec<TricorderReadings> &data ) { int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, F::Get(data[idx-1]) ), ); }
This is the easiest and STL-ish way to do something, but it looks like a whole bunch of extra typing and syntax and later class definitions. It compiles almost exactly the same as the two above, but it also unloads a bunch of redundant function definitions throughout the executable. (I checked this with / FAcs , but maybe the linker takes them out again.)
All three above will work, and the compiler emits almost the same code for all of them; so the most important choice I have to make is just what is most readable. What do you think?