[I did some vertical compression below just for quote-shortening purposes]
... usually [the code duplication] happens when you want something
to be done with only one coordinate of vector and not all. For eg.
in my kdtree building module, I have to find the median of the
vertices(vectors) along x, y or z coordinates based on the value
of a flag called splitplane. This splitplane may take values 0, 1,
2 (0 means split along x, 1 means split along y, 2 means split
along z). If this notation was used -
typedef struct { double coord[3]; } vector;
... I simply need to specify v.coord[splitplane] ... whereas if this
notation is followed -
typedef struct { double x, y, z; } vector;
I have to do something like below :
if (splitplane == 0) { /* Find median along x */ }
if (splitplane == 1) { /* Find median along y */ }
if (splitplane == 2) { /* Find median along z */ }
This is what I mean by "triplification". Here, the logic behind
finding the median is same but it will be repeated thrice.
Elsethread, others have suggested various approaches. Here is one
more that is a variant of the pointer-to-element version. It is
not very pretty, but -- modulo errors (this code is untested) --
is guaranteed to work, and *may* have a lower runtime cost than
calling a function to find &vecp->x, &vecp->y, or &vecp->z.
[begin contents of vector.h]
/* Vector type, including its element-type. */
typedef double Vector_elt_type;
/*
* optionally, you might also includ
* typedef struct vector Vector_type;
* but I prefer to use the word "struct" for a type-name, except
* in cases where it is guaranteed to be an alias for a scalar
* type (as with Vector_elt_type, which is one of float, double,
* or long double).
*
* (Think of the "struct" keyword as a funny way to spell the
* word "type".)
*/
struct vector {
Vector_elt_type x, y, z;
};
/* Vector "plane selector" numbers. */
enum { VECTOR_X = 0, VECTOR_Y = 1, VECTOR_Z = 2 };
/*
* Note that these are byte offsets, so that they will work
* even if the "vector" struct is augmented with additional
* fields before, between, and/or after the three elements
* we care about, and regardless of any padding a compiler might
* use.
*/
extern const size_t vector_field_offsets[];
/*
* Given a pointer to a vector, pick the X, Y, or Z element based
* on the plane number, which must be one of VECTOR_X, VECTOR_Y,
* or VECTOR_Z.
*/
#define VECTOR_TO_ELT(vecp, plane) \
((Vector_elt_type *)((char *)(vecp) + vector_field_offsets[plane]))
[end of vector.h extract -- there might be more in there, e.g.,
prototypes for functions in vector.c]
[begin contents of vector.c]
#include "vector.h"
/*
* The initializers here assume that VECTOR_X is 0,
* VECTOR_Y is 1, and VECTOR_Z is 2. We could check this
* with a COMPILE_TIME_ASSERT macro (google for it), but
* here I do not bother.
*/
const size_t vector_field_offsets[] = {
offsetof(struct vector, x),
offsetof(struct vector, y),
offsetof(struct vector, z),
};
[end contents of vector.c, though in practice there would be more
code]
[begin contents of some vector-using C code]
#include <assert.h>
#include <stddef.h>
#include "vector.h"
/* some sort of median finding operation: */
void median_op(struct vector *vector_set, size_t nvectors, int plane) {
size_t i;
Vector_elt_type *p;
... other variables as needed ...
/*
* It is a programmer error to call this function with
* a plane that is not one of the three specified vector
* plane numbers.
*/
assert(plane == VECTOR_X || plane == VECTOR_Y || plane == VECTOR_Z);
for (i = 0; i < nvectors; i++) {
p = VECTOR_TO_ELT(&vector_set
, plane);
... work with *p as needed to find median ...
}
}