Question

I have a struct definition which is visible only in the .c file where it is declared.

struct private
{
    int n ;
    void* data ;

    int field ;
}

The only way to access the members is though the functions defined in the same file and declared in the header.

I declare a struct in a header that is visible everywhere

struct public
{
    int n ;
    void* data ;
}

And then I have a function that returns the public struct that is actually private struct casted

struct public* this = GetPrivateStruct() ;    //function returns pointer to struct private malloced internally, casted to public struct 
this->n = 123 ;

So far the code is correct with no undefined behavior.

But can I make the public struct with const members instead?

struct public
{
    const int n ;
    const void* data ;
}

So only reading is allowed:

void* private_struct = GetPrivateStruct() ;
struct public* this = ( struct public* )private_struct ;
this->n = 123 ;   //<-- this will now give an error which is fine as it is not allowed
int n = this->n ; //we can only read the value
Was it helpful?

Solution

int and const int are not compatible types (6.7.3p10) so you wouldn't be able to apply the common initial sequence rule (6.5.2.3p6) for storage in a union; that said, that's not what you're doing anyway, so you must be relying on the lvalue conversion rules (6.3.2.1p2) which allow stripping one layer of type qualification, but only if the lvalue refers to an object of appropriate type.

Note that from 6.2.5p26 and 6.7.2.1p15 we can infer that the two structs have the same layout (for the public elements) but it does not follow that what you're doing is legitimate. As discussed at "Private" struct members in C with const, the key is that the optimiser (when operating on user code) will note that the members of struct public are const and infer that they cannot be changed anywhere including by (member) functions of the implementation.

However, if you're happy to trust the user to not const-cast this->n, then they can be trusted to not const-cast this, so why not give them a pointer to a const-qualified object? You can even make public a const typedef:

typedef const struct initial {
    int n;
    void *data;
} public;

In addition, it makes sense to reuse the initial layout at the cost of a few extra characters:

struct private {
    struct initial i;

    int field;
};

You can now give users &this->i and as initial and public are qualified versions of compatible types you don't even need to cast anything (though you could, by 6.7.2.1p15).

OTHER TIPS

You should be able to, but it's very strange. You really should avoid a situation where you have to maintain 2 definitions of the same data structure.

Also, if you do anything like have an array of the struct members you will get "undefined behaviour" indeed. Sounds like you'd prefer to be in C++!!

When doing something similar in C before I've had a pointer to private information, like this:

struct _my_public_struct
{
int stuff
void *pMyPrivateData;
};

When the struct is created I assign another struct to the private data

struct _my_public_struct ss = _malloc(sizeof(struct _my_public_struct));
ss->pMyPrivateData = _malloc(sizeof(struct _my_private_struct));

Here the _my_private_struct will only contain your private data, not the public stuff and you can change it to your heart's content.

I've tried that with Pelles C, which is C99 compliant, and works the way you expect: trying to compile this file:

struct public
{
    const int n;
    const void* data;
    const int field;
};

void *GetPrivateStruct (int n, int field);
void PrintPrivateStruct (void *p);

int main()
{
    struct public *p;

    p = (struct public *)GetPrivateStruct (1,2);
    PrintPrivateStruct (p);
    p->n = 3;
    PrintPrivateStruct (p);
}

Throws me this error at the line with p->n = 3;:

main.c(17): error #2033: Assignment to const location.

Beware, because it's easy to bypass the const modifier:

void swap (int *a, int *b)
{
    int t;

    t = *a;
    *a = *b;
    *b = t;
}

int main()
{
    struct public *p;

    p = (struct public *)GetPrivateStruct (1,2);
    PrintPrivateStruct (p);
    swap ((int *)&p->field, (int *)&p->n);
    PrintPrivateStruct (p);
}

So: if you show the internal fields of your struct, there's always a posibility for someone to bypass the read-only behaviour imposed by const and perform actual writes to those fields. The only data that is actually protected by the const keyword are those global variables that have been initialized during their declaration, and have been stored in a read-only memory region, protected by the OS, or in the case of a embedded system, protected by the unability of the memory device to accept writes from the processor.

There is a feature I learnt from Borland C++ and has been ported into C# which would prove very helpful here if it were implemented: accessors and mutators. Basically, your fields are private (unknown to the user) and there are public aliases of these fields, known to the user, so when you make an assignment to the public alias, you are actually calling a private function, the mutator, that is in charge of updating the private field, as its discrection. When you use the public field in a expression, you are actually calling another function, the accessor, which retrieves the private field value and passes it to the expression. If you write an accessor for a field, but not a mutator, you are effectively making that field read only for all intents and purposes.

We can emulate this feature, if not with the same sintax, at least, clear enough to not to mess with the code. My proposal is like this:

Don't document any public version of your struct. All fields will kept private.

Document the public fields your struct will have. Assign them different names from the private version (the alias).

For each field, write a function like this (for example, for alias fields n and field you would write):

int inline __get_field_n (void *p)
{
  return ((struct private *)(p))->__private_n;
}

int inline __get_field_field (void *p)
{
  return ((struct private *)(p))->__private_field;
}

Write a macro that will serve as accessor:

#define GET(pstr,fld) (__get_field_##fld (pstr))

This macro will be used to access any field from a valid pointer to the struct. It's written so you can do things like this:

printf ("n = %d, field = %d\n", GET(p,n), GET(p,field) );

Considering this structure:

struct element1 {
    int a;
    int b;
    char c;
};

Declaring this structure only defines the way your program will store that's structure data and how to manipulate it. So declaring a variable of that structure type is allowing to store the element this way:

------------------------------
| 4 bytes | 4 bytes | 1 byte |
------------------------------
|    a    |    b    |    c   |
------------------------------

However you can't define the public structure with constant fields as they are not a compatible types. Instead you can define the whole structure as a constant.

const struct element2 {
    int a;
    int b;
};

So by declaring the public structure as constant and a pointer to that structure, then assining to it a casted pointer to the private structure is just making the first one points to the adresse pointed to by the second.

The way that the data pointed to will be manipulated is depending on how the public structure is defigned.

Conclusion: From the definition of the public structure we can assume that:

1) The last field (char c) of the original structure can't be handled by the public structure as it is not defined within it.

2) The fields of the structure can't be modified as the public structure is defined to be a constante.

NB: When defining the public structure make sure to match the types and the order of the fields (regardless of the fields you want to ignore but you can't ignore a field in the middle) as the casting doesn't matchs them. This exemple shows the confliction if the fields are not matched properly http://ideone.com/A41vn7.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top