Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reproduce the class methods behavior in C99?

Tags:

c

pointers

struct

I have a simple code to calculate a polygon perimeter

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

struct point { float x; float y; };

struct polygon
{
    int sides;
    struct point vertex[20];                // No more than 20 sided polygon
    float (*perimeter)(struct polygon * p);
};

struct polygon * polygon_init(int sides)
{
    struct polygon *poly;
    poly = malloc(sizeof *poly);
    poly->sides = sides;
    return poly;
}

void polygon_destroy(struct polygon * poly)
{
    free(poly);
}

float distance(struct point p1, struct point p2)
{
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}

// Assuming points are ordered ;-)
float perimeter(struct polygon * poly)
{
    float p = 0;
    for (int i = 0; i < poly->sides - 1; i++)
        p += distance(poly->vertex[i], poly->vertex[i+1]);
    return p + distance(poly->vertex[poly->sides - 1], poly->vertex[0]);
}

int main(void)
{
    struct polygon *p1 = polygon_init(3);
    struct point t[3] = { {0, 0}, {.5, 1}, {1, 0}};
    memcpy(p1->vertex, t, sizeof(t));
    p1->perimeter = &perimeter;
    printf("perimeter = %.2f\n", p1->perimeter(p1));
    polygon_destroy(p1);
    return 0;
}

As you can notice, I use a pointer to function to assign a "custom" function to calculate the perimeter (p1->perimeter = &perimeter;). With that I can "abstract" the call based on the struct assigned to p1 .

But as noticeable, I pass the reference to struct itself as parameter to the perimeter() function as p1->perimeter(p1), and this looks pretty much redundant.

I would like to know if there is a way to call p1->perimeter(), instead the call above and inside perimeter() function it would automagically (know that I'm referencing p1), said that it would calculate the perimeter of p1 itself.

In a single phrase: I'm trying to fake the behavior of class methods.

like image 386
Lin Avatar asked Jan 26 '23 19:01

Lin


1 Answers

The idiomatic solution is not to expose the function pointer to the end user, but instead to use a regular function call which does the pointer lookup internally.

float polygon_perimeter(struct polygon *p)
{
    return p->perimeter(p);
}

This is nice because it parallels polygon_init() and polygon_destroy(): all the functions are outside the "class" rather than this particular one being called via a member pointer just because it's polymorphic.

It also avoids leaking the internals of struct polygon to users. There are a lot of benefits if you require all access to a "class" to be done via an opaque pointer and a library of function calls. No members accessed directly, so the layout of the struct can change freely without risking API breakage.

If you go that route then you can completely hide its internals by only providing an empty forward declaration in polygon.h:

// polygon.h
struct polygon;
like image 186
John Kugelman Avatar answered Jan 29 '23 21:01

John Kugelman