I am designing an API and am considering the use of "immutable" structs", or "read-only structs". Using a simplified example, this could look something like:
struct vector {
const float x;
const float y;
};
struct vector getCurrentPosition(void);
struct vector getCurrentVelocity(void);
Everything works fine as long as I return the immutable struct on the stack. However, I run into issues when implementing a function like:
void getCurrentPositionAndVelocity(
struct vector *position,
struct vector *velocity);
At this point, I do not want to introduce a new immutable struct that contains two vectors. I also can not do this:
void
getCurrentPositionAndVelocity(
struct vector *position,
struct vector *velocity)
{
*position = getCurrentPosition();
*velocity = getCurrentVelocity();
}
because position
and velocity
are read-only (although my installed version of clang
incorrectly compiles this without warning).
I could use memcpy
s to work around this, like this:
void
getCurrentPositionAndVelocity(
struct vector *position,
struct vector *velocity)
{
struct vector p = getCurrentPosition();
struct vector v = getCurrentVelocity();
memcpy(position, &p, sizeof *position);
memcpy(velocity, &v, sizeof *velocity);
}
This looks bad but I believe it does not mislead the user of the API, to whom the vector
struct still looks immutable. I could additionally add a required initializer for this purpose, where a call to a function like this would only succeed for vector
structs with a special value. Something like:
const struct vector * const VECTOR_UNINITIALIZED;
where the user should do
struct vector position = *VECTOR_UNINITIALIZED;
struct vector velocity = *VECTOR_UNINITIALIZED;
before invoking getCurrentPositionAndVelocity()
. Before memcpy
-ing, the implementation would assert with a memcmp
that the vectors do have the "uninitialized sentinel" value. (Typing this, I realize that this will only work if there are certain truly unused values that can act as "uninitialized sentinel" values but I think that is the case for me.)
My question is whether this usage of the const
keyword is in line with its intention, from the API user's perspective? And would there be any risks involved from a compiler perspective, in that this code may, strictly speaking, violate the read-only semantics as indicated with the const
keyword? As an API user, what would you think of this approach or would you have any better suggestions?