2

This builds off of my previous question: Is this an appropriate use of const qualifiers in C?

In Vector.h:

typedef struct _Vector Vector;
Vector* vector_init(const UInt32 size);
void*  vector_get(const Vector *v, UInt32 idx);
void   vector_add(Vector *v, void* const elem);
void   vector_set(Vector *v, const UInt32 idx, void* const elem);
...etc

In Vector.c:

struct _Vector{
    UInt32 used;
    UInt32 size;
    void** arr;
};

Vector* vector_init(const UInt32 size){
    Vector* v = malloc(sizeof(Vector));
    v->used = 0;
    v->size = size;
    v->arr = malloc(size*sizeof(void*));
    return v;
}

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

void vector_add(Vector *v, void* const elem){
    if( v->used == v->size )
        vector_resize(v);
    v->arr[v->used++] = elem;
}
...etc

I want to prevent void** arr in _Vector from being accidentally modified by my implementation, as a warning/error at compile time. I can't make arr a const void** because I don't want the vector to be permanent, and want to avoid casting away constness.

The idea here is that used and size are data that are directly related to the scope of my functions, whereas arr is just a kind of metadata that I don't want to modify. arr is opaque, so I can't directly modify it, but I can cast it or directly overwrite in bad ways such as memcpy.

In this example it seems unnecessary to enforce this access since each function is straight-forward, but this example mostly for demonstration purposes.

I have two related questions that can be answered at the same time:

  1. Is this access restriction possible with the language support in C, and if not, is the opaqueness of the pointer considered a strong enough discouragement of directly modifying the data?
  2. Am I just chasing my tail here and instead should focus on generating code that is well documented and well structured, where this wouldn't even become an issue?

I see a related question was asked in: Is there a way to protect a class variable from being modified outside of a function

That user seems to be in a similar predicament in C#; my question #2 is relevant in that scenario.

EDIT: I think from everyone's input, it's becoming clear that I'm pushing the language outside of its original design by looking for some type of magical keyword to document things for me. Either hiding my data behind another abstraction, or structurally separating out my functionality to prevent the temptation to poke at the data, seems to be the best solutions.

Community
  • 1
  • 1
Mike Lui
  • 1,301
  • 10
  • 23
  • `void** const arr;`? – user657267 Jun 19 '15 at 02:46
  • Maybe you can add some `#defines` that do the member access for you, and obfuscate the name a bit. That could help, because you can easily change the (file-internal) `#defines` to define and access a member with another name, but outside of the `.c` you would have to name the (obfuscated name of) member explicitly. – WorldSEnder Jun 19 '15 at 02:57
  • 3
    I'll take #2 for $10, bob – M.M Jun 19 '15 at 03:27
  • 1
    Why did you add C++ tag? In C++ you would not code like this at all. Decide which language you are using ... – M.M Jun 19 '15 at 03:28
  • @user657267: that would only prevent a function from changing the pointer, not the data it's pointing to. I'd also be forced to cast my malloc to const, thereby not being able to correctly free it. – Mike Lui Jun 19 '15 at 03:52
  • @MattMcNabb: Good point about structuring the problem in c++, I removed the tag. – Mike Lui Jun 19 '15 at 03:52
  • @ChronoKitsune: This is the back and forth I had in my previous question linked at the top of this question. I ended up being convinced that that would be a misuse of constness. – Mike Lui Jun 19 '15 at 03:52

3 Answers3

1
struct _Vector
{
    UInt32 used;
    UInt32 size;
    void **arr;
};

...

void foo(Vector *v)
{
    //Data cannot be modified, but arr and arr[idx] can still be assigned.
    const void **arr = v->arr;

    /*
     * Only work with local variable "arr".
     */

    /* Since v->arr wasn't const in the first place, casting away
       "const" shouldn't be an issue. */
    v->arr = (void **)arr;
}

I'm guessing some variation of that will work for you. Otherwise, use the abstraction of getter and setter functions instead of assigning directly to structure members in every function. That way, you don't need to worry about it.

  • The only problem with this is that if someone extends the implementation, they don't know to create a const local, and depending on what's actually being implemented, they might directly modify the data in that function. – Mike Lui Jun 19 '15 at 04:00
  • @MitchLaskis Yes, that's very true. I merely suggested it as an alternative to adding the use of getter/setter functions. Great question! :-) –  Jun 19 '15 at 04:08
0
  1. I believe you can do something like void* const. The const placement makes the pointer and the data the pointer points to immutable.

  2. In my opinion (which isn't saying much), wanting the data of a pointer to not be modified seems like a reasonable desire, as it helps document what the code does with the data passed to it (not modify it).

Adam Evans
  • 2,072
  • 1
  • 20
  • 29
  • 1
    `void* const` only makes the pointer, not the data *appear* immutable – WorldSEnder Jun 19 '15 at 02:54
  • @WorldSEnder that's what he wants (I think) – M.M Jun 19 '15 at 03:28
  • I actually do want the data to appear immutable. I want to document in the code that the container is purely meant to be a container for the data, not to modify it in any way, to prevent any one from being tempted to directly modify the data. This case is obvious, but for more complicated data abstractions it might not be. – Mike Lui Jun 19 '15 at 03:57
0

Since you were looking for a solution either in C/C++, I created a layer of abstraction in C++ to protect the void** const arr as follows. Please check does it meet your requirement.

class  array{
       public:
        void setArray(void** key)
        {
            arr=key;
        }

        void** getArray()
        {
            return &(*arr);
        }
    private:
     void** arr;
};
struct Vector: public array{
    int  used;
    int  size;

};

Vector* vector_init(const int  size){
    Vector* v =(Vector*) malloc(sizeof(Vector));
    v->used = 0;
    v->size = size;
    void** tem=v->getArray();
    tem=(void**) malloc(size*sizeof(void*));
    v->setArray(tem);
     return v;
}

Demo: http://coliru.stacked-crooked.com/a/005b335e5fb372ce

Steephen
  • 14,645
  • 7
  • 40
  • 47
  • While I'd prefer to get away from additional overhead, it does seem that adding yet another layer of abstraction would be the most straightforward and correct solution. – Mike Lui Jun 19 '15 at 03:54
  • 1
    The original question is tagged with C. The answer here is in C++ – tangrs Jun 19 '15 at 04:47
  • @tangrs It was originally tagged C++ too please check comment from Matt McNabb to the question. – Steephen Jun 19 '15 at 10:44