473,396 Members | 1,995 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

oh oh it's OO - calling encapsulated functions - question for the experts

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);
}


Nov 14 '05 #1
4 1715
Gibby Koldenhof wrote:
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.


C99:

#define CALL_METHOD(classvar,method,...) \
classvar.method(&classvar, __VA_ARGS__)

where method is a function pointer to be "initialised" by your
"constructor".
C89: Use another calling convention. Something along the lines of
CALL_METHOD_N() where N is the number of additional arguments is
easy to realise but maybe there is a more elegant solution out
there.

As these are macros, they are pretty fast.
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Nov 14 '05 #2
<snip>
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.


C99:

#define CALL_METHOD(classvar,method,...) \
classvar.method(&classvar, __VA_ARGS__)

where method is a function pointer to be "initialised" by your
"constructor".
C89: Use another calling convention. Something along the lines of
CALL_METHOD_N() where N is the number of additional arguments is
easy to realise but maybe there is a more elegant solution out
there.

As these are macros, they are pretty fast.


Cool! I didn't know of this C99 feature (I really need to read up on it) -
using var. args in C89's macro's is a no-no though, but the alternative
(less elegant) way would be to pass an array (or a string for that matter
but then parsing the args back in would take to long) - but assuming I'm
going to compile everything in C99 this still has a problem:

It breaks with the encapsulation, e.g. I can't use generic pointers to
objects ... or can I? (somehow) or maybe I'm missing something?

What I mean is: (compile with gcc test.c --std=c99 --pedantic)

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define CALL_METHOD(classvar,method,...) \
classvar.method(&classvar, __VA_ARGS__)

typedef struct {
void (*func)(void * class, ...);
} foo_t ;

static void blah(void * class, ...)
{
printf("blah!\n");
}

int main(void)
{
foo_t foo;

foo.func = blah;

CALL_METHOD(foo, func, 1, 2, 3);

/* what I would like but what (of course) doesn't work in this construct :
*/

if(1)
{
extern void * find_class(const char * name);

void * foobar = find_class("something");

CALL_METHOD(foobar, func, 1, 2, 3);
}

return 0;
}

Ergo, the code within the if(1) scope is what I would really like to do so
the definition of foo_t is invisible to the user program.

Thanks, More tips welcome!

Cheers,
Gibby Koldenhof

Nov 14 '05 #3
Gibby Koldenhof wrote:
<snip>
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.
C99:

#define CALL_METHOD(classvar,method,...) \
classvar.method(&classvar, __VA_ARGS__)

where method is a function pointer to be "initialised" by your
"constructor".
C89: Use another calling convention. Something along the lines of
CALL_METHOD_N() where N is the number of additional arguments is
easy to realise but maybe there is a more elegant solution out
there.

As these are macros, they are pretty fast.

Cool! I didn't know of this C99 feature (I really need to read up on it) -
using var. args in C89's macro's is a no-no though, but the alternative
(less elegant) way would be to pass an array (or a string for that matter
but then parsing the args back in would take to long) - but assuming I'm
going to compile everything in C99 this still has a problem:

It breaks with the encapsulation, e.g. I can't use generic pointers to
objects ... or can I? (somehow) or maybe I'm missing something?


Nope. However, passing generic pointers effectively does not get you
very far -- and not in a very fast way.
What I mean is: (compile with gcc test.c --std=c99 --pedantic)

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define CALL_METHOD(classvar,method,...) \
classvar.method(&classvar, __VA_ARGS__)

typedef struct {
void (*func)(void * class, ...);
} foo_t ;

static void blah(void * class, ...)
{
printf("blah!\n");
}

int main(void)
{
foo_t foo;

foo.func = blah;

CALL_METHOD(foo, func, 1, 2, 3);

/* what I would like but what (of course) doesn't work in this construct :
*/

if(1)
{
extern void * find_class(const char * name);

void * foobar = find_class("something");

CALL_METHOD(foobar, func, 1, 2, 3);
}

return 0;
}

Ergo, the code within the if(1) scope is what I would really like to do so
the definition of foo_t is invisible to the user program.

Thanks, More tips welcome!


Hints:
* Use OO. Inheritance helps.

Example:

typedef struct base {
unsigned long control;
Env_Item v;
int (*Construct)(struct base *);
int (*Destruct) (struct base *);
int (*Init) (struct base *, int argc, char **argv);
int (*Execute)(struct base *, int argc, char **argv);
int (*Display)(struct base *);
} Base;

Every derived structure now contains as first member a
variable of type Base. You can store information about the
type in control (bitwise). You are not passing around void *
but base *.
Env_Item belongs to some environment, e.g. an environment
directory, effectively storing the name etc.

The types are registered by either the control entry or
by prefixes and are "created" at the beginning (with storing
the necessary size). When something is to be constructed,
you find out the type by a prefix or suffix (or the complete name).

You can always call Construct, Destruct, Init, Execute and Display
when casting the derived types (ideally: type pointers) to Base(Base *).

e.g.
#define CALL_BAS_METHOD(classvar_p,method,...) \
IS_BASE((Base *)(classvar_p)) ? \
((Base *)(classvar_p))->method((Base *)(classvar_p), __VA_ARGS__) \
: /* Do something sensible */

(untested, just a sketch)

* Make it only as abstract as you need it.
Re-creating C++ with C is worse than using C++ in the first place.

* If you need "private" methods and data, you can hide them in standard
C ways. First idea: One file per class, private stuff hidden in a
"private" * member.
Extension to inheritance left as exercise to the reader. (Ugly)
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Nov 14 '05 #4
<snip>
It breaks with the encapsulation, e.g. I can't use generic pointers to
objects ... or can I? (somehow) or maybe I'm missing something?
Nope. However, passing generic pointers effectively does not get you
very far -- and not in a very fast way.
What I mean is: (compile with gcc test.c --std=c99 --pedantic)

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
<snip code>
/* what I would like but what (of course) doesn't work in this construct : */

if(1)
{
extern void * find_class(const char * name);

void * foobar = find_class("something");

CALL_METHOD(foobar, func, 1, 2, 3);
}

return 0;
}

Ergo, the code within the if(1) scope is what I would really like to do so the definition of foo_t is invisible to the user program.

Thanks, More tips welcome!


Hints:
* Use OO. Inheritance helps.

Example:

typedef struct base {
unsigned long control;
Env_Item v;
int (*Construct)(struct base *);
int (*Destruct) (struct base *);
int (*Init) (struct base *, int argc, char **argv);
int (*Execute)(struct base *, int argc, char **argv);
int (*Display)(struct base *);
} Base;


Okay, if I understand correctly these are the base methods. Agreed - I
already have those, they include stuff like contruct, destruct, cast, clone,
cout, cin, serialize, etc. Since these routines are generally not
speed-critical I've got them hidden and find them through a string.

I wouldn't mind having one 'Base' type though - but I don't think it helps
me - read below.
Every derived structure now contains as first member a
variable of type Base. You can store information about the
type in control (bitwise). You are not passing around void *
but base *.
Env_Item belongs to some environment, e.g. an environment
directory, effectively storing the name etc.
Okay, but I already do this - my objects are 'simply' encapsulated structs
(a tad more complex than that but anyway) which I can reference through a
void pointer, e.g I can do stuff like:

(just as an example)

void * pic = new("image", 320, 200, 32);

new * blah = clone(pic);

printf("%s\n", typeof(blah)); /* prints 'image' */

delete(pic);

the 'image' object is in turn made up of 'RGB' objects and those are made up
3 doubles. When I call:

void * a = new("int", 2);

call(blah, "divide", a);

it will cascade the call down to the 'double' object and and will figure out
that it will needs to divide all double objects which are contained in RGB
and in turn contained in image. Ergo, I only have to implement a divide
function for the most basic types and inherited types will figure out how to
find it and apply it to themselves. Of course to speed it up it would be
wise to implement the "divide" higher up the chain - but this is later work,
it already works as it is.
The types are registered by either the control entry or
by prefixes and are "created" at the beginning (with storing
the necessary size). When something is to be constructed,
you find out the type by a prefix or suffix (or the complete name).
Agreed, this is what new() also does, you can give it some string and it
will find out what to do, e.g.:

new("int:3", 2, 3, 4)
new("vector", 2.3, 1.2, 4.5);

or even

void * method(void * obj) { return 0; }

void * a = composite(new("int"), new("double"), new("callback", method));

calling the "divide" from the previous example will work on the composite
too - it will divide the int, the double and the result object from method -
at least if that object is dividable.

It's somewhat comparable to templates in C++ in that it doesn't matter what
type it is it will get resolved at run-time, the downside of course (as
opposed to templates) is that it's slower the nice thing is that it's very
dynamic.
You can always call Construct, Destruct, Init, Execute and Display
when casting the derived types (ideally: type pointers) to Base(Base *).

e.g.
#define CALL_BAS_METHOD(classvar_p,method,...) \
IS_BASE((Base *)(classvar_p)) ? \
((Base *)(classvar_p))->method((Base *)(classvar_p), __VA_ARGS__) \
: /* Do something sensible */
Okay, so I can call the basic 'methods' - yet I still cannot call functions
that are not part of the more traditional routines such as constr., destr.
etc. So a getpixel() is very specific to an image object yet I can't put it
in the struct - at least not without throwing away the abstraction .... or
am I missing your point here?

Of course I could make a image_get_pixel(void * obj, int x, int y) which
would only work on an image object but this is not really what I want -
partly because I'm going to duplicate code, and partly because it isn't
contained in the image class.

What would be nicer would be to have a 'read' method, so that:

void * obj1 = call(image, "read", x, y);
void * obj2 = call(volume, "read", x, y, z);

and even

void * file = new("io", "/usr/local/etc/blah.txt", "rb");
void * string = call(file, "read", 12);

would work.
(untested, just a sketch)

* Make it only as abstract as you need it.
Re-creating C++ with C is worse than using C++ in the first place.
True - and maybe I'm going overboard - maybe it's overkill and will bite me
later on because the whole damn system becomes way too slow ...

It's just annoying the hell out of me that the whole system is pretty
simple, straightforward and elegant yet that this last thing seems to be a
real problem.
* If you need "private" methods and data, you can hide them in standard
C ways. First idea: One file per class, private stuff hidden in a
"private" * member.
Extension to inheritance left as exercise to the reader. (Ugly)


What I do is just have a number of fields per method/data - e.g. it can be
protected, private, etc. And indeed each (major) function in a seperate
file. It's just like a nice generic plugin type-thing. The nice thing is
that it can generate all kinds of stuff by itself, like output XML, GUI
code, you can embed scripts into the classes, etc. And finding the classes
is quite fast since they are all in a binary tree that balances itself to
the # of generated objects per class. The big plus of inheritance is that I
can open any file and the system will figure out what type of object it
would need to represent, so an image file will generate an image object, a
MRI dataset will generate a volume file, etc.

It's like C++ only without all the surrounding crap :)

Thanks Michael for the tips!

Cheers,
Gibby Koldenhof
Nov 14 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: Denis | last post by:
Hello, I am working on a large business application written mostly with VC++ and MFC. What I'm trying to do is accessing my database with the existing functions in my c++ application using XML...
12
by: johny smith | last post by:
I am trying to figure out a way to print the address of what called a certain function once inside the function. I am assuming that this information is on the stack somewhere. But can someone...
2
by: Vidya Bhagwath | last post by:
Hello Experts, I am having C++ code. Now I am porting that C++ code to the Visual C#.NET. In my C++ code I used the windows functions such as "PurgeComm", "SetCommTimeouts"... etc. How can I port...
1
by: H.B. | last post by:
Hi, I need to make a function that can display data on my Managed C++ app and be called by an unmanaged C++ DLL. Something like : void Form1::Form1_Load(System::Object * sender,...
18
by: John Friedland | last post by:
My problem: I need to call (from C code) an arbitrary C library function, but I don't know until runtime what the function name is, how many parameters are required, and what the parameters are. I...
167
by: shaanxxx | last post by:
I started programming with c. Lot of projects are being done in C++. We have to move in THE C++. I read around 3 - 4 books (including Faqs, stroustrup) on c++. What i found in most of the book is...
45
by: dolphin | last post by:
Is it a good thing that program mix C and C++?
4
by: Tiago | last post by:
Hello Experts! I´m searching sites,tutorials or other, about Virtual Functions! Any person can hel me? Thank´s! Bye!
23
by: Stewart Berman | last post by:
I am trying to develop a wrapper class for the Windows API functions in Visual Studio 2008: GetOpenFileName GetSaveFileName I put together a starter class: using System; using...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.