I have a module whose implementation I want to hide from its clients.
I chose to declare an opaque type which is actually a pointer to structure to be defined only in the implementation.
It all is working fine, except that I can assign the zero value to a variable of this type, which I want to avoid.
Here's an example in C.
header file foo.h
/* foo.h */
typedef struct foo *foo_t; /* <- sorry this was obviously flawed, the '*' was missing */
extern void foo_create( foo_t *t );
extern void foo_destroy( foo_t *t );
extern void foo_tile( foo_t x );
implementation file foo.c
/* foo.c */
#include <stdlib.h>
#include "foo.h"
struct foo {
int some_member;
};
void foo_create( foo_t *t )
{
if ( *t==0 ) {
*t = malloc( sizeof(struct foo) );
}
}
void foo_destroy( foo_t *t )
{
if ( *t!=0 ) {
free(*t);
*t = 0;
}
}
void foo_tile( foo_t t )
{
t->some_member++;
}
And now here is an example client that uses the module: bar.c:
#include "foo.h"
int main( int argc , char **argv )
{
foo_t toe;
foo_create( &toe );
toe = 0; /* <-- How to make the compiler (gcc) refuse this? */
toe = 1; /* <--- the compiler rejects this YAY!! */
}
The opaque type is actually a pointer to a dynamically allocated structure; If I assign the value 0 to it I incur a memory leak which could be avoided If the compiler denied the assignment of 0 to this opaque pointer.
Assigning non-null values to the pointer isn't accepted by the compiler, so I supose that with a little more effort the same could be achieved for zero value.
Is it possible to disable this assignment? How can I achieve that? If using some bit of C++ or gcc specific constructs is needed, I'll be ok with that, although a pure C solution would be nice.
Thanks in advance.
I'm not sure you can do it that way. The compiler would fail in main():
toe = 0; /* <-- How to make the compiler (gcc) refuse this? */
It would also fail in foo_destroy():
void foo_destroy( foo_t *t )
{
if ( *t!=0 ) {
free(*t);
*t = 0; /* compiler refuses this also */
}
}
You might try returning the allocated memory from foo_create() directly rather than passing in the foo_t parameter (emulating a constructor):
extern foo_t * foo_create( void );
foo_t * foo_create( void )
{
foo_t * t;
t = malloc( sizeof(struct foo) );
return(t);
}
int main( int argc , char **argv )
{
foo_t * toe;
toe = foo_create();
toe = 0; /* Clearly a memory leak via reassignment */
...
}