I am designing an iterator interface for my hashmap data structure. The current design looks like this:
// map.h
typedef struct map_iterator map_iterator;
// map.c
struct map_iterator
{
// Implementation details
};
// client.c
map *m = map_new();
map_iterator *it = map_iterator_new(m);
void *key, *value;
while (map_iterator_next(it, &key, &value)) {
// Use key, value
}
map_iterator_free(it);
However, this requires a heap allocation for the iterator object, and the client must remember to free the iterator when they're done. If I make map_iterator_new
return the iterator on the stack, the code looks like this:
// map.h
typedef struct map_iterator
{
// Implementation details
};
// client.c
map *m = map_new();
map_iterator it = map_iterator_new(m);
void *key, *value;
while (map_iterator_next(&it, &key, &value)) {
// Use key, value
}
However, this requires that I provide the definition of the map_iterator
struct to client code (otherwise I get an incomplete type error). I would like to hide this definition and provide only the declaration.
Is there any way of achieving this? Essentially, I'm looking for a way to tell the client code "this struct takes up X bytes so you can allocate it on the stack, but I'm not telling you how to access its members".
Edit: Standard C only, please! (no compiler extensions/platform-specific functions)
Use serialization.
You are always allowed to copy an object of the T to an array of unsigned char and then back to an object of type T. That object will be the same as the original object. T can be any object type, and this can be done with automatic storage duration (stack):
int value = 568;
unsigned char store[sizeof( int )];
memcpy( store , &value , sizeof( int ) );
int other;
memcpy( &other , store , sizeof( int ) );
assert( other == value ),
This can be (ab)used to hide the implementation from the user. Define two struct, the one that defines the actual implementation and is not visible to the user, and one that is visible and contains only an array of unsigned char of appropriate size.
implementation.c
#include "implementation.h"
struct invisible
{
int element1;
char element2
float element3;
void** element4;
};
_Static_assert( sizeof( struct invisible ) <= VISIBLE_SIZE );
struct visible New( void )
{
struct invisible i = { 1 , '2' , 3.0F , NULL };
struct visible v = { 0 };
memcpy( &v , &i , sizeof(i) );
return v;
}
void Next( struct visible* v )
{
struct invisible i = { 0 };
memcpy( &i , &v , sizeof(i) );
i.element1++; //make some changes
const int next = i.element;
memcpy( &v , &i , sizeof(i) );
return next;
}
implementation.h
#define VISIBLE_SIZE 24 //make sure it greater or equal to the size of struct invisible
struct visible
{
unsigned char[VISIBLE_SIZE];
};
struct visible New( void );
int Next( struct visible* v );
user.c
#include "implementation.h"
void Func( void )
{
struct visible v = New();
while( 1 )
{
const int next = Next( &v );
if( next == 10 )
{
break;
}
printf( "%d\n" , next );
}
}
This example only touches the member element1
. In a real implementation, you can modify the invisible struct freely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With