I want to give a simple example about creating abstract data types in Pelles C.
This example could be very much expanded. But I tried to leave it as simple as possible to show the principle case.
The user should see the ADT as a black box. He has not to know anything about internal structures nor the implementation of the ADT. All he knows and should use is the name of the ADT and its exported functions.
Therefore the implementor of an ADT writes an header file, the user may include.
In this example we will build a string set. We declare the ADT type name as:
typedef struct _STRSET *STRSET;
All the user can see is, that STRSET
is a pointer to an unknown struct _STRSET
.
We will provide a set of access functions. First there are two functions who serve as cunstructors and destructores of the ADT. StrSetInit()
creates an emty string set and StrSetFree()
frees the string set.
STRSET StrSetInit(void); void StrSetFree(STRSET ss);
We further provide some functions to insert a string in the ADT: StrSetInsert()
and to retrieve its address: StrSetGetPtr()
.
We also add some utility functions. StrSetSize()
returns the number of strings in the set and StrSetClear()
deletes all the strings in the set, but not the string set itself.
void StrSetClear(STRSET ss); int StrSetSize(STRSET ss);
The whole header file strset.h
is:
#ifndef STRSET_H #define STRSET_H typedef struct _STRSET *STRSET; STRSET StrSetInit(void); void StrSetClear(STRSET ss); void StrSetFree(STRSET ss); int StrSetInsert(STRSET ss, int nPosition, char *s); int StrSetSize(STRSET ss); char* StrSetGetPtr(STRSET ss, int nPosition); #endif // STRSET_H
The user now is able to use the ADT:
#include <stdio.h> #include <string.h> #include "strset.h" char *input(char *b) { printf("> "); scanf("%s", b); return b; } int main(int argc, char *argv[]) { int i=0; char buf[100] = ""; // declare a temporary buffer STRSET S = StrSetInit(); // declare the string set while (strcmp(input(buf), "quit")) StrSetInsert(S, i++, buf); // insert the string in the set for (i=0; i<StrSetSize(S); i++) // loop over all the strings in the set puts(StrSetGetPtr(S, i)); // get and display each string StrSetFree(S); // free the string set return 0; }
Ok! That is all from the users point of view!
However, the implementor has to create the ADT. He does this in a separate module strset.c
which could be compiled to a static library, to hit its content.
To perform this task we will use the DPA functions in COMCTL32.lib
.
Here is strset.c
:
#include <windows.h> #include <commctrl.h> #include "strset.h" #pragma comment(lib, "comctl32.lib") struct _STRSET { HDPA h; }; STRSET StrSetInit(void) { STRSET ss = malloc(sizeof(struct _STRSET)); if (ss) ss->h = DPA_Create(10); return ss; } void StrSetClear(STRSET ss) { if (ss) for (int i=0; i<DPA_GetPtrCount(ss->h); i++) { free(DPA_GetPtr(ss->h, i)); DPA_DeletePtr(ss->h, i); } } void StrSetFree(STRSET ss) { if (ss) { StrSetClear(ss); free(ss); } } int StrSetInsert(STRSET ss, int nPosition, char *s) { char *b = NULL; if (ss && s) { b = malloc(strlen(s)+1); strcpy(b,s); return DPA_InsertPtr(ss->h, nPosition, b); } return -1; } int StrSetSize(STRSET ss) { return DPA_GetPtrCount(ss->h); } char *StrSetGetPtr(STRSET ss, int nPosition) { return DPA_GetPtr(ss->h, nPosition); }
As you can see, the secret struct
struct _STRSET { HDPA h; };
is very simple in this case. It only holds a DPA handle.
Because the COMCTL32.LIB
is needed, we instruct the linker to link against it, with the #pragma
directive. So our user has not to know what additional libraries are used.
All our ADT functions with the exception of StrSetInit()
use a first parameter of type STRSET
. And all this access functions have to verify that this parameter is valid (not NULL
).
If you have any questions? Use the Pelles C forum to ask.