Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass C struct as "deep" const

I have a struct which holds some pointers to data it does not own, e.g.:

struct s {
    int* x;
    int* y;
};

I can then pass it as a const pointer itself:

int foo(const struct s* s) {
    return *s->x + *s->y;
}

This however is not as safe as I want, because while foo is not allowed to modify the pointers s->x and s->y, it is allowed to modify the ints they point to:

int foo(const struct s* s) {
    // s->x = s->y;  // compiler error: assignment of member in read-only object
    *s->x = *s->y;  // allowed!
    return *s->x + *s->y;
}

Is there any way to change the signature of foo so that it treats the passed struct as "deeply" const, including all its pointers (possibly nested)?

In C++ I would solve it by making the pointers private and overloading getters:

class foo {
    private:
    int *_x;
    int *_y;

    public:
    s(int* x, int* y) : _x{ x }, _y{ y } {}
    int* x() { return _x; }
    const int* x() const { return _x; }
    int* y() { return _y; }
    const int* y() const { return _y; }
};
like image 484
Dominik Kaszewski Avatar asked Oct 15 '25 03:10

Dominik Kaszewski


1 Answers

Is there any way to change the signature of foo so that it treats the passed struct as "deeply" const, including all its pointers (possibly nested)?

No. Because the members of s are declared as int * which means you are allowed to change what they point to. I can't see a way around that short of changing the declaration of s to make the pointers point to const integers. This, for example errors:

struct SConst 
{
    const int* x;
    const int* y;
};

int foo(const struct SConst* s) 
{
    *s->x = *s->y; // Error! 
    return *s->x + *s->y;
}

This is what I see if I compile the above (with clang)

foo.c:19:8: error: read-only variable is not assignable
   19 |         *s->x = *s->y;
      |         ~~~~~ ^

Of course, it does mean you can't assign to the things the members point to.

    int x = 1;
    int y = 2;
    struct SConst baz = { .x = &x, .y = &y };
    *baz.x = 5; // error!

But then, maybe that's what you want since you can still assign to the thing pointed to.

int main()
{
    int x = 1;
    int y = 2;
    struct SConst baz = { .x = &x, .y = &y };
    x = 5;
    printf("%d\n", *baz.x); // prints 5
}
like image 151
JeremyP Avatar answered Oct 17 '25 21:10

JeremyP