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?
Personally, I'd more like this:
It's C after all, if people want to screw up, they should be allowed to - no need to hide stuff, except:
If what you need is to keep the ABI/API compatible, there's 2 approaches that's more common from what I've seen.
Don't give your clients access to the struct, give them an opaque handle (a void* with a pretty name), provide init/destroy and accessor functions for everything. This makes sure you can change the structure without even recompiling the clients if you're writing a library.
provide an opaque handle as part of your struct, which you can allocate however you like. This approach is even used in C++ to provide ABI compatibility.
e.g
My solution would be to provide only the prototype of the internal struct and then declare the definition in the .c file. Very useful to show C interface and use C++ behind.
.h :
.c :
Note: In that case, the variable have to be a pointer because the compiler is unable to know the size of the internal struct.
There are better ways to do this, like using a
void *
pointer to a private structure in the public struct. The way you are doing it you're fooling the compiler.Not very private, given that the calling code can cast back to a
(SomeStructSource *)
. Also, what happens when you want to add another public member? You'll have to break binary compatibility.EDIT: I missed that it was in a .c file, but there really is nothing stopping a client from copying it out, or possibly even
#include
ing the .c file directly.I do not recommend using the public struct pattern. The correct design pattern, for OOP in C, is to provide functions to access every data, never allowing public access to data. The class data should be declared at the source, in order to be private, and be referenced in a forward manner, where
Create
andDestroy
does allocation and free of the data. In a such way the public/private dilemma won't exist any more.In the other side, if you do not want to use Malloc/Free (which can be unnecessary overhead for some situations) I suggest you hide the struct in a private file. Private members will be accessible, but that on user's stake.
Related, though not exactly hiding.
Is to conditionally deprecate members.
Note that this works for GCC/Clang, but MSVC and other compilers can deprecate too, so its possible to come up with a more portable version.
If you build with fairly strict warnings, or warnings as errors, this at least avoids accidental use.