Interfaces in C

#include <stdio.h>
#include <math.h>

// Here's a basic interface for geometric shapes.
typedef struct {
    double (*area)(void*);
    double (*perim)(void*);
} geometry;

// For our example we'll implement this interface on
// `rect` and `circle` types.
typedef struct {
    double width, height;
} rect;

typedef struct {
    double radius;
} circle;

// To implement an interface in C, we need to define functions
// that match the function pointers in the interface.
// Here we implement `geometry` on `rect`s.
double rect_area(void* r) {
    rect* rect_ptr = (rect*)r;
    return rect_ptr->width * rect_ptr->height;
}

double rect_perim(void* r) {
    rect* rect_ptr = (rect*)r;
    return 2 * rect_ptr->width + 2 * rect_ptr->height;
}

// The implementation for `circle`s.
double circle_area(void* c) {
    circle* circle_ptr = (circle*)c;
    return M_PI * circle_ptr->radius * circle_ptr->radius;
}

double circle_perim(void* c) {
    circle* circle_ptr = (circle*)c;
    return 2 * M_PI * circle_ptr->radius;
}

// If a variable has an interface type, then we can call
// methods that are in the named interface. Here's a
// generic `measure` function taking advantage of this
// to work on any `geometry`.
void measure(void* g, geometry* geom) {
    printf("Area: %f\n", geom->area(g));
    printf("Perimeter: %f\n", geom->perim(g));
}

int main() {
    rect r = {width: 3, height: 4};
    circle c = {radius: 5};

    // The `circle` and `rect` struct types both
    // implement the `geometry` interface so we can use
    // instances of these structs as arguments to `measure`.
    geometry rect_geometry = {rect_area, rect_perim};
    geometry circle_geometry = {circle_area, circle_perim};

    printf("Rectangle:\n");
    measure(&r, &rect_geometry);

    printf("\nCircle:\n");
    measure(&c, &circle_geometry);

    return 0;
}

This C code demonstrates the concept of interfaces using function pointers. Here’s a breakdown of the changes and explanations:

  1. We define a geometry struct that contains function pointers for area and perim. This acts as our interface.

  2. We define rect and circle structs to represent rectangles and circles.

  3. We implement the interface functions for both rect and circle. These functions take a void* parameter, which we cast to the appropriate struct type inside the function.

  4. The measure function takes a void* for the shape and a geometry* for the interface. It calls the interface functions through the function pointers.

  5. In main, we create instances of rect and circle, and corresponding geometry structs with the appropriate function pointers.

  6. We call measure with each shape and its corresponding geometry interface.

To compile and run this program:

$ gcc -o interfaces interfaces.c -lm
$ ./interfaces
Rectangle:
Area: 12.000000
Perimeter: 14.000000

Circle:
Area: 78.539816
Perimeter: 31.415927

This example demonstrates how to simulate interfaces in C using function pointers. While C doesn’t have built-in support for interfaces like some higher-level languages, this pattern allows for a similar level of abstraction and polymorphism.