Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emulating Classes in C using Structs

Tags:

c

class

struct

I am constrained to using C for a competition and I have a need to emulate classes. I am trying to construct a simple "point" class that can return and set the X and Y coordinates of a point. Yet, the below code returns errors such as "unknown type name point", "expected identifier or (" and "expected parameter declarator." What do these errors mean? How do I correct them? Is this the correct approach to writing a "pseudo-class"?

typedef struct object object, *setCoordinates;

struct object {
    float x, y;
    void (*setCoordinates)(object *self, float x, float y);
    void (*getYCoordinate)(object *self);
    void (*getXCoordinate)(object *self);
};

void object_setCoordinates(object *self, float x, float y){
    self->x = x;
    self->y = y;
}

float object_getXCoordinate(object *self){
    return self->x;
}

float object_getYCoordinate(object *self){
    return self->y;
}

object point;
point.setCoordinates = object_setCoordinates;
point.getYCoordinate = object_getYCoordinate;
point.getXCoordinate = object_getXCoordinate;

point.setCoordinates(&point, 1, 2);
printf("Coordinates: X Coordinate: %f, Y Coordinate: %f", point.getXCoordinate, point.getYCoordinate);

Reference: 1. C - function inside struct 2. How do you implement a class in C?


2 Answers

You would do much better to implement it as follows:

#include <stdio.h>

struct point {
    float x;
    float y;
};

void point_setCoordinates(struct point *self, float x, float y){
    self->x = x;
    self->y = y;
}

float point_getXCoordinate(struct point *self){
    return self->x;
}

float point_getYCoordinate(struct point *self){
    return self->y;
}

int main(void) {
    struct point my_point;

    point_setCoordinates(&my_point, 1, 2);

    printf("Coordinates: X Coordinate: %f, Y Coordinate: %f\n",
           point_getXCoordinate(&my_point),
           point_getYCoordinate(&my_point));

    return 0;
}

A few things to note:

  • As @Olaf has pointed out, never typedef a pointer - it hides your intent and makes things unclear. Yes, it's all over poor APIs (e.g: Windows), but it reduces readability.
  • You really don't need these functions to be the equivalent to virtual functions... just have a set of point_*() functions that you call on the point 'thing'.
  • Don't confuse things with poor names... if it's an X,Y point, then call it such - not an object (which is a very generic concept).
  • You need to call functions... in your call to printf() you used point.getXCoordinate - that is to say you took it's address and asked printf() to display it as though it were a float
  • You might start to wonder why you'd care about calling a function to get access to a variable that is inside a transparent struct... See below.

Many libraries / APIs provide opaque datatypes. This means that you can get a 'handle' to a 'thing'... but you have no idea what's being stored within the 'thing'. The library then provides you with access functions, as shown below. This is how I'd advise you approach the situation.

Don't forget to free the memory!

I've implemented an example below.

point.h

#ifndef POINT_H
#define POINT_H

struct point;

struct point *point_alloc(void);
void point_free(struct point *self);

void point_setCoordinates(struct point *self, float x, float y);
float point_getXCoordinate(struct point *self);
float point_getYCoordinate(struct point *self);

#endif /* POINT_H */

point.c

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

#include "point.h"

struct point {
    float x;
    float y;
};

struct point *point_alloc(void) {
    struct point *point;

    point = malloc(sizeof(*point));
    if (point == NULL) {
        return NULL;
    }

    memset(point, 0, sizeof(*point));

    return point;
}

void point_setCoordinates(struct point *self, float x, float y) {
    self->x = x;
    self->y = y;
}

float point_getXCoordinate(struct point *self) {
    return self->x;
}

float point_getYCoordinate(struct point *self) {
    return self->y;
}

void point_free(struct point *self) {
    free(self);
}

main.c

#include <stdio.h>

#include "point.h"

int main(void) {
    struct point *point;

    point = point_alloc();

    point_setCoordinates(point, 1, 2);

    printf("Coordinates: X Coordinate: %f, Y Coordinate: %f\n",
           point_getXCoordinate(point),
           point_getYCoordinate(point));

    point_free(point);

    return 0;
}
like image 114
Attie Avatar answered Sep 19 '25 06:09

Attie


Your code has some minor errors. That's why it doesn't compile.

Fixed here:

typedef struct object object;

struct object {
    float x, y;
    void (*setCoordinates)(object *self, float x, float y);
    float (*getYCoordinate)(object *self);
    float (*getXCoordinate)(object *self);
};

void object_setCoordinates(object *self, float x, float y){
    self->x = x;
    self->y = y;
}

float object_getXCoordinate(object *self){
    return self->x;
}

float object_getYCoordinate(object *self){
    return self->y;
}

int main()
{

    object point;
    point.setCoordinates = object_setCoordinates;
    point.getYCoordinate = object_getYCoordinate;
    point.getXCoordinate = object_getXCoordinate;

    point.setCoordinates(&point, 1, 2);
    printf("Coordinates: X Coordinate: %f, Y Coordinate: %f", 
    point.getXCoordinate(&point), point.getYCoordinate(&point));
}

As for the approach, there's probably no need to store the pointers to your methods inside the struct when you can simply call them directly:

object x;
object_setCoordinates(x, 1, 2);
//...
like image 45
PSkocik Avatar answered Sep 19 '25 08:09

PSkocik