5

I have a simple vector implementation in C, which holds an array of void*. It's up to the user to take care of the actual type.

I want to 'promise' that the vector will not alter its contents, so I store data as:

Struct _Vector{
    UInt32 used;
    UInt32 size;
    const void** arr;
};

I don't know if it's considered "overdoing it" with my constness correctness, but I try to maintain const correctness with my accessors/modifiers:

void vector_add(Vector *v, const void* const elem);   
void vector_set(Vector *v, const UInt32 idx, const void* const elem);

When I return an element from the vector, it is a pointer to the user's data so I let the user modify the data being pointed to, by casting away the constness in the internal array.

void* vector_get(const Vector *v, const UInt32 idx){
    if ( idx >= v->used ) 
        exitAtError("Vector","Array out of bounds");
    return (void*)v->arr[idx];
}

Instead of returning a const void*, I return a void* instead. The idea is that internally, I want to ensure that I am not changing the data being pointed to, but I don't care what happens to the data outside the vector.

Is this considered a correct use of const? I would think it's okay to declare data as const in one place, while having it be mutable elsewhere. I'm unsure after reading many dogmatic articles claiming never to cast away constness, and if it is casted away, then the use of const was not appropriate to begin with.

Mike Lui
  • 1,301
  • 10
  • 23
  • I wouldn't consider it "dogmatic" to avoid casting away constness. The whole point of constness is to let the compiler enforce it. That becomes useless, if you circumvent it by casting `const` away. – mastov Jun 18 '15 at 21:46
  • I would not pay any regards to the return value, since it can be cast to that by the caller. – Pynchia Jun 18 '15 at 21:46
  • You both make good points. Const qualifiers were designed the way they were for a reason. I wasn't sure if I was 'going around its back' or not by casting const away, but regardless I realize that my reasoning is not sufficient. – Mike Lui Jun 18 '15 at 22:37

3 Answers3

2

I want to 'promise' that the vector will not alter its contents

This is C. Data structures do not have behavior. You can define a vector data structure such that the data ultimately pointed-to cannot be modified via the vector, and that's what you have done. Of course, this has its implications.

I don't know if it's considered "overdoing it" with my constness correctness, but I try to maintain const correctness with my accessors/modifiers:

If you're going to bother with const at all, then by all means do adhere rigorously to const-correctness. Otherwise, there's not much point.

When I return an element from the vector, it is a pointer to the user's data so I let the user modify the data being pointed to, by casting away the constness in the internal array.

Nope. You've blown it. const-correctness needs to be all or nothing to be worth anything.

By casting away const, you violate the contract that your Vector type makes with its users by allowing the pointed-to values to be modified via an instance. Indirectly so, to be sure, but that doesn't matter. Moreover, this can contribute to wider violations of const correctness -- suppose, for example, that the data entered into your vector were const in the first place. Then storing them in your data structure would be ok, but you provide functions that could allow them to be modified (or at least would allow an attempt at that).

You could consider using an opaque data structure instead of const-qualifying the members to prevent elements from being accessed directly through the vectors. Ultimately, though, const-correctness will require separate data structures and functions for vectors that contain const data and those that contain non-const data.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thanks, this was my concern, that I was misunderstanding the strength of the const contract. You're example is a great demonstration of how its misuse can be problematic. Would you say that C just does not support the kind of enforcement I want, i.e. only the scope of the vector doesn't modify the data? Or is it just the way I've designed my code? – Mike Lui Jun 18 '15 at 22:30
  • Additionally I want to point out that the Vector struct is indeed opaque. Anyone who implements it, only sees the Vector's typedef and function declarations. – Mike Lui Jun 18 '15 at 22:41
  • @MitchLaskis: Note that violating the `const` contract will _exhibit undefined behaviour_. `const` variables in the first place might be placed in read-only memory or write-protected, so any write will result in desaster (even literally!). – too honest for this site Jun 18 '15 at 23:34
  • @Olaf : I see now that const is really saying "this will never change until the program ends or the variable goes out of scope", and is not a meant to enforce temporary access permissions. What I'm really looking for is a way to say, "don't ever modify this pointer except through these specific functions", which const in not designed for. – Mike Lui Jun 19 '15 at 00:47
  • @MitchLaskis: It a guarantee _you_, as the programmer, give to the compiler (I like the term "contract" here). For such, use opaque pointers (well, pointer to opaque structures). But that would be another question. I'd recomend to do some reseach on your own about this. There should be quite some resources to be found on that thing called "internet". – too honest for this site Jun 19 '15 at 00:50
  • @Olaf: I'm aware of opaque pointers but I was looking for something even more restrictive. Someone can still directly modify the data with something like memcpy and I was wanted to give a compiler error/warning in a case like that. However I realized that at that point, you could just cast the pointer to a void* and we're back at the beginning. TL;DR: I overthought the problem. Thanks for the helpful feedback! – Mike Lui Jun 19 '15 at 01:10
  • 1
    @MitchLaskis: You should have searched for that, as I might have [answered that already](http://stackoverflow.com/questions/30845344/is-there-any-way-of-protecting-a-variable-for-being-modified-at-runtime-in-c/30845926#30845926). Note that is for an int, but can be easily extended. If something is not clear, you might leave a comment there. – too honest for this site Jun 19 '15 at 01:14
1

I would consider this a bad idea, as it allows writing to const objects without a warning. For example:

const int x = 5;
vector_add(&v, &x);

int *p = vector_get(&v, 0);
*p = 6;   // oops

Instead, return const void *. Then you can let the caller make the decision about when to cast away const.

You'll probably end up having two versions of the vector, one holding void * and one holding const void *.

M.M
  • 138,810
  • 21
  • 208
  • 365
1

While other answers are correct in limited const-correctness perspective, you should note that in C once pointer entered this vector, there is no way to know if data is [im]mutable. In this case, vector should not care about that and always take and return non-const pointers. If someone wants to pass const-pointer there, he will cast and take care to un-cast when doing get(). It is responsibility of user to restore initial storage semantics, as he does know it.

Your actual claim here is that you will never ever dereference the pointer, but not that it is const. Just write that in docs.

user3125367
  • 2,920
  • 1
  • 17
  • 17