Hiding members in a C structure

I read about OOP in C, but I never liked how you cannot have private data members like you can in C ++. But then it occurred to me that you can create 2 structures. One of them is specified in the header file, and the other 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 simply shift one structure to another. Is this considered bad practice? Or is this often done?

+48
c struct private-members
Apr 20 '10 at 1:40
source share
13 answers

Personally, I like more:

 typedef struct { int _public_member; /*I know you wont listen, but don't ever touch this member.*/ int _private_member; } SomeStructSource; 

After all, if people want to mess up, they should be allowed to - no need to hide things except:

If you need to maintain compatibility with ABI / API, there are two approaches, a more general one from what I saw.

  • Do not give your clients access to the structure, give them an opaque descriptor (void * with a nice name), provide init / destroy and accessor functions for everything. This ensures that you can change the structure without even recompiling clients if you are writing a library.

  • provide an opaque handle as part of your structure, which you can select as you like. This approach is even used in C ++ to ensure compatibility with ABI.

eg

  struct SomeStruct { int member; void* internals; //allocate this to your private struct }; 
+31
Apr 20 '10 at 1:57
source share

sizeof(SomeStruct) != sizeof(SomeStructSource) . It will make someone find you and kill you someday.

+40
Apr 20 2018-10-10T00:
source share

You have almost none, but he has not gone far enough.

In the title:

 struct SomeStruct; typedef struct SomeStruct *SomeThing; SomeThing create_some_thing(); destroy_some_thing(SomeThing thing); int get_public_member_some_thing(SomeThing thing); void set_public_member_some_thing(SomeThing thing, int value); 

In .c:

 struct SomeStruct { int public_member; int private_member; }; SomeThing create_some_thing() { SomeThing thing = malloc(sizeof(*thing)); thing->public_member = 0; thing->private_member = 0; return thing; } ... etc ... 

The fact is that now consumers do not know about the insides of SomeStruct, and you can change it with impunity, add and remove members, even if consumers do not need to be recompiled. In addition, they cannot β€œaccidentally” use members or assign SomeStruct to the stack. This, of course, can also be considered a drawback.

+20
Apr 20 2018-10-10T00:
source share

I do not recommend using a public structure template. The correct design pattern for OOP in C is to provide access functions to each data, which does not allow public access to data. Class data must be declared in the source in order to be private, and referenced in reverse order, where Create and Destroy select and release the data. Thus, a public / private dilemma will no longer exist.

 /*********** header.h ***********/ typedef struct sModuleData module_t' module_t *Module_Create(); void Module_Destroy(module_t *); /* Only getters and Setters to access data */ void Module_SetSomething(module_t *); void Module_GetSomething(module_t *); /*********** source.c ***********/ struct sModuleData { /* private data */ }; module_t *Module_Create() { module_t *inst = (module_t *)malloc(sizeof(struct sModuleData)); /* ... */ return inst; } void Module_Destroy(module_t *inst) { /* ... */ free(inst); } /* Other functions implementation */ 

In another part, if you do not want to use Malloc / Free (which may be an unnecessary overhead for some situations), I suggest you hide the structure in a private file. Private members will be available, but it depends on the proportion of users.

 /*********** privateTypes.h ***********/ /* All private, non forward, datatypes goes here */ struct sModuleData { /* private data */ }; /*********** header.h ***********/ #include "privateTypes.h" typedef struct sModuleData module_t; void Module_Init(module_t *); void Module_Deinit(module_t *); /* Only getters and Setters to access data */ void Module_SetSomething(module_t *); void Module_GetSomething(module_t *); /*********** source.c ***********/ void Module_Init(module_t *inst) { /* perform initialization on the instance */ } void Module_Deinit(module_t *inst) { /* perform deinitialization on the instance */ } /*********** main.c ***********/ int main() { module_t mod_instance; module_Init(&mod_instance); /* and so on */ } 
+11
Mar 11 '13 at 14:17
source share

Never do that. If your API supports everything that SomeStruct accepts as a parameter (which I expect from it), then they can allocate one from the stack and pass it. You will get serious errors when trying to access a private member, because the one that the compiler allocation for the client class does not contain space for it.

The classic way to hide members in a structure is to invalidate it *. This is basically a handle / cookie that only your implementation files know about. Almost every C library does this for private data.

+9
Apr 20 '10 at 1:50
source share

Something similar to the method you proposed is actually used sometimes (for example, see the various options for struct sockaddr* in the BSD socket API), but it is almost impossible to use without violating C99's strict anti-aliasing rules.

However, you can do it safely:

somestruct.h :

 struct SomeStructPrivate; /* Opaque type */ typedef struct { int _public_member; struct SomeStructPrivate *private; } SomeStruct; 

somestruct.c :

 #include "somestruct.h" struct SomeStructPrivate { int _member; }; SomeStruct *SomeStruct_Create() { SomeStruct *p = malloc(sizeof *p); p->private = malloc(sizeof *p->private); p->private->_member = 0xWHATEVER; return p; } 
+6
Apr 20 '10 at 2:11
source share

I would write a hidden structure and refer to it using a pointer in a public structure. For example, your .h may have:

 typedef struct { int a, b; void *private; } public_t; 

And your .c:

 typedef struct { int c, d; } private_t; 

Obviously, it does not protect against pointer arithmetic and adds a bit of overhead for allocation / deallocation, but I assume this is beyond the scope of the question.

+4
Apr 20 '10 at 2:04
source share

Use the following workaround:

 #include <stdio.h> #define C_PRIVATE(T) struct T##private { #define C_PRIVATE_END } private; #define C_PRIV(x) ((x).private) #define C_PRIV_REF(x) (&(x)->private) struct T { int a; C_PRIVATE(T) int x; C_PRIVATE_END }; int main() { struct T t; struct T *tref = &t; ta = 1; C_PRIV(t).x = 2; printf("ta = %d\nt.x = %d\n", ta, C_PRIV(t).x); tref->a = 3; C_PRIV_REF(tref)->x = 4; printf("tref->a = %d\ntref->x = %d\n", tref->a, C_PRIV_REF(tref)->x); return 0; } 

Result:

 ta = 1 tx = 2 tref->a = 3 tref->x = 4 
+3
Apr 05 '15 at 20:40
source share

There are better ways to do this, for example, using the void * pointer for a private structure of a public structure. As you do this, you are cheating on the compiler.

+2
Apr 20 2018-10-10T00:
source share

This approach is valid, useful, standard C.

A slightly different approach used by the socket API, which was defined by BSD Unix, is the style used for struct sockaddr .

+1
Apr 20 '10 at 1:57
source share

Not very private, given that the calling code may return to (SomeStructSource *) . Also, what happens when you want to add another public member? You will have to break binary compatibility.

EDIT: I missed that it was in the .c file, but in fact nothing prevents the client from copying it, or perhaps even #include directly in the .c file.

0
Apr 20 '10 at 1:43
source share

My solution would be to provide only a prototype of the internal structure and then declare the definition in a .c file. It is very useful to show the C interface and use C ++.

.h:

 struct internal; struct foo { int public_field; struct internal *_internal; }; 

.c:

 struct internal { int private_field; // could be a C++ class }; 

Note. In this case, the variable must be a pointer, because the compiler cannot know the size of the internal structure.

0
Oct 10 '13 at 7:12
source share

Connected, although not completely hidden.

It is a conditional refusal of members.

Please note that this works for GCC / Clang, but MSVC and other compilers may also refuse, so it can be found in a more portable version.

If you create with rather strict warnings or warnings as errors, this at least avoids accidental use.

 // ========================================= // in somestruct.h #ifdef _IS_SOMESTRUCT_C # if defined(__GNUC__) # define HIDE_MEMBER __attribute__((deprecated)) # else # define HIDE_MEMBER /* no hiding! */ # endif #else # define HIDE_MEMBER #endif typedef struct { int _public_member; int _private_member HIDE_MEMBER; } SomeStruct; #undef HIDE_MEMBER // ========================================= // in somestruct.c #define _IS_SOMESTRUCT_C #include "somestruct.h" SomeStruct *SomeStruct_Create() { SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource)); p->_private_member = 42; return (SomeStruct *)p; } 
0
Apr 02
source share



All Articles