I'm working on a school assignment and I'm having trouble understanding how to use an ADT. Basically, I need to implement a symbol table ADT that stores <key, value> pairs. The value associated with a key is an arbitrary object defined by the user that is passed to the ADT by a void pointer. I have the header file already included, I just need to make the source file for it.
The declaration I am stuck on is for the structure itself. It is a symbol table object pointerd to by a pointer of type SymTable_T. It should be able to make copies of <key, value> pairs inserted into it and these copies should be destroyed when deleted from the table or when the table itself is destroyed.
The implementation should employ a hash table that uses chaining to resolve collisions. I already am familiar with hashing, so there is no trouble there.
This is what I came up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "symTable.h"
#define DEFAULT_TABLE_SIZE 61
#define HASH_MULTIPLIER 65599
typedef struct SymTable *SymTable_T;
{
char *key;
int value;
struct SymTable *next; //linked list
};
Can someone point me in the right direction? Could someone explain to me the essentials of implementing an ADT? Thank you so much in advance!
The essence of an abstract data type is that client code has no insight whatsoever in the structure of values of that type - which also implies that the implementation of any functions dealing with such values is opaque.
In your example, it means that you do not define the struct in the header file but merely use a forward declaration. For traversal, you may also want to define an equally opaque iterator type, e.g.
struct symtable;
struct symtable_iterator;
...and then a collection of functions working with the table, e.g.
/* Create symtable, destroy it, insert values. */
void symtable_alloc(struct symtable **table);
void symtable_free(struct symtable *table);
void symtable_insert(struct symtable *table, const char *key, void *value);
/* Create symtable iterator, destroy it, access key/value. */
void symtable_iterator_alloc(struct symtable *table, struct symtable_iterator **it);
void symtable_iterator_free(struct symtable_iterator *it);
bool symtable_iterator_next(struct symtable_iterator **it);
const char *symtable_iterator_key(struct symtable_iterator *it);
void *symtable_iterator_value(struct symtable_iterator *it);
That's all you should put into the header file. In the implementation (.c) file, you would actually define the structure and its fields - but that code is hidden from clients.
You could use them like
struct symtable *table;
symtable_alloc(&table);
symtable_insert(table, "one", "eins");
symtable_insert(table, "two", "zwei");
symtable_insert(table, "three", "drei");
struct symtable_iterator *it;
symtable_iterator_alloc(table, &it);
while (symtable_iterator_next(&it)) {
printf("%s: %s\n", symtable_iterator_key(it), symtable_iterator_value(it));
}
symtable_iterator_free(it);
symtable_free(table);
Note how the set of functions clearly defines the API of the data structure, but the actual type is abstract - there's no information which gives away the implementation of the table, e.g. whether it's a linked list or a hash table or something else.
Related
I'm new to C programming and trying to write a simple example. Percisely I tried to abstract over a type implementation and simply use typedef and specify operations I can do with this type. I understand that at that point the type is incomplete, but I was intended to complete it into c-file, not header. Here is it:
test.h
#ifndef _TEST_H
#define _TEST_H
typedef my_type_t;
void init(my_type_t **t);
#endif //_TEST_H
test.c
#include <stdlib.h>
#include "test.h"
// implementation details
struct my_type_t{ //<---- completening my_type_t to be a struct with 1 field
int field;
};
void init(struct my_type_t **t){ //<--- error: conflicting type for init
*t = malloc(sizeof(struct my_type_t));
(*t) -> field = 42;
}
Is something like this possible? I wanted the implementation completely hide all the details about the actual type definition exposing only operations that can be done with it.
UPD: If we rewrite the c-file as follows:
#include <stdlib.h>
#include "test.h"
struct internal_my_type_definition_t{
int field;
};
void init(my_type_t **t){
struct internal_my_type_definition_t *st = malloc(sizeof(struct internal_my_type_definition_t));
st -> field = 42;
*t = st;
}
Is there any problem with such an implementation?
In your header, change
typedef my_type_t;
to
struct my_type_t;
It's a pretty common pattern. Just keep in mind that you'll need a function to allocate the struct on the heap and free it; one of the pieces of information you're hiding is the size of the struct, so the API consumer can really only deal with pointers to the struct not the struct itself.
The idiomatic API would be something like
struct my_type_t* my_type_new(void);
void my_type_free(struct my_type_t* self);
my_type_init would typically be used to initialize an already allocated instance, which is really only useful if you want to chain up to it in the *_new function of a subtype.
Edit: in response to your follow-up question, you could conceivably do something like this in your header:
#if !defined(MY_TYPE_NS)
# define MY_TYPE_NS struct
#endif
typedef MY_TYPE_NS my_type_t my_type;
my_type* my_type_new(void);
/* ... */
Then, in your *.c file:
#define MY_TYPE_NS union
#include "test.h"
union my_type_t {
/* ... */
};
my_type* my_type_new(void*) {
my_type* res = malloc(sizeof(my_type));
res->field = 42;
return res;
}
Which I find to be only slightly evil. I'd probably just use a union nested inside of the struct to avoid any surprises in the code.
The design pattern you are looking for is called "opaque type"/"opaque pointers".
You almost have it correctly, you just need to specify the type explicitly in the header:
typedef struct my_type_t my_type_t;
This is both a typedef and a forward declaration of an incomplete type, which is completed in your .c file and not visible to the caller.
Now the caller can declare pointers to this type, but not objects. They can't access struct members - we've achieved private encapsulation. You have to design your functions to always take a pointer type.
I decided to make static library realising doubly linked lists with functions. Its header file is like this now:
#ifndef LISTS
#define LISTS
#define LIST {0, NULL, NULL}
typedef struct node node;
typedef struct list {
unsigned int length;
node *beginning;
node *end;
} list;
void listAppend(list *list, int value);
int listPop(list *list);
char listRemove(list *list, int value);
void listPrint(list *list);
void listClear(list *list);
#endif
i.e. user should initialize list with list myList = LIST;.
Can I prevent list.length from casual changing by user in his code like list.length++?
Usually, if you want to hide implementation from client in pure C, you might use pointers to incomplete types. To do this, you put forward declaration of your struct in .h file and its full declaration in *.c file. You can't even add literal zero to a pointer to incomplete type, not to mention dereference it and/or alter some data.
Also, if you want to go against all odds and put your lists's header on stack, you might want to write a macro around alloca(), however I'm not sure how to calculate size of your struct in *.h file without having its declaration in scope. it's possible via extern const, but IMHO it's too complicated.
this error seems very easy to fix but i've been trying and have no clue.
So i have three files:
symtable.h:
typedef struct symbolTable *SymTable_T;
symtablelist.c:
#include "symtable.h"
struct Node{
char* key;
void* value;
struct Node* next;
};
struct symbolTable{
struct Node* head;
int length;
};
SymTable_T SymTable_new(void){
/* code */
}
And main.c:
#include "symtable.h"
int main(int argc, const char * argv[]) {
// insert code here...
SymTable_T emptyTable = SymTable_new();
emptyTable->length = 3; <------- ERROR
return 0;
}
I'm getting error: Incomplete definition of type "struct symbolTable"
Can anyone please give me a hint?
The reason i declare my struct in my source file is that i will have another implementation for the header file. so is there another way to fix my bug beside moving my struct declaration?
You can't access the members directly with an opaque pointer - if you keep the implementation in a separate source file, you'll have to access all the members via your interface, and not directly mess with the struct.
For instance, add this to symtable.h:
void SymTable_set_length(SymTable_T table, int len);
this to symtablelist.c:
void SymTable_set_length(SymTable_T table, int len)
{
table->length = len;
}
and in main.c change this:
emptyTable->length = 3;
to this:
SymTable_set_length(emptyTable, 3);
although in this specific case passing the length as an argument to SymTable_new() is an obviously superior solution. Even more superior is not letting the user set the length of a linked list data structure at all - the length is the number of items in it, and it is what it is. It would make no sense to, for instance, add three items to the list, and then allow main.c to set the length to 2. symtablelist.c can calculate and store the length privately, and main.c can find out what the length is, but it doesn't make much sense for main.c to be able to set the length directly. Indeed, the whole point of hiding the members of a struct behind an opaque pointer like this is precisely to prevent client code from being able to mess with the data like that and breaking the data structure's invariants in this manner.
If you want to access the members directly in main.c, then you have to have the struct definition visible, there is no alternative. This will mean either putting the struct definition in the header file (recommended) or duplicating it in main.c (highly unrecommended).
In typedef symbolTable *SymTable_T;, you refer to a non-existent type symbolTable. In C (unlike C++) the type is named struct symbolTable. (Note: the question has changed to fix this since answering it.)
There's a second problem. In main.c the code will need to be able to see the definition of struct symbolTable for you to be able to refer to fields of emptyTable. At the moment, the definition is hidden in a .c file... it should be moved to the header.
I have defined a simple C API that manages a doubly linked list with a Data type, defined at a header file, acompanied with its functions (in example: print_data, compare, etc.).
This way it is easy for me to use it easily into any project I need.
But what if I want to use this ADT into the same project but with different data?
The ADT is defined as this at its header file:
typedef struct DoublyLinkedList_ADT *dllistptr;
And at it source file there are these two structs:
struct DoublyLinkedListNode
{
dllnodeptr previous, next;
Data data;
};
struct DoublyLinkedList_ADT
{
dllnodeptr head, tail;
int size;
};
And at the data header file there is this:
typedef struct Data_type
{
int num;
}Data;
So that I can define Data into anything I want.
Is there any way to achieve this?
The only one that I've come up is to copy paste the code adding a "_OtherData" to all "Data" references. But this doesn't sound that good.
After reading some other questions, I've seen that there is an implementation that needs a void* definintion of Data and each time I use it, I must cast to the right data type.
Is there anything easier/faster?
typedef struct node{
int term;
struct node *next;
}node;
typedef void(*PTR )(void *);
typedef void(*PTR1)(void *,int,int);
typedef int(*PTR2)(void *,int);
typedef void(*PTR3)(void *,int);
typedef void(*PTR4)(void *,void *,void *);
typedef struct list{
node *front,*rear;
PTR3 INSERT;
PTR *MANY;
PTR DISPLAY,SORT,READ;
PTR4 MERGE;
}list;
void constructor(list **S)
{
(*S)=calloc(1,sizeof(list));
(*S)->front=(*S)->rear=NULL;
(*S)->INSERT=push_with_value;
(*S)->READ=read;
(*S)->SORT=sort;
(*S)->DISPLAY=display;
(*S)->MERGE=merger;
(*S)->MANY=calloc(2,sizeof(PTR));
(*S)->MANY[1]=read;
}
int main()
{
list *S1,*S2,*S3;
constructor(&S1);
constructor(&S2);
constructor(&S3);
S1->MANY[1](S1);
S1->SORT(S1);
S1->DISPLAY(S1);
return 0;
}
The void * parameter in all such functions gets typecast to list * inside the function.
Is there any way through which I can call S1->READIT; by changing the MANY[1] to another name like READ_IT;?
I intend to create a common header file, so that I can use it for all my programs.
Since I don't know how many function pointers I will need I intend to create a dynamic array of each function pointer type.
typedef struct list{
node *front,*rear;
PTR3 INSERT;
PTR READIT;
PTR DISPLAY,SORT,READ;
PTR4 MERGE;
}list;
...
(*S)->READIT = read;
...
S1->READIT(S1);
Take a look at the Linux kernel implementation of (doubly linked) lists, as defined here (and following/referenced files). They are used all over the place. Most of the manipulation is done in macros to e.g. run an operation on all nodes of the list.
If what you are trying to define is getting too complicated, step back and look for simpler alternatives. Don't generalize beforehand; if the generalization isn't used it is a waste; if something (slightly) different is later needed, it is a poor match that requires workarounds or even reimplementation.
Take a look at the interfaces exposed by the C++ STL list, those folks have thought long and hard on the matter (in a different setting, though).
Or just bite the bullet and use C++ if you want full-fledged OOP.