Emulating Classes in C using Structs

Viewed 5142

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?

4 Answers

I also have an example of basic class emulation in C [the OP specified for a specific application, although, this answer is to the general question]:

A header file called "c_class.h"

#ifndef CLASS_HEADER_H
#define CLASS_HEADER_H

// Function pointer prototypes used by these classes
typedef int sub_func_t (int);
typedef float sub_funcf_t (int,int);

/* class type definition 
  (emulated class type definition; C doesn't really have class types) */
typedef struct {
    //Data Variables
    int a;

    /*Function (also known as Method) pointers
      (note that different functions have the same function pointer prototype)*/
    sub_func_t* add;
    sub_func_t* subt;
    sub_func_t* mult;
    sub_funcf_t* div;  
} class_name;


// class init prototypes
// These inits connect the function pointers to specific functions
// and initialize the variables.
class_name* class_init_ptr (int, sub_func_t*, sub_func_t*, sub_func_t*, sub_funcf_t*);
class_name class_init (int, sub_func_t*, sub_func_t*, sub_func_t*, sub_funcf_t*);

#endif

A source code file called "c_class.c"

//gcc -o c_class c_class.c

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"c_class.h"

// The class function definitions.

/*
    If we make these member functions static then they are only 
    accessible via code from this file.
    However, we can still pass the class-like objects around a 
    larger program and access their member functions,
    just like in any OO language.
    
    It is possible to emulate inheritance by declaring a class object 
    from the class type definition (I don't touch on these more 
    abstract subjects though, this is only a basic class emulation).
*/
    
static int AddFunc(int num){
    num++;
    return num;
}

static int SubtFunc(int num){
    num--;
    return num;  
}

static int MultFunc(int num){
    num *= num;
    return num;
}

static float DivFunc(int num, int denom){
    float fnum = (float)num / (float)denom;
    return fnum;  
}

// The class init function definitions.
class_name* class_init_ptr (int num, sub_func_t* addition, sub_func_t* subtraction, sub_func_t* multiplication, sub_funcf_t* division) 
{ 
    class_name* new_class = malloc(sizeof(*new_class)); 
    assert(new_class != NULL);
    *new_class = (class_name){num, addition, subtraction, multiplication, division};
    /*We could also just type:
    new_class->a = num;
    new_class->add = addition;
    new_class->subt = subtraction;   
    new_class->mult = multiplication; 
    new_class->div = division;  
    */
    return new_class; 
}

class_name class_init(int num, sub_func_t* addition, sub_func_t* subtraction, sub_func_t* multiplication, sub_funcf_t* division) 
{ 
    class_name new_class; 
    new_class = (class_name){num, addition, subtraction, multiplication, division};
    /* We could also just type:
    new_class.a = num;
    new_class.add = addition;
    new_class.subt = subtraction;   
    new_class.mult = multiplication; 
    new_class.div = division;  
    */
    return new_class; 
}

//Working Function Prototypes
class_name* Working_Function(class_name*);
class_name Working_Function_Two(class_name);

int main(){
    /* It's possible to connect the functions within the init also,
       w/o sending them. */
    class_name *MyClass = class_init_ptr(5, AddFunc, SubtFunc, MultFunc, DivFunc);
    class_name MyOtherClass = class_init(0, AddFunc, SubtFunc, MultFunc, DivFunc);
    
    printf("%i\n",MyClass->add(100));// 101
    
    printf("%i\n",MyClass->subt(100));// 99

    printf("%i\n",MyClass->mult(100));// 10000

    printf("%f\n",MyClass->div(MyClass->a,2)); // 2.5
    
    printf("%i\n",MyClass->mult(MyClass->mult(100))); //100000000

    MyClass = Working_Function(MyClass);
    //This would work also (because we're passing a pointer):
    //Working_Function(MyClass); 
   printf("%i\n",MyClass->a); //a = 5000

    MyOtherClass = Working_Function_Two(MyOtherClass);
    printf("%i\n",MyOtherClass.a); //a = 9999

    MyOtherClass.a = 25;
    Working_Function_Two(MyOtherClass); //pass by value
    printf("%i\n",MyOtherClass.a); //a = 25  (no value change)

    Working_Function(&MyOtherClass); //pass by reference
    printf("%i\n",MyOtherClass.a); //a = 5000 (value changed)

    return 0;
}

//Working Functions
class_name* Working_Function(class_name* PassedClass){
    printf("%i\n",PassedClass->a);// 5, then 25
    printf("%i\n",PassedClass->add(PassedClass->a));// 6, then 26
    PassedClass->a = 5000;
    return PassedClass;
}

class_name Working_Function_Two(class_name PassedClass){
    printf("%i\n",PassedClass.a);// 0, then 25
    printf("%i\n",PassedClass.add(PassedClass.a));// 1, then 26
    PassedClass.a = 9999;
    return PassedClass;
}

/* We're passing emulated class objects and emulated class pointers 
   by reference and value, if everything works it should print this:

101
99
10000
2.500000
100000000
5
6
5000
0
1
9999
25
26
25
25
26
5000

*/
Related