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.
I guess, you already checked Wikipedia article on polymorphism.
In computer science, polymorphism is a programming language feature that allows values of different data types to be handled using a uniform interface.
According to that definition, no, C doesn't natively support polymorphism. For instance, there is no general function for acquiring absolute value of a number (abs
and fabs
are for integers and doubles respectively).
If you're also familiar with C++, take a look at OOP inheritance and templates - those are mechanisms for polymorphism there.
Almost all implementations of runtime polymorphism in C will use function pointers, so this is the basic building block.
Here is a simple example when procedure runtime behavior changes depending on it's argument.
#include <stdio.h>
int tripple(int a) {
return 3 * a;
}
int square(int a) {
return a * a;
}
void transform(int array[], size_t len, int (*fun)(int)) {
size_t i = 0;
for(; i < len; ++i)
array[i] = fun(array[i]);
}
int main() {
int array[3] = {1, 2, 3};
transform(array, 3, &tripple);
transform(array, 3, &square);
size_t i = 0;
for (; i < 3; ++i)
printf("%d ", array[i]);
return 0;
}
Using function pointers you can create virtual tables and use it to create "objects" that will be treated uniformly, but behave differently at runtime.
#include <stdio.h>
struct animal_vtable {
const char* (*sound)();
};
struct animal {
struct animal_vtable methods;
const char* name;
};
const char* cat_sound() {
return "meow!";
}
const char* dog_sound() {
return "bark!";
}
void describe(struct animal *a) {
printf("%s makes \"%s\" sound.\n", a->name, a->methods.sound());
}
struct animal cat = {{&cat_sound}, "cat"};
struct animal dog = {{&dog_sound}, "dog"};
int main() {
describe(&cat);
describe(&dog);
return 0;
}
There's no intrinsic support for polymorphism in C, but there are design patterns, using function pointers, base 'class' (structure) casts, etc., that can provide a logical equivalent of dynamic dispatch. The GTK library is good example.