Hiya,
I'm setting up some code in the spirit of Design Patterns, OOP, etc. All
nice and well, it handles pretty much all OO style things and serves my
purposes well. There's one last final question remaining: How to properly,
simple and eleganty implement functions. The functions are encapsulated in
the objects, the objects themselves are stored in a binary tree and objects
contain 'methods' (functions) along with data. I don't want to make publicly
available routines since the encapsulation is then gone.
So practically, how do I call a function that is encapsulated in some
structure without using that structure directly (e.g. only through void *)?
The code below are four examples of how to fix this, only example2 is kinda
okay. The biggest problem however is that it is very slow.
If anybody has got an idea of how to do this better (or even different) I'd
love to hear it because I'm kinda stuck with this.
P.s. I don't want to use C++ - it breaks more than fixes in this code base.
This is all in the spirit of stuff like
http://www.planetpdf.com/codecuts/pdfs/ooc.pdf and related techniques and
docs. The GTK system implements something simalar (but in a terrible way
IMHO).
Any tips welcome!
Cheers,
Gibby Koldenhof
/*--------------------------------------------------------------------------
----------*/
/* compile with -ansi -pedantic -Wall */
/*--------------------------------------------------------------------------
----------*/
/* example 1, using func. pointer */
typedef void (*method_t)(void);
extern method_t find_method(const char * name);
void foo1(void)
{
int (*getpixel)(int x, int y) = (int (*)(int,int))find_method("getpixel");
/* this is terrible, we need to cast a heck of a lot for each function
we're gonna call, this clutters up the code and invites trouble if the cast
is wrong */
int pixel = getpixel(1, 2);
}
/*--------------------------------------------------------------------------
----------*/
/* example 2, using var. args */
void do_method(const char * name, ...)
{
/* find method through name and parse arg list */
}
void foo2(void)
{
extern void * new(const char *, ...);
int pixel ;
do_method("getpixel", 1, 2, &pixel);
/* two problems : (1) it is *SLOW*, e.g. parsing arg list and finding
'getpixel' method in the tree take a long time. The latter could be solved
by caching the found pointer.
(2) we can't check var. args on correctness, e.g. if a double should be
passed and the user passes an int it buggers up */
/* possible solution, only pass objects, so we can check the type */
if(1)
{
void * x = new("int", 1);
void * y = new("int", 2);
void * result = new("int", 0);
void * method = new("method::getpixel");
/* this is more elegant but adds tons of indirection, ergo TERRIBLY slow
.... ;-( */
do_method(method, x, y, result);
}
}
/*--------------------------------------------------------------------------
----------*/
/* example 3, using var. args */
typedef struct {
/* table with all possible methods ... ugh */
int (*get_1)(int a);
int (*get_2)(int a, int b);
int (*get_3)(int a, int b, int c);
} methods_t ;
static methods_t methods;
#define some_get(x) methods.get_##x
void foo3(void)
{
/* Yikes, this is terrible! we can only use a real integer (due to
pasting) and the syntax of the thing is terrible */
int pixel = some_get(2)(1,2);
}
/*--------------------------------------------------------------------------
---------*/
/* example 4, using casts and stuff */
typedef struct {
int xsize, ysize, bpp ;
void * data ;
int (*getpixel)(int x, int y);
} image_t ;
void * new(const char * name)
{
static image_t image ;
return &image ;
}
void foo4(void)
{
void * picture = new("image");
/* yuck - there goes the encapsulation, and this is terrible to call -
even if we fix it with a macro */
int pixel = ((image_t *)picture)->getpixel(1,2);
}