Should I use
Because I'm not one of those object-fetishists,
but also didn't wanted to use a module for it,
I decided for using a closure.
BTW, when thinking abour objects, well, in OCaml I could have used
anonymous objects, and possibly this would be the way I would go it
the next time, because this would help me avoiding the unneccessary definition of a class...
...I just only needed one counter at that script.
Some days ago I came along some C-programming and had some leisure time
to do something, I would like to explore.
So I thought about: how would I do such a counter in C?
What I needed was a way to create that counter easily,
as well as passing it around. So I thought, it possibly would
make sense to do it in an object-like datastructure.
Something like an object... but... in C you have none provided by the
language itself.
But there are ways to achieve similar things: one can use a structure,
that holds pointers to the data as well as to the functions/operations (methods)
that are used together with that data.
The declarations for the counter,
counter.h:
typedef struct _counter * COUNTER_P; typedef struct _counter { int val; /* counter-value */ int (*get) ( COUNTER_P ); /* function to get the value of the counter */ void (*clear)( COUNTER_P ); /* function to clear the value of the counter */ void (*incr) ( COUNTER_P ); /* function to increment the value of the counter */ void (*decr) ( COUNTER_P ); /* function to decrement the value of the counter */ void (*prt) ( COUNTER_P ); /* function to print the value of the counter */ char* text; /* text to print before the value */ } COUNTER; /* ----------------------------------------------------------- */ /* prototypes for the visible constructor/destructor functions */ /* ----------------------------------------------------------- */ COUNTER* new_counter( char* txt ); void free_counter( COUNTER* ctrp );
Now look at the implementation of the counter-stuff
counter.c:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "counter.h" /* ------------------------------------- */ /* get the value of the counter via the */ /* returnvalue from this function. */ /* ------------------------------------- */ static int get( COUNTER_P ctrp ) { return ctrp->val; } /* ---------------------------------------------- */ /* clear the counter (means: setting value to 0 ) */ /* ---------------------------------------------- */ static void clear( COUNTER_P ctrp ) { ctrp->val = 0; return; } /* ---------------------------- */ /* increment the counter by one */ /* ---------------------------- */ static void incr( COUNTER_P ctrp ) { ++ctrp->val; return; } /* ---------------------------- */ /* decrement the counter by one */ /* ---------------------------- */ static void decr( COUNTER_P ctrp ) { --ctrp->val; return; } /* --------------------------------------------------- */ /* Print the value of the counter, followed by "\n" . */ /* if there is a text assigned to ctrp->text, print it */ /* before the counter-value is printed. */ /* --------------------------------------------------- */ static void prt( COUNTER_P ctrp ) { if( ctrp->text ) printf("%s", ctrp->text ); printf("%d\n", ctrp->val); return; } /* ----------------------------------------------------- */ /* default-values are set by this template (convenience) */ /* ----------------------------------------------------- */ static COUNTER vorlage = { 0, get, clear, incr, decr, prt }; /* ======================================= */ /* Create a new counter. */ /* ======================================= */ /* This function is visible to the outside */ /* ======================================= */ COUNTER* new_counter( char* txt ) { COUNTER* counter = calloc( 1, sizeof(COUNTER) ); if( counter == NULL ) return NULL; memcpy( counter, &vorlage, sizeof(COUNTER) ); if( txt != NULL ) counter->text = strdup(txt); return counter; } /* ======================================= */ /* Delete a counter. */ /* ======================================= */ /* This function is visible to the outside */ /* ======================================= */ void free_counter( COUNTER* ctrp ) { if( ctrp == NULL ) return; if( ctrp->text != NULL ) free( ctrp->text ); free( ctrp ); return; }
And now an example of usage, which I used to play around,
main.c:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "counter.h" /* ----------------------------------------------- */ /* this function is used as a bew counter-function */ /* it can be used to replace the default-printer */ /* ----------------------------------------------- */ void my_counter_printer( COUNTER_P ctrp ) { printf("Ich bin der neue Counter-Printer.\tValue: %d\n", ctrp->val ); return; } /* ------------------------------------------------------------------ */ /* Action passed around: we clear the counter from "somewehere else", */ /* showing that we can pass around the functionality via the pointer. */ /* ------------------------------------------------------------------ */ void sub_macht_counter_platt( COUNTER_P ctrp ) { ctrp->clear(ctrp); return; } /* ======================================== */ /* demonstrate how to use the counter-stuff */ /* ======================================== */ int main() { COUNTER* ctr_a = NULL; COUNTER* ctr_b = NULL; ctr_a = new_counter("Hello "); if( ctr_a == NULL ) abort(); ctr_a->incr(ctr_a); ctr_a->prt(ctr_a); ctr_b = new_counter("Counter B: "); if( ctr_b == NULL ) abort(); ctr_b->incr(ctr_b); ctr_b->prt(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_a->prt(ctr_a); ctr_b->prt(ctr_b); printf("----------------------------------\n"); ctr_b->prt = my_counter_printer; ctr_a->prt(ctr_a); ctr_b->prt(ctr_b); printf("----------------------------------\n"); ctr_a->prt(ctr_a); ctr_b->prt(ctr_b); sub_macht_counter_platt( ctr_b ); ctr_b->prt(ctr_b); free_counter( ctr_a ); ctr_a = NULL; free_counter( ctr_b ); ctr_b = NULL; return 0; }
OK, now I had converted the OCaml-closure to an ANSI-C Object-like thingy.
This was a nice pastime.
But what now?
Now I have such a nice counter,
which also can be passed around, but who would have a need of it?
Well, it's a convenient way of using a counter.
This might be nice for some cases of programming.
But: why not using a function that has a static int-variable and works on it?
Ok, if one needs only one such counter (as was in my first need), this would
be completely ok; but only, if reentrance would not be needed.
Using such a structure-based approach brings the advantage of re-entrance-ability,
as well as the possibility to have more than one counter available.
Also calling it from seomwhere in your code is easy, because it's
object-like and needs no functions from somewhere else but have all it needs
in it's own bag.
When we want to pass some counters around, this is fine.
When we pass one counter around to many places, each will
be worked on seperately and no problems occur.
But the one big advantage of such an encapsulation is,
to have the possibility not only to spread it over
the code, which uses it... it is in principially also possible
to spread it timely-parallelized, by using threads for example.
And especially here, this approach is quite useful, because
ONE poointer can be passed to many places and work at many
times, but the value in use (the counter-value) is one value,
that all passed references of it are pointing to.
This is quite useful, when for example thinking on Web and I/O.
But to be sure that this really works, one has to protect the data (with a mutex),
as is necessary in a multithreaded application.
If you would not protect the data, I'm sure, you could not count on your counter.
So now we go that way: Making Mor Out Of It, by making it multithreaded.
Threaded version of counter.h
typedef struct _counter * COUNTER_P; typedef struct _counter { int val; /* counter-value */ void (*get) ( COUNTER_P, int* res ); /* function to get the value of the counter */ void (*clear)( COUNTER_P ); /* function to clear the value of the counter */ void (*incr) ( COUNTER_P ); /* function to increment the value of the counter */ void (*decr) ( COUNTER_P ); /* function to decrement the value of the counter */ void (*prt) ( COUNTER_P ); /* function to print the value of the counter */ char* text; /* a text that will be printed before the counter-value */ pthread_mutex_t* mutex_p; /* this mutex is used for all operations on the data */ } COUNTER; /* ----------------------------------------------------------- */ /* prototypes for the visible constructor/destructor functions */ /* ----------------------------------------------------------- */ COUNTER* new_counter( char* txt ); void free_counter( COUNTER* ctrp );
Threaded version of counter.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include "counter.h" /* --------------------------------------- */ /* get the value of the counter. */ /* The value will be passed to the memory */ /* location that is pointed to by "result" */ /* --------------------------------------- */ static void get( COUNTER_P ctrp, int* result ) { if( result == NULL ) return; pthread_mutex_lock( ctrp->mutex_p ); *result = ctrp->val; pthread_mutex_unlock( ctrp->mutex_p ); return; } /* ---------------------------------------------- */ /* clear the counter (means: setting value to 0 ) */ /* ---------------------------------------------- */ static void clear( COUNTER_P ctrp ) { pthread_mutex_lock( ctrp->mutex_p ); ctrp->val = 0; pthread_mutex_unlock( ctrp->mutex_p ); return; } /* ---------------------------- */ /* increment the counter by one */ /* ---------------------------- */ static void incr( COUNTER_P ctrp ) { pthread_mutex_lock( ctrp->mutex_p ); ++ctrp->val; pthread_mutex_unlock( ctrp->mutex_p ); return; } /* ---------------------------- */ /* decrement the counter by one */ /* ---------------------------- */ static void decr( COUNTER_P ctrp ) { pthread_mutex_lock( ctrp->mutex_p ); --ctrp->val; pthread_mutex_unlock( ctrp->mutex_p ); return; } /* --------------------------------------------------- */ /* Print the value of the counter, followed by "\n" . */ /* if there is a text assigned to ctrp->text, print it */ /* before the counter-value is printed. */ /* --------------------------------------------------- */ static void prt( COUNTER_P ctrp ) { pthread_mutex_lock( ctrp->mutex_p ); if( ctrp->text ) printf("%s", ctrp->text ); printf("%d\n", ctrp->val); pthread_mutex_unlock( ctrp->mutex_p ); return; } /* ----------------------------------------------------- */ /* default-values are set by this template (convenience) */ /* ----------------------------------------------------- */ static COUNTER vorlage = { 0, get, clear, incr, decr, prt }; /* ======================================= */ /* Create a new counter. */ /* ======================================= */ /* This function is visible to the outside */ /* ======================================= */ COUNTER* new_counter( char* txt ) { COUNTER* counter = calloc( 1, sizeof(COUNTER) ); int retval = 0; if( counter == NULL ) return NULL; memcpy( counter, &vorlage, sizeof(COUNTER) ); if( txt != NULL ) counter->text = strdup(txt); counter->mutex_p = calloc( 1, sizeof(pthread_mutex_t) ); if( counter == NULL ) { free_counter( counter ); /* free's all already calloc't data */ return( NULL ); } retval = pthread_mutex_init( counter->mutex_p, NULL); if( retval ) { free_counter( counter ); /* free's all already calloc't data */ return NULL; } return counter; } /* ======================================= */ /* Delete a counter. */ /* ======================================= */ /* This function is visible to the outside */ /* ======================================= */ void free_counter( COUNTER* ctrp ) { if( ctrp == NULL ) return; if( ctrp->text != NULL ) free( ctrp->text ); if( ctrp->mutex_p != NULL ) { pthread_mutex_destroy( ctrp->mutex_p ); free( ctrp->mutex_p ); } free( ctrp ); return; }
Threaded version of main.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include "counter.h" /* -------------------------------------------- */ /* We want to have more then one thread. */ /* here we define the number of threads to use */ /* and provide an array of thread-id's, */ /* to be filled later. */ /* -------------------------------------------- */ #define NUM_THREADS 5 pthread_t thread_id_array[ NUM_THREADS ]; /* ----------------------------------------------- */ /* this function is used as a bew counter-function */ /* it can be used to replace the default-printer */ /* ----------------------------------------------- */ void my_counter_printer( COUNTER_P ctrp ) { printf("Ich bin der neue Counter-Printer.\tValue: %d\n", ctrp->val ); return; } /* ------------------------------------------------------------------ */ /* Action passed around: we clear the counter from "somewehere else", */ /* showing that we can pass around the functionality via the pointer. */ /* ------------------------------------------------------------------ */ void sub_macht_counter_platt( COUNTER_P ctrp ) { ctrp->clear(ctrp); return; } /* ---------------------------------------- */ /* A function that can be used by a thread. */ /* ---------------------------------------- */ /* There is a random sleep-time, so that */ /* the demonstration will not become too */ /* boring. */ /* ---------------------------------------- */ void* use_counter( void* counter_p ) { int loop = 0; int sleeptime = 0; COUNTER_P counter = counter_p; pthread_t self = pthread_self(); sleeptime = rand()/(RAND_MAX / 4); printf("myself: %d | sleeptime: %d | ctr_a: %p\n", self, sleeptime, counter ); for( loop = 0; loop < 5; loop++ ) { printf("myself: %d | loop: %d\n", self, loop ); counter->prt(counter); counter->incr(counter); printf("myself: %d\n", self ); sleep(sleeptime); } return NULL; } /* ======================================== */ /* demonstrate how to use the counter-stuff */ /* ======================================== */ int main() { pthread_t thread; COUNTER* ctr_a = NULL; COUNTER* ctr_b = NULL; COUNTER* ctr_dummy = NULL; size_t idx = 0; int retval = 0; pthread_mutex_t mutex; /* creating counters */ /* ================= */ ctr_a = new_counter("Hello "); if( ctr_a == NULL ) abort(); ctr_a->incr(ctr_a); ctr_a->prt(ctr_a); ctr_b = new_counter("Counter B: "); if( ctr_b == NULL ) abort(); /* doing some increments */ /* --------------------- */ ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); ctr_b->incr(ctr_b); /* ----------------------------------------------------- */ /* create threads that use function use_counter and have */ /* counter ctr_a as argument */ /* ----------------------------------------------------- */ for( idx = 0; idx < NUM_THREADS; idx++ ) { printf("ctr_a: %p\n", ctr_a ); retval = pthread_create( &thread_id_array[idx], NULL, use_counter, ctr_a ); if( retval ) abort(); sub_macht_counter_platt( ctr_a ); /* ugly man ;-) */ printf("%d, %d\n", retval, thread_id_array[idx]); } sub_macht_counter_platt( ctr_a ); sleep(2); /* sleeping for a short while */ /* doing incrementations and printing values of counters */ /* ----------------------------------------------------- */ for( idx = 0; idx < 5; idx++ ) { ctr_b->incr(ctr_a); ctr_b->incr(ctr_b); ctr_b->prt(ctr_b); ctr_b->prt(ctr_a); sleep(1); } /* changing the printer of counter A */ /* --------------------------------- */ pthread_mutex_lock( ctr_a->mutex_p ); ctr_a->prt = my_counter_printer; pthread_mutex_unlock( ctr_a->mutex_p ); sleep(2); /* sleep again */ /* again doing incrementations and printing values of counters */ /* ----------------------------------------------------------- */ for( idx = 0; idx < 5; idx++ ) { ctr_b->incr(ctr_a); ctr_b->incr(ctr_b); ctr_b->prt(ctr_b); ctr_b->prt(ctr_a); sleep(1); } /* wait for all created threads to be finished */ /* ------------------------------------------- */ for( idx = 0; idx < NUM_THREADS; idx++ ) { pthread_join( thread_id_array[idx], NULL ); } /* OK, now, when no one uses the counters anymore, we can free them */ /* ---------------------------------------------------------------- */ free_counter( ctr_a ); ctr_a = NULL; free_counter( ctr_b ); ctr_a = NULL; return 0; }