cstructdata-hiding

Public sizeof for privately defined struct


I have a little data-hiding module that looks like this:

/** mydata.h */
struct _mystruct_t;
typedef struct _mystruct_t mystruct;

mystruct *newMystruct();
void freeMystruct( mystruct** p );

/** mydata.c */
#include "mydata.h"
struct _mystruct_t {
    int64_t data1;
    int16_t data2;
    int16_t data3;
};
// ... related definitions ... //

For the most part, this is what I want; although simple, the struct has strict consistency requirements and I really don't want to provide access to the data members.

The problem is that in client code I would like to include the struct in another struct which I would like to allocate on the stack. Right now I am jumping through hoops to free the mystruct*s in some client code. Since a) mystruct is pretty small and I really don't think it's going to get big anytime soon and b) it's not a problem that client code has to recompile if I ever change mystruct, I would like to make the size of mystruct public (i.e. in the header).

Two possibilities I've considered:

/** mydata.h */
typedef struct {
    // SERIOUSLY DON'T ACCESS THESE MEMBERS
    int64_t data1;
    int16_t data2;
    int16_t data3;
} mystruct;

I think the drawbacks here speak for themselves.

OR

/** mydata.h */
#define SIZEOF_MYSTRUCT (sizeof(int64_t)+sizeof(int16_t)+sizeof(int16_t))
// everything else same as before...

/** mydata.c */
// same as before...
_Static_assert (SIZEOF_MYSTRUCT == sizeof(mystruct), "SIZEOF_MYSTRUCT is incorrect")

Of course this seems non-ideal since I have to update this value manually and I don't know if/how alignment of the struct could actually cause this to be incorrect (I thought of the static assert while writing this question, it partially addresses this concern).

Is one of these preferred? Or even better, is there some clever trick to provide the actual struct definition in the header while later somehow hiding the ability to access the members?


Solution

  • I've been researching and thinking and took one of my potential answers and took it to the next level; I think it addresses all of my concerns. Please critique.

    /** in mydata.h */
    typedef const struct { const char data[12]; } mystruct;
    mystruct createMystruct();
    int16_t exampleMystructGetter( mystruct *p );
    // other func decls operating on mystruct ...
    
    
    /** in mydata.c */
    typedef union {
        mystruct public_block;
        struct mystruct_data_s {
            int64_t d1;
            int16_t d2
            int16_t d3;
        } data;
    } mystruct_data;
    
    // Optionally use '==' instead of '<=' to force minimal space usage
    _Static_assert (sizeof(struct mystruct_data_s) <= sizeof(mystruct), "mystruct not big enough");
    
    mystruct createMystruct(){
        static mystruct_data mystruct_blank = { .data = { .d1 = 1, .d2 = 2, .d3 = 3 } };
        return mystruct_blank.public_block;
    }
    
    int16_t exampleMystructGetter(mystruct *p) {
        mystruct_data *a = (mystruct_data*)p;
        return a->data.d2;
    }
    

    Under gcc 4.7.3 this compiles without warnings. A simple test program to create and access via the getter also compiles and works as expected.