About void pointers.
You have probably touched void pointers already. When calling functions
like `malloc' and `free', these handles (returns and take as argument respectively)
a pointer to some data object. Let us look at free for a while. It takes
a pointer as its single argument, so obviously type of the parameter must
be "pointer to something". Recall the usage of free - some examples:
(1)
struct my_type *p;
. . . /* allocation and usage */
free(p);
(2)
int *p;
. . . /* allocation and usage */
free(p);
free seems to be expected to accept "pointer to int" sometimes and
sometimes "pointer to some other type" (e.g. a user-defined type). Looking
at the prototype in our text-book we find the solution to the mystery:
void free(void *ptr);
So free takes "a pointer to void". A pointer variable of type
void can be thought of as a generic pointer that is of no use until
the value of it is either casted to a certain type, or assigned to another
pointer variable with a certain type (which, if using strictly ANSI, requires
the value of the former variable to be casted).
The value pointed to by a void pointer is not possible to access directly.
Suppose we want to know how a function prototyped like free looks inside
(and we want to, because we want to use CGUI!). Also suppose we have defined
a type named `my_type'. Example:
void my_func(void *data)
{
my_type *p = data;
/* here goes the code that uses p */
}
. . .
void another_func(void)
{
my_type *p;
p = malloc(sizeof(my_type));
/* give the data object that p points to some value
*/
my_func(p),
. . .
}
The above should normally have been written this way:
void my_func(my_type *p)
{
/* here goes the code that uses p */
}
. . .
void another_func(void)
{
my_type *p;
p = malloc(sizeof(my_type));
/* give the data object that p points to some value
*/
my_func(p),
. . .
}
The two examples are functionally equivalent. In the first one the
pointer value will be temporarily stored into the `void *' parameter. So
why use void pointers then? If there is no more than the above code, it would have
been no meaning to use it, but in the case of `free' it definitely has a meaning,
and we shall see that it has a meaning when using CGUI as well. We must
pass data-pointers to some CGUI-fuctions, where the type of the pointer
may alter from time to time.
About pointers to functions
You know about pointers to data. A pointer variable is a named memory
location which value is assumed to be the address of some memory location,
e.g. some other variable or some dynamically allocated memory. Probably
you also know that the program code (i.e. the machine instructions that the
compiler generated from the code that you have written) is located somewhere in
the memory - if you didn't then you know it now.
Therefore it should not be too hard to belief that a pointer
variable can be assigned a value such that it points to the beginning of
the code. To use this knowledge we need to learn some new things:
- Calling a function that a pointer points to, is quite simple:
suppose:
fp = free;
we can then call
fp(data);
with the same effect as
free(data);
About pointers to functions in conjunction with void pointers used
in CGUI.
To make it convenient for you, CGUI will call any of your functions
when needed, doing lots of tedious work for you until time has come. To
know which of your functions to be called and for what reason, you have
to pass pointers to functions (i.e. passing the name of a function you
have written, and that you want to be called) to some of the CGUI functions.
In most of these cases you may pass a void-pointer too. CGUI will then
remember them both and call the function when appropriate. Example:
void my_okfunc(void *data)
{
my_type *p = data;
/* do whatever needed for the case of a click on the
"Do"-button */
}
. . .
my_type *my_data;
. . .
my_data = malloc(sizeof(my_type));
. . .
AddButton(TOPLEFT, "Do", my_okfunc, my_data);
. . .
when the button is clicked by the user, CGUI will perform a call which
has the effect of
my_func(my_data);
That's it!
Just another example:
Suppose you somewhere in your code want to make the call:
my_func(my_data);
In CGUI you can get the same thing done by calling GenEvent like:
GenEvent(my_func, my_data, 0, 0);
with the difference that maybe some other event will be processed before
the time comes to my_func.
All this provided that ProcessEvents(); has been called. Now look at all the examples in CGUI and you will find that they confirm the text above. You will soon think that the style of programming imposed by these function pointers and the event queue, makes your code more modular, and thus more readable and easier to maintain.
Words of wisdom
When you have found out that void-pointers are quite easy and useful,
you maybe starts to figure out of a number of places in your code where
to introduce them. Don't do it if not necessary! Void pointers is necessary
sometimes, but may also introduce tricky memory errors since using them
don't take advantage of the type-checking provided by the compiler.