I've been reading about OOP in C but I never liked how you can't have private data members like you can in C++. But then it came to my mind that you could create 2 structures. One is defined in the header file and the other is defined in the source file.
// =========================================
// in somestruct.h
typedef struct {
int _public_member;
} SomeStruct;
// =========================================
// in somestruct.c
#include "somestruct.h"
typedef struct {
int _public_member;
int _private_member;
} SomeStructSource;
SomeStruct *SomeStruct_Create()
{
SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource));
p->_private_member = 42;
return (SomeStruct *)p;
}
From here you can just cast one structure to the other. Is this considered bad practice? Or is it done often?
Use the following workaround:
Result is:
This approach is valid, useful, standard C.
A slightly different approach, used by sockets API, which was defined by BSD Unix, is the style used for
struct sockaddr
.You almost have it, but haven't gone far enough.
In the header:
In the .c:
The point is, here now consumers have no knowledge of the internals of SomeStruct, and you can change it with impunity, adding and removing members at will, even without consumers needing to recompile. They also can't "accidentally" munge members directly, or allocate SomeStruct on the stack. This of course can also be viewed as a disadvantage.
sizeof(SomeStruct) != sizeof(SomeStructSource)
. This will cause someone to find you and murder you someday.I'd write a hidden structure, and reference it using a pointer in the public structure. For example, your .h could have:
And your .c:
It obviously doesn't protect against pointer arithmetic, and adds a bit of overhead for allocation/deallocation, but I guess it's beyond the scope of the question.
Something similar to the method you've proposed is indeed used sometimes (eg. see the different varities of
struct sockaddr*
in the BSD sockets API), but it's almost impossible to use without violating C99's strict aliasing rules.You can, however, do it safely:
somestruct.h
:somestruct.c
: