C With Classes

5 minute read Published:

C Programming

Article Header

This is a thought experiment. I couldn’t imagine someone would go to this trouble without just using C++ instead.

Someone on Reddit asked recently if structs could be used to emulate classes. Early on when learning Java, it was explained to me that the difference between a struct and a class is that a class can do something. I don’t know how accurate that is, but we can certainly call function pointers from a struct:

int _GetClawLength(void *_this);
struct Cat{
        int ClawLength;
        int (*GetClawLength)(void *);
};
//...
int _GetClawLength(void *_this){
        struct Cat *this = (struct Cat *)_this;
        return this->ClawLength;
};

In order to call this, simply allocate a cat, assign the appropriate function pointers, and call it!

int main(void){
        struct Cat *Ian = malloc(sizeof(struct Cat));
        Ian->GetClawLength = _GetClawLength;
        Ian->ClawLength = 9;
        printf("%d", Ian->GetClawLength(Ian); 
}

But this is hardly news, is it? You’re just organizing function pointers and needlessly calling them from a struct context. After all,

Ian->GetClawLength(Ian)

Is equivalent to

_GetClawLength(Ian)

So what other features can we implement to make it worth while?

Hello, privacy

Because of the way the compiler (GCC) stacks variables in a struct, you can pack variables into the allocated space of other structures. For example, to create private variables,

struct Cat{
        int ClawLength;
        int (* GetClawLength)(void *);
        int (* GetNumLives)(void *);
        void (* SetNumLives)(void *, int);
};
struct PrivateCat{
        struct Cat PublicScope;
        int NumLives; //The number of lives a cat has left is best
                      //kept private; its enemies could use
                      //this information to their advantage!
};

The compiler (GCC) will create, in memory, an object that looks like this:

{
        //Public cat variables:
        int,
        int function pointer,
        int function pointer,
        void function pointer,
        //private cat variables:
        int
}

Which means that casting a PrivateCat to a Cat will simply limit the namespace of the struct to the compiler. So if you have the function, “GetNumLives”, in the private scope, we can access it from the Public scope by calling that function and casting to private:

int _GetNumLives(void *_this){
        struct PrivateCat *this = (struct PrivateCat *)_this;
        return this->NumLives;
}
void _SetNumLives(void *_this, int NumLives){
        struct PrivateCat *this = (struct PrivateCat *)_this;
        this->NumLives = NumLives;
}

Constructing this, you can do the following

int main(void){
        struct Cat Mosby = malloc(sizeof(PrivateCat));
        Mosby->GetClawLength = _GetClawLength;
        Mosby->GetNumLives = _GetNumLives;
        Mosby->SetNumLives = _SetNumLives;
        Mosby->SetNumLives(Mosby, 9);
        printf("%d", Mosby->GetNumLives(Mosby));    //prints `9`
}

Because the public scope is stacked on top of the private scope, you can access it in memory, but only if the type referenced is struct PrivateCat

Unfortunately, it’s quite tedious to have to initialize this struct every time and assign its function pointers. We can create a function which does this, “InitializeCat”, or even “CatConstructor”, but I think I can do you one better…

Hello, inheritance; Hello, constructors; Hello static context

Because we can stack these things, we can stack whatever the hell we want, so long as we’re consistent. All Java objects inherit the Object class, so let’s create our own:

struct Object{
        void *(*New)(); //You can implement arguments
                        //to this function however you want, using
                        //varargs or void *, etc.
};

Simple. Just takes a void pointer to a constructor function. To implement these, Public scope structs must include them as members. Actually, this object only needs to be implemented by a single object once, so you can implement this as an anonymous union if you want, so that subsequent objects can use this memory space for other variables.

struct Cat{
        struct Object Parent;
        // Other members...
};

What does this do for us? We can create a constructor function like so:

void *ConstructCat(){
        struct PrivateCat *this = malloc(sizeof(struct PrivateCat));
        this->PublicScope.ClawLength = 9;
        this->PublicScope.GetClawLength = _GetClawLength;
        this->PublicScope.GetNumLives = _GetNumLives;
        this->PublicScope.SetNumLives = _SetNumLives;
        this->NumLives = 9;
        return PrivateCat;
}

For syntactic sugar, and to support a “static” context, we do two things: create a “new” function, and create a “static” object, or a global variable for that class that we can call by a name recognizable as being in the “static” context.

//We have to declare the constructor before
//the struct so that we can initialize the static
//cat right after defining the struct.
void *ConstructCat();
struct Cat{
        struct Object Parent;
        //Members go here
} StaticCat = {
        .Parent.New = ConstructCat
        //Define whatever static variables you want in here
        //so that you can call them as `StaticCat.ClawLength`
        //These won't be purely static however, as instantiations
        //will have their own copies, but they do give a global
        //context for these member variables
};

Here’s where the magic of inheritance comes into play: because the compiler stacked the structs on top of each other in the order we defined them, we can cast any struct which implements the Object struct to (struct Object *) and then call the constructor from there:

void *new(void *Template){
        return ((struct Object*)Template)->New();
}

Which enables this:

struct Cat *Ian = new(&StaticCat);

For added comfort, choose your own naming convention so that it makes more sense to you. If you want, you can work it into “new(&Cat)”.

Of course, you’ll need to write destructors and methods which free the largest size pointer you allocate (private in this case). Also, nothing is stopping the developer from casting whatever pointers she wants to the private form and accessing variables this way, but this is how C works, of course.

A final look at an implementation in a file “CatClass.c” might look like

//Declare Constructor before structs
void *ConstructCat();
struct Cat{
        struct Object Parent;
        int ClawLength;
        int (*GetClawLength)(void *);
        int (*GetNumLives)(void *);
} StaticCat = {
        .Parent.New = ConstructCat
};    //instantiate this as a private cat if you want static
      //access to private variables for some reason...
struct PrivateCat{
        struct Cat PublicScope;
        int NumLives;
};
int _GetClawLength(void *this);
int _GetNumLives(void *this);
void *ConstructCat(){
        struct PrivateCat *this = malloc(sizeof(struct PrivateCat));
        this->PublicScope.ClawLength = 8;
        this->PublicScope.GetClawLength = _GetClawLength;
        this->PublicScope.GetNumLives = _GetNumLives;
        this->NumLives = 9;
}
int _GetClawLength(void *this){
        return ((struct Cat *)this)->ClawLength;
}
int _GetNumLives(void *this){
        return ((struct PrivateCat *)this)->NumLives;
}

The ObjectClass.c file would simply be

struct Object{
        void *(*New)();
};
void *new(void *Template){
        return ((struct Object*)Template)->New();
}

and main.c:

int main(void){
        struct Cat *Ian = new(&StaticCat);
        printf("%d\n", Ian->GetNumLives(Ian));
            //prints `9`
}

Source: Collin Oswalt