Polymorphism in C

Started by
2 comments, last by Glass_Knife 7 years, 11 months ago
I have been experimenting of ways to implement interfaces in C and came up with this

inteface.h


#define interface(NAME, FNS) \
    typedef struct {FNS} NAME ## _implementation; \
    typedef struct { void* target; NAME ## _implementation* implementation; } NAME;

#define implements(CLASS, INTERFACE, COUNT, ...) \
    typedef union { INTERFACE ## _implementation implementation; void* pointers[COUNT]; } CLASS ## _implementation_of_ ## INTERFACE ## _struct; \
    CLASS ## _implementation_of_ ## INTERFACE ## _struct CLASS ## _implementation_of_ ## INTERFACE = { .pointers = {__VA_ARGS__} }; \
    INTERFACE CLASS ## To ## INTERFACE(CLASS* this) { INTERFACE result; result.target = this; result.implementation = &CLASS ## _implementation_of_ ## INTERFACE.implementation; return result; }

#define CallInterface(INTERFACE, METHOD, ...) INTERFACE.implementation->METHOD(INTERFACE.target, ## __VA_ARGS__)

istack.h

#include "interface.h"

interface(IStack,
    int (*push)(void* this, int value);
    int (*pop)(void* this);
    int (*peek)(void* this);
    int (*size)(void* this);
);

smallstack.c (I removed the implementation for brevity)

#include "istack.h"

#define MAX_STACK_SIZE 20

typedef struct {
    int entries[MAX_STACK_SIZE];
    int currentIndex;
} SmallStack;

SmallStack* SmallStack_init(SmallStack* this) {
    this->currentIndex = 0;
}

int SmallStack_push(SmallStack* this, int value) {...}

int SmallStack_pop(SmallStack* this) {...}

int SmallStack_peek(SmallStack* this) {...}

int SmallStack_size(SmallStack* this) {...}

implements(SmallStack, IStack, 4, 
    &SmallStack_push,
    &SmallStack_pop,
    &SmallStack_peek,
    &SmallStack_size
);

main.c

int Sum(IStack stack) {
    int result = 0;
    while (CallInterface(stack, size)) {
        result += CallInterface(stack, pop);
    }
    return result;
}

int main() {
    SmallStack smallStack;
    SmallStack_init(&smallStack);

    SmallStack_push(&smallStack, 10);
    SmallStack_push(&smallStack, 5);
    SmallStack_push(&smallStack, 2);

    printf("Sum: %d\n", Sum(SmallStackToIStack(&smallStack)));

    return 0;
}

My question is, would you use something like this? My biggest concern is in the implements macro.

You need to specify the methods in the same order and with the same count defined in the interface, otherwise your implementation will be broken. Can you think of a way for the compiler to enforce correct implementation of the interface?
My current game project Platform RPG
Advertisement
I answered my own question on type safety, sort of.

here is the updated implements



#define implements(CLASS, INTERFACE, ...) \
    INTERFACE ## _implementation CLASS ## _implementation_of_ ## INTERFACE = { __VA_ARGS__ }; \
    INTERFACE CLASS ## To ## INTERFACE(CLASS* this) { INTERFACE result; result.target = this; result.implementation = &CLASS ## _implementation_of_ ## INTERFACE; return result; }

and this is how its used



implements(SmallStack, IStack, 
    .push = &SmallStack_push,
    .pop = &SmallStack_pop,
    .peek = &SmallStack_peek,
    .size = &SmallStack_size
);

You can still leave out methods though, but this is already way more readable. I also had to change SmallStack_push to accept a void pointer instead of a specific type, but after a cast it works out.
My current game project Platform RPG
My question is, would you use something like this? My biggest concern is in the implements macro.

Not particularly. Interfaces in C already exists and works well.

What you describe already has an implementation in idiomatic in C, and has existed since the '70s.

The virtual functions in C++ was a way to automate the process with a standardized seven letter keyword and a standard structure called a vtable that can be made static rather than the more common dynamic structure, but otherwise the practice is common in C and has been for decades.

Many of the "opaque pointers" in C such as the FILE structure contain a collection of function pointers that are specialized for whatever the underlying file connection happens to be. The functions the programmer calls in the library, fread(), fwrite(), fclose(), and so on, were often implemented as what C++ calls virtual dispatch. Instead of doing the work directly, they call the FILE structure's internal read function, or write function, or close function, and those functions are assigned when fopen() figures out the details of the stream it will be working with.

You might have a CreateFoo that returns a pointer to a FooStruct. You can state that FooStruct contains function pointers for your push, pop, peek, and size operations. Or you might have the FooStruct have a guaranteed member called SmallStack that has a function table with those function pointers in it so you can implement multiple interfaces that way.

Trying not to be too harsh, but it looks like all you did is wrap up some virtual function assignment with a macro specific to your individual functionality. Since everyone is going to write their own version for any new functionality they write, it doesn't seem to be much of a helper. With idiomatic C you write that in your factory method already, and it is not a difficult or tedious task needing a macro.

This is good read on the subject if you haven't seen it yet.

https://www.cs.rit.edu/~ats/books/ooc.pdf

I think, therefore I am. I think? - "George Carlin"
My Website: Indie Game Programming

My Twitter: https://twitter.com/indieprogram

My Book: http://amzn.com/1305076532

This topic is closed to new replies.

Advertisement