I'm writing a library for matrix and vector mathematics and I find it would be convenient to access vector components as if the vertex were both an array vector[2]
and a struct vector.y
, all while being able to initialize the variable with curly braces (type) vector = {0, 1, 3};
.
I found something close with the use of unions, namely
typedef union {
struct {
float x, y, z;
};
float data[3];
} v3;
which is great for the latter two criteria, but indexing the data with ints can only be done with vector.data[2]
.
I find it would be convenient to access vector components as if the vertex were [...] an array
Through its definition in terms of pointer arithmetic, the C indexing operator []
requires one operand to be an integer and the other a pointer (possibly the result of automatic array-to-pointer conversion). Structures and unions are not acceptable operands. Pointers to structures and unions are acceptable, but they would not produce the semantics you want. C does not have operator overloading, so that's pretty much the end of the story. There is no way to define a C data type that is not an array type yet provides for array-like element access via the indexing operator.*
I found something close with the use of unions
Yes, and variations on your union approach are the best you can do for a data type that provides access to the vector components both via index and via name. When I write something along those general lines, I tend to choose the union member names in a way that expresses what is going on as clearly as possible. In this case, I might name the array member as_array
, so that you might write vector.as_array[1]
. YMMV.
As an alternative, you could consider macro-based accessors for at least the access-by-name case. For example, given
typedef float vector3[3];
#define vec_element_x 0
#define vec_element_y 1
#define vec_element_z 2
#define vec_element_0 0
#define vec_element_1 1
#define vec_element_2 2
#define element(v,name) (v[vec_element_ ## name])
, you could write
vector3 dir = { 1, 2, 3 };
// access by name
float y = element(dir, y);
element(dir, z) = 4;
// access by index is supported this way too
float x = element(dir, 0);
element(dir, 1) = -0.5;
// ... but only for integer constants 0, 1, 2, not expressions or variables
* Where by saying element access, I mean to exclude pointer types such as float *
, on the basis that the pointed-to object and others around it are not elements of the pointer.