Polymorphism in C

M

Mike

Attached below is a file that implements polymorphism in C. It's
loosly based on the way Miro Samek describes it
(http://www.embedded.com/97/fe29712.htm). I found the article a
little confusing so I made some changes and came up with wat's
below. It appears to work, but I've written plenty of programs before
that seemed to work, but didn't. Anyone willing to take a look, and
check for correcness? I would also love to hear suggestions on how to
simplify, improve upon, or increase the power of this method.

-Goal-
To have an array containing various shapes, and draw (just print
statement in this app) them, without knowing what they are.

-Implementation-
structs are used as classes.
A base class, shape_t is inherited by child classes, circle_t and
rectangle_t.
Inheritance is accomplished by including shape_t as the first member
in the childs structure (it has to be first).
A generic class is created, any_shape_t. A pointer of any_shape_t
type, can point to objects of type circle_t and rectangle_t.
Objects created must in initialized by a constuctor function.

This file was tested with GCC (with no warnings) on Linux.
Dumping what's below into a single file should do the trick.
This is the output I got:
Draw a circle of radius 17.
Draw a rectangle of length 6 and width 5.

/********************* source *******************/

#include <stdio.h>

typedef struct
{
void (*Draw)();
}shape_t, *p_shape_t;

typedef struct
{
shape_t shape;
}any_shape_t;

typedef struct
{
shape_t shape;
int radius;
}circle_t;

typedef struct
{
shape_t shape;
int length;
int width;
}rectangle_t;

circle_t* Circle_ctor(circle_t* p_c, int radius);
void Circle_draw(circle_t* p_c);

rectangle_t* Rectangle_ctor(rectangle_t* p_r, int length, int width);
void Rectangle_draw(rectangle_t* p_r);


/********* circle *********/
circle_t* Circle_ctor(circle_t* p_c, int radius)
{
p_c->radius = radius;
p_c->shape.Draw = Circle_draw;
return p_c;
}

void Circle_draw(circle_t* p_c)
{
printf("Draw a circle of radius %d.\n", p_c->radius);
}


/********* rectangle *********/
rectangle_t* Rectangle_ctor(rectangle_t* p_r, int length, int width)
{
p_r->length = length;
p_r->width = width;
p_r->shape.Draw = Rectangle_draw;
return p_r;
}

void Rectangle_draw(rectangle_t* p_r)
{
printf("Draw a rectangle of length %d and width %d.\n", p_r->length, p_r->width);
}


/********* main *********/
int main()
{
circle_t c1;
circle_t* p_c1;
rectangle_t r1;
rectangle_t* p_r1;
any_shape_t* p_shapes[] = {0,0,0,0,0,0};
int i;


p_c1 = Circle_ctor(&c1, 17);
p_r1 = Rectangle_ctor(&r1, 6, 5);

p_shapes[0] = (any_shape_t*)p_c1;
p_shapes[1] = (any_shape_t*)p_r1;

for(i=0; p_shapes != 0; i++)
p_shapes->shape.Draw(p_shapes);

return 0;
}
 
J

Jack Klein

Attached below is a file that implements polymorphism in C. It's
loosly based on the way Miro Samek describes it
(http://www.embedded.com/97/fe29712.htm). I found the article a
little confusing so I made some changes and came up with wat's
below. It appears to work, but I've written plenty of programs before
that seemed to work, but didn't. Anyone willing to take a look, and
check for correcness? I would also love to hear suggestions on how to
simplify, improve upon, or increase the power of this method.

If you want to simplify this method, use C++. Really. That's what it
is for. If you wanted to expand your example by having more than one
"member" function, you would need to either increase the size of each
structure for more function pointers, or invent your own "vtable".
Why do all the work when any C++ compiler, even a 10 year old one,
will do it for you for free?

Attempting to write C++ in C generally leads to results that are
neither good C nor good C++. At least I haven't seen such an effort
yet that was.
 
M

Malcolm

Jack Klein said:
Attempting to write C++ in C generally leads to results that are
neither good C nor good C++. At least I haven't seen such an effort
yet that was.
Direct X (the Microsoft Windows game library) uses a component technology
called com, which is basically a C++ class. However there is also a C
interface, which is usable, if a little clunky.
 
J

Jarno A Wuolijoki

If you want to simplify this method, use C++. Really. That's what it
is for. If you wanted to expand your example by having more than one
"member" function, you would need to either increase the size of each
structure for more function pointers, or invent your own "vtable".
Why do all the work when any C++ compiler, even a 10 year old one,
will do it for you for free?

Attempting to write C++ in C generally leads to results that are
neither good C nor good C++. At least I haven't seen such an effort
yet that was.

Funny, I used to think that e.g. encapsulating file I/O that way was
a clever method of making the interface more powerful and portable in
various libraries I've seen..
 
X

xarax

Jack Klein said:
If you want to simplify this method, use C++. Really. That's what it
is for. If you wanted to expand your example by having more than one
"member" function, you would need to either increase the size of each
structure for more function pointers, or invent your own "vtable".
Why do all the work when any C++ compiler, even a 10 year old one,
will do it for you for free?

Attempting to write C++ in C generally leads to results that are
neither good C nor good C++. At least I haven't seen such an effort
yet that was.

There are times when C++ is just not a good choice
compared with C. The polymorphic technique presented
is extremely old and it only supports single-inheritance.
However, it is quite viable if there is an automated way
of generating the rather complex supporting C code for
instance construction and destruction.

I use a very similar technique in a TCP/IP component
that defines each message packet as a "class". The complexity
of keeping everything in synch (which a C++ compiler would
do automatically) is handled by a program generator that
accepts XML input that specifies the message packet fields
and types, then spits out the appropriate supporting C
code (and Java) that implements the polymorphic code. Functions
are represented in their own namespace by using function pointer
fields within a struct. A supporting constructor function
is generated that allocates the memory for the instance
data and assigns the function pointers to the class struct.
Parent constructors/destructors are automatically called as
needed when an instance is created or destroyed. Similar
Java classes are also generated so that my application can
send the message packets between Java and C.

There is no way that I could possibly keep everything
organized in C (and Java) without automation. At the
"client level" where I invoke the polymorphic methods,
it is simply a matter of double indirection (i.e., two
dereferences via ->) to get the function pointer and
pass the parameters, including the required first parameter
that points to the object instance. The "client level"
usage is very simple and easy to use, so it pays off
with higher productivity.

I have a GUI front-end for the program generator. Adding
a new field to message packet, or creating new message
packets is just a point-and-click. All of the Java and
C code is regenerated (thousands of lines of code). I
recompile the application and it's ready to go.

I suggest reading "Object Oriented Software Construction"
(2nd Edition) by Bertrand Meyer, before getting too deep
into this polymorphic C thing. Thinking in object oriented
terms and understanding how object oriented semantics are
expressed and represented is vital before you get too
deep into the C implementation details.

2 cents worth. Your mileage may vary.


--
----------------------------
Jeffrey D. Smith
Farsight Systems Corporation
24 BURLINGTON DRIVE
LONGMONT, CO 80501-6906
http://www.farsight-systems.com
z/Debug debugs your Systems/C programs running on IBM z/OS!
Are ISV upgrade fees too high? Check our custom product development!
 
S

stelios xanthakis

Attached below is a file that implements polymorphism in C. It's
loosly based on the way Miro Samek describes it
(http://www.embedded.com/97/fe29712.htm). I found the article a
little confusing so I made some changes and came up with wat's
below. It appears to work, but I've written plenty of programs before
that seemed to work, but didn't. Anyone willing to take a look, and
check for correcness? I would also love to hear suggestions on how to
simplify, improve upon, or increase the power of this method.

For more info you can see the lwc preprocessor which is
something like a C++ to C converter.

http://students.ceid.upatras.gr/~sxanth/lwc/

And an example for polymorphism:

http://students.ceid.upatras.gr/~sxanth/lwc/ex4.html
/********************* source *******************/

#include <stdio.h>

typedef struct
{
void (*Draw)();
}shape_t, *p_shape_t;

Here, shouldn't the prototype of Draw() have an argument??

It should be more like:

void (*Draw) (p_shape_t *this);

And the Draw() function should dispatch the 'this' argument by
the offset (zero indeed in this example) that points to a circle or rectanle....

Generally, the concept of callbacks is used very frequently (operating
systems:device drivers, for example).


stelios
 
S

SenderX

-Goal-
To have an array containing various shapes, and draw (just print
statement in this app) them, without knowing what they are.

That is VERY easy in C, believe it or not.


( pseudo code! )



/* SHAPE INTERFACE */


IShape.h
-------------------

/* Shape interface */
typedef struct IShape_
{
void *pImpl;
void ( *fpDraw ) ( void *pThis );
void ( *fpFree ) ( void *pThis );

} IShape;


/* Shape C API */

#define ShapeDraw( i ) ( (i)->fpDraw( (i)->pImpl ) )
#define ShapeFree( i ) ( (i)->fpFree( (i)->pImpl ) )




/* CIRCLE OBJECT */


Cricle.h
-------------------

#include "IShape.h"


/* Allocs a circle */
extern int CircleAlloc( IShape *pIFace, /* circle stuff */ );



Circle.c
-------------------

/* Circle object */
typedef struct Circle_
{
int iVal1;
/* ... */

} Circle;


/* Draws a circle */
static void CircleDraw( Circle *pThis )
{
/* ... */
}


/* Frees a circle */
static void CircleFree( Circle *pThis )
{
/* ... */

free( pThis );
}


/* Allocs a circle */
int CircleAlloc( IShape *pIFace, /* circle stuff */ )
{
Circle *pThis = malloc( sizeof( *pThis ) );

/* Init circle */

/* Init shape interface */
pIFace->pImpl = pThis;
pIFace->fpFree = CircleFree;
pIFace->fpDraw = CircleDraw;

return 0;
}





/* APPLICATION */


DrawShapes.c
-------------------

#include "Circle.h"

int main( void )
{
int i;

/* Ten shapes */
IShape s[10];


/* Alloc the circles */
for ( i = 0; i < 10; ++i )
{
CircleAlloc( &s, /* whatever */ );
}


/* Draw the circles */
for ( i = 0; i < 10; ++i )
{
ShapeDraw( &s );
}


/* Free the circles */
for ( i = 0; i < 10; ++i )
{
ShapeFree( &s );
}


return 0;
}


As you can see, this is fairly clean and will allow for any number of
different shapes to use the IShape interface.

OOP in C is very nice, IMHO...

;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top