Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OOP in C, implicitly pass self as parameter

Tags:

c

oop

I've been working on an example to learn OOP in C. Currently I've come up with this code which is working, however I'm interested in making the methods implicitly pass self as a parameter.

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//#include "Stopwatch.h"


typedef struct stopwatch_s
{
    unsigned int milliseconds;
    unsigned int seconds;
    unsigned int minutes;
    unsigned int hours;

    bool is_enabled;

    void ( *tick )      ( struct stopwatch_s* );
    void ( *start )     ( struct stopwatch_s* );
    void ( *stop )      ( struct stopwatch_s* );
    void ( *reset )     ( struct stopwatch_s* );
} stopwatch_t;

static void tick (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    if (self->is_enabled)
    {
        self->milliseconds++;
        if (self->milliseconds >= 1000)
        {
            self->milliseconds = 0;
            self->seconds++;

            if (self->seconds >= 60)
            {
                self->seconds = 0;
                self->minutes++;

                if (self->minutes >= 60)
                {
                    self->minutes = 0;
                    self->hours++;
                }
            }
        }
    }
}

static void start (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    self->is_enabled = true;
}

static void stop (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    self->is_enabled = false;
}

static void reset (stopwatch_t* _self)
{
    stopwatch_t * self = _self;
    self->is_enabled = false;
    self->milliseconds = 0;
    self->seconds = 0;
    self->minutes = 0;
    self->hours = 0;
}

void * new_stopwatch()
{
    stopwatch_t * newInstance = (stopwatch_t *)calloc(1, sizeof(stopwatch_t));
    newInstance->is_enabled = false;
    newInstance->milliseconds = 0;
    newInstance->seconds = 0;
    newInstance->minutes = 0;
    newInstance->hours = 0;
    newInstance->tick = &tick;
    newInstance->start = &start;
    newInstance->stop = &stop;
    newInstance->reset = &reset;

    return newInstance;
}

void main()
{
    struct stopwatch_s * Stopwatch = new_stopwatch();
    printf ("Initial: %d\n", Stopwatch->milliseconds);
    Stopwatch->start (Stopwatch);
    Stopwatch->tick (Stopwatch);
    Stopwatch->tick (Stopwatch);
    Stopwatch->tick (Stopwatch);
    printf ("Started: %d\n", Stopwatch->milliseconds);
    Stopwatch->stop (Stopwatch);
    Stopwatch->tick (Stopwatch);
    printf ("Stopped: %d\n", Stopwatch->milliseconds);
    Stopwatch->reset (Stopwatch);
    printf ("Reset: %d\n", Stopwatch->milliseconds);    
}

I've tried reading and following Object Oriented Programming with ANSI-C, but can't wrap my head around how to structure my "object" so instead of

Stopwatch->tick(Stopwatch);

I can write

Stopwatch->tick();
like image 384
Carsten Farving Avatar asked Sep 02 '15 08:09

Carsten Farving


1 Answers

I can't wrap my head around how to structure my "object" so instead of

Stopwatch->tick(Stopwatch);

I can write Stopwatch->tick();

This is not possible in standard C. You need to pass the receiver as an explicit formal argument to your C functions (in contrast with C++ which has this as an implicit formal).

However:

  • you generally want to pack all the method functions in one single struct with several function members (and have each instance start with a pointer to that struct). Read about vtable-s.

  • you could have some macro (or perhaps inline function) to avoid giving Stopwatch twice; you'll still write TICK(Stopwatch) not Stopwatch->tick();; the statement-expr extension of GCC could be useful.

Look into GTK and its Gobject system as an example of a cute object system for C. Read also about the ObjVLisp model and wikipage on virtual method tables. Maybe see this draft report and RefPerSys and also the blog of the late J.Pitrat.

BTW, you could decide that you have first class method selectors (perhaps as integers, or pointers to some common selector type) and code a variadic send dispatching function (so you would code send(StopWatch,TICK_SEL) instead of your dreamed Stopwatch->tick()) or macro. You might find libffi useful. The old Xview could be inspirational.

At last, as many fancy object layer implementors, you might use some metaprogramming and provide some C code generating tool (like moc in Qt). You might even consider customizing your GCC compiler with MELT for such purposes. Or making a translator (see this) from your fancy OOP dialect to C (like VALA or SWIG or Bigloo or Chicken-Scheme do; see also this). Or preprocess your code with an external preprocessor (your own one, or m4 or GPP, etc...).

like image 64
Basile Starynkevitch Avatar answered Nov 16 '22 00:11

Basile Starynkevitch