Polymorphism in C++

Polymorphism (in C)

This is Nekuromento's second example, factored in the way I consider idiomatic for object-oriented C:

animal.h

#ifndef ANIMAL_H_
#define ANIMAL_H_

struct animal
{
// make vtable_ a pointer so they can be shared between instances
// use _ to mark private members
const struct animal_vtable_ *vtable_;
const char *name;
};

struct animal_vtable_
{
const char *(*sound)(void);
};

// wrapper function
static inline const char *animal_sound(struct animal *animal)
{
return animal->vtable_->sound();
}

// make the vtables arrays so they can be used as pointers
extern const struct animal_vtable_ CAT[], DOG[];

#endif

cat.c

#include "animal.h"

static const char *sound(void)
{
return "meow!";
}

const struct animal_vtable_ CAT[] = { { sound } };

dog.c

#include "animal.h"

static const char *sound(void)
{
return "arf!";
}

const struct animal_vtable_ DOG[] = { { sound } };

main.c

#include "animal.h"
#include <stdio.h>

int main(void)
{
struct animal kitty = { CAT, "Kitty" };
struct animal lassie = { DOG, "Lassie" };

printf("%s says %s\n", kitty.name, animal_sound(&kitty));
printf("%s says %s\n", lassie.name, animal_sound(&lassie));

return 0;
}

This is an example of runtime polymorphism as that's when method resolution happens.

C1x added generic selections, which make compile-time polymorphism via macros possible. The following example is taken from the C1x April draft, section 6.5.1.1 §5:

#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)

Type-generic macros for math functions were already available in C99 via the header tgmath.h, but there was no way for users to define their own macros without using compiler extensions.

How can I simulate OO-style polymorphism in C?

The first C++ compiler ("C with classes") would actually generate C code, so that's definitely doable.

Basically, your base class is a struct; derived structs must include the base struct at the first position, so that a pointer to the "derived" struct will also be a valid pointer to the base struct.

typedef struct {
data member_x;
} base;

typedef struct {
struct base;
data member_y;
} derived;

void function_on_base(struct base * a); // here I can pass both pointers to derived and to base

void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class

The functions can be part of the structure as function pointers, so that a syntax like p->call(p) becomes possible, but you still have to explicitly pass a pointer to the struct to the function itself.

Polymorphism in C

You generally do it with function pointers. In other words, simple structures that hold both the data and pointers to functions which manipulate that data. We were doing that sort of stuff years before Bjarne S came onto the scene.

So, for example, in a communications class, you would have an open, read, write and close call which would be maintained as four function pointers in the structure, alongside the data for an object, something like:

typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And the data for the object goes here.
} tCommsClass;

tCommsClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;

tCommsClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;

The initialisation of those function pointers would actually be in a "constructor" such as rs232Init(tCommClass*), which would be responsible for setting up the default state of that particular object to match a specific class.

When you 'inherit' from that class, you just change the pointers to point to your own functions. Everyone that called those functions would do it through the function pointers, giving you your polymorphism:

int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");

Sort of like a manually configured vtable, in C++ parlance.

You could even have virtual classes by setting the pointers to NULL -the behaviour would be slightly different to C++ inasmuch as you would probably get a core dump at run-time rather than an error at compile time.

Here's a piece of sample code that demonstrates it:

#include <stdio.h>

// The top-level class.

typedef struct _tCommClass {
int (*open)(struct _tCommClass *self, char *fspec);
} tCommClass;

// Function for the TCP class.

static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}

// Function for the HTML class.

static int htmlOpen (tCommClass *html, char *fspec) {
printf ("Opening HTML: %s\n", fspec);
return 0;
}
static int htmlInit (tCommClass *html) {
html->open = &htmlOpen;
return 0;
}

 

// Test program.

int main (void) {
int status;
tCommClass commTcp, commHtml;

// Same base class but initialized to different sub-classes.
tcpInit (&commTcp);
htmlInit (&commHtml);

// Called in exactly the same manner.

status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHtml.open)(&commHtml, "http://www.microsoft.com");

return 0;
}

This produces the output:

Opening TCP: bigiron.box.com:5000
Opening HTML: http://www.microsoft.com

so you can see that the different functions are being called, depending on the sub-class.

Is Polymorphism possible in C?

Polymorphism as a feature of object-oriented languages is not available in C. Neither are encapsulation and inheritance - the language does not have the corresponding features.

This does not mean, however, that it is impossible to model the corresponding behavior with the regular features of C: it is possible to build a library that lets you produce behavior that looks like polymorphism, for example, by using arrays of function pointers.

Trying to achieve polymorphism in C

Frankly, you will do best to defer trying to achieve polymorphism in C until you are no longer a newbie at programming in C.

Your code is dubious (it doesn't compile!). That should be void (*al)(void); in your main and you should arguably include the void in the argument lists of tripple and square. You don't need the & in front of the function names in the assignments to al, though I don't think it does any actual harm. (Beware though; there is a difference between using an array name and the address of an array name! That is: char a[10]; char *s = a; char (*t)[10] = &a;) You should also include a newline at the end of each message in each of these functions. Newlines at the beginning of a message are often (but not always) indicative of problems. Sometimes, it is OK to omit the newline at the end of a message, but not very often.

Any polymorphism implemented in C will use function pointers. But you should not be trying to implement polymorphism in C until you are comfortable using function pointers without attempting polymorphism. I suppose it could be said to be 'learning to swim by jumping in at the deep end', but you'd do better to learn a language like C++ that supports polymorphism than trying to do it in C which doesn't really do so.

Polymorphic data structures in C

I hope I understand what you want - I'm unsure but I guess you want to do something like that:

typedef struct
{
int type; // file or folder?
} Item;

typedef struct
{
struct A;
// data related to a file
} File;

typedef struct
{
struct A;
// data related to a folder - like pointer to list of Item
} Folder;

As long as both structure follow the same memory mapping (same variables) and adds to it as a child, you'll be able to use the pointer properly in both structs.

Check this one out as well: How can I simulate OO-style polymorphism in C?

Edit: I'm not sure about the syntax above (took it from the link above). I'm used to writing it this way instead:

typedef struct
{
int type;
// data for file
} File;

typedef struct
{
int type;
// data for folder - list, etc
} Folder;


Related Topics



Leave a reply



Submit