27

Possible Duplicate:
How can I simulate OO-style polymorphism in C?

I'm trying to better understand the idea of polymorphism with examples from languages I know; is there polymorphism in C?

Community
  • 1
  • 1
Belgi
  • 14,542
  • 22
  • 58
  • 68
  • 8
    You can always write something polymorphic by implementing, say, some vtable technology. It's just about playing around with function pointers to create a suitable level of indirection. – Kerrek SB Nov 19 '11 at 13:11
  • 2
    http://stackoverflow.com/questions/524033/how-can-i-simulate-oo-style-polymorphism-in-c – 0x90 Nov 19 '11 at 13:22

4 Answers4

51

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.

Community
  • 1
  • 1
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • Is it convention to put `DOG` AND `CAT` in `animal.h`? I could imagine they can go in their respective header too. – Brady Dean Feb 12 '19 at 22:21
22

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;
}
Nekuromento
  • 2,147
  • 2
  • 16
  • 17
  • the 'idiomatic' way to do OO in C (insofar as there is such a thing) would make the `methods` member of `struct animal` a pointer so different instances of the same 'class' can share their vtables – Christoph Nov 19 '11 at 13:59
  • @Christoph: that's an extra level of indirection, can be a good or bad thing. it's a space vs speed tradeoff. – Karoly Horvath Nov 19 '11 at 14:29
  • @yi_H: sure, but I would still consider using pointers idiomatic as the vtable corresponds to an object's class, which is (at least conceptionally) shared between instances; if you look at it this way, including the vtable in the instance provides a method cache, ie it is a form of optimization... – Christoph Nov 19 '11 at 14:42
  • 1
    @Christoph: that's because you think in *classes*. imagine a system where each vtable entry can be independently set. there a class approach would lead to a combinatorial explosion. again, I'm just saying there is no *right* approach, there are tradeoffs. – Karoly Horvath Nov 19 '11 at 16:05
  • 5
    "Woof" is the idiomatic noise emitted by Dog as established in RFC K9 – Matt Montag May 28 '19 at 02:16
3

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.

Brett Hale
  • 21,653
  • 2
  • 61
  • 90
2

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.

aztek
  • 1,399
  • 10
  • 12
  • You need to cast pointers to it, so, technically, they're no longer of different types. This is one of possible workarounds, though. – aztek Nov 19 '11 at 13:48
  • 2
    C99 added polymorphic math functions for arithmetic types in a header called `tgmath.h`; C1x adds `_Generic` to make user-defined polymorphic macros possible – Christoph Nov 19 '11 at 14:30