Using multiple interfaces in C

In my previous post I described one way of implementing interfaces and classes in C.
Some of my readers may wonder why I added the baseptr member.

Why do

static int dog_getage(Animal super)
{
	Dog self = (Dog) super->baseptr;
	return self->age;
}

when it’s easier to do

static int dog_getage(Animal super)
{
	Dog self = (Dog) super;
	return self->age;
}

With only single interface inheritance as in the previous post then

	super == self == super->baseptr

and

	super->baseptr

is redundant. But if we wish to allow for multiple interfaces then
this raw form of upcasting does not work.

Let’s assume that we need to add a class “Lamp” which implements two interfaces
“Switch” and “Regulator”. The Switch interface has the methods

void set_enabled(bool)
bool get_enabled()

The Regulator interface also has two methods

void set_level(int)
int get_level();

where the argument ranges between 0 and 99.

This can be implemented in C by creating a structure

struct Lamp_s {
	struct Switch_s switch_base;
	struct Regulator_s regulator_base;

	/* Members go here */
	bool enabled;
	int level;
};

where Switch_s is defined as

struct Switch_s {
	struct SwitchVtbl_s* vtbl;
	void* baseptr;
};

and Regulator_s is defined as

struct Regulator_s {
	struct RegulatorVtbl_s* vtbl;
	void* baseptr;
};

The constructor of Lamp may look like

Lamp lamp_create()
{
	Lamp self = calloc(sizeof(struct Lamp_s), 1);
	if (self)
	{
		/* Initialize vtbl */
		self->switch_base->vtbl       = &switch_vtbl;
		self->switch_base->baseptr    = self;
		self->regulator_base->vtbl    = &regulator_vtbl;
		self->regulator_base->baseptr = self;

		self->enabled = DEFAULT_MODE;
		self->level = DEFAULT_LEVEL;

		return self;
	}

	return NULL;
}

where switch_vtbl and regulator_vtbl are member function tables
just like in the original Animal/Dog example. We then need to provide two accessors in Lamp for the Regulator and Switch interfaces.

Switch lamp_get_switch(Lamp self);

Regulator lamp_get_regulator(Lamp self);

The complete code for the Switch, Regulator, Lamp and an example of how they can be used (main.c) comes here

main.c

#include <stdio.h>

#include "lamp.h"

int main()
{
	Lamp l = lamp_create();

	Switch    s = lamp_get_switch(l);
	Regulator r = lamp_get_regulator(l);

	printf("State of switch: %d\n", switch_get_enabled(s));
	printf("Regulator level: %d\n", regulator_get_level(r));

	switch_set_enabled(s, true); // Turn on
	regulator_set_level(r, 40); // Adjust level to 40%

	printf("State of switch: %d\n", switch_get_enabled(s));
	printf("Regulator level: %d\n", regulator_get_level(r));

	switch_destroy(s); // Not required but harmless
	regulator_destroy(r); // Not required but harmless

	lamp_destroy(l);
}

lamp.c

	
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#include "lamp.h"

#define DEFAULT_MODE  false
#define DEFAULT_LEVEL 50

struct Lamp_s {
       struct Switch_s    switch_base;
       struct Regulator_s regulator_base;

       /* Members go here */
       bool enabled;
       int  level;
};

static void lamp_set_enabled(Switch super, bool enabled)
{
	Lamp self = (Lamp) super->baseptr;

	printf("%s: Setting enabled=%d\n", __func__, enabled);

	self->enabled = enabled;
}

static bool lamp_get_enabled(Switch super)
{
	Lamp self = (Lamp) super->baseptr;

	return self->enabled;
}

static void lamp_set_level(Regulator super, int level)
{
	Lamp self = (Lamp) super->baseptr;

	printf("%s: Setting level=%d\n", __func__, level);

	self->level = level;
}

static int lamp_get_level(Regulator super)
{
	Lamp self = (Lamp) super->baseptr;

	return self->level;
}

struct RegulatorVtbl_s regulator_vtbl = 
{
	.set_level = lamp_set_level,
	.get_level = lamp_get_level
};

struct SwitchVtbl_s switch_vtbl =
{
	.set_enabled = lamp_set_enabled,
	.get_enabled = lamp_get_enabled
};

Lamp lamp_create()
{
	Lamp self = calloc(sizeof(struct Lamp_s), 1);
	if (self)
	{
		/* Initialize vtbl */
		self->switch_base.vtbl	     = &switch_vtbl;
		self->switch_base.baseptr    = self;
		self->regulator_base.vtbl    = &regulator_vtbl;
		self->regulator_base.baseptr = self;

		self->enabled = DEFAULT_MODE;
		self->level   = DEFAULT_LEVEL;

		return self;
	}

	return NULL;
}

Switch lamp_get_switch(Lamp self)
{
	return &self->switch_base;
}

Regulator lamp_get_regulator(Lamp self)
{
	return &self->regulator_base;
}

void lamp_destroy(Lamp self)
{
	if (self)
	{
		free(self);
	}
}

lamp.h

#ifndef LAMP_H
#define LAMP_H

#include "regulator.h"
#include "switch.h"

typedef struct Lamp_s* Lamp;

Lamp lamp_create();

Switch lamp_get_switch(Lamp self);

Regulator lamp_get_regulator(Lamp self);

void lamp_destroy(Lamp self);

#endif /* LAMP_H */

regulator.c

#include <stdio.h>

#include "regulator.h"

void regulator_set_level(Regulator super, int level)
{
	super->vtbl->set_level(super, level);
}

int  regulator_get_level(Regulator super)
{
	return super->vtbl->get_level(super);
}

void regulator_destroy(Regulator super)
{
	/* Don't require a destructor */
	if (super->vtbl->destroy)
	{
		super->vtbl->destroy(super);
	}
	else
	{
		printf("%s: No destructor defined\n", __func__);
	}
}

regulator.h

#ifndef REGULATOR_H
#define REGULATOR_H

typedef struct Regulator_s* Regulator;

struct RegulatorVtbl_s
{
	void (*set_level)(Regulator super, int level);
	int  (*get_level)(Regulator super);
	void (*destroy)(Regulator super);
};

struct Regulator_s
{
	struct RegulatorVtbl_s* vtbl;
	void*		        baseptr;
};

void regulator_set_level(Regulator super, int level);

int regulator_get_level(Regulator super);

void regulator_destroy(Regulator super);

#endif /* REGULATOR_H */

switch.c

#include <stdio.h>

#include "switch.h"

void switch_set_enabled(Switch super, bool enable)
{
	super->vtbl->set_enabled(super, enable);
}

bool switch_get_enabled(Switch super)
{
	return super->vtbl->get_enabled(super);
}

void switch_destroy(Switch super)
{
	/* Don't require a destructor */
	if (super->vtbl->destroy)
	{
		super->vtbl->destroy(super);
	}
	else
	{
		printf("%s: No destructor defined\n", __func__);
	}
}

switch.h

#ifndef SWITCH_H
#define SWITCH_H

#include <stdbool.h>

typedef struct Switch_s* Switch;

struct SwitchVtbl_s
{
	void (*set_enabled)(Switch super, bool enable);
	bool (*get_enabled)(Switch super);
	void (*destroy)(Switch super);
};

struct Switch_s
{
	struct SwitchVtbl_s* vtbl;
	void*		baseptr;
};

void switch_set_enabled(Switch super, bool enable);

bool switch_get_enabled(Switch super);

void switch_destroy(Switch super);

#endif /* SWITCH_H */
Posted in Uncategorized | Tagged , , , , , , , | Leave a comment

Using interfaces in C

C is a powerful language and it’s very useful for low level system development. It’s possible to write big programs using only procedural style programming, but abstraction techniques such as object oriented programming are very useful for breaking up dependencies and keeping programs well structured. Unfortunately C does not have built in support for classes or interfaces, but it’s possible to implement at least the most basic elements using pointers and structures. The result is not very pretty, but if you are stuck in C and can’t or don’t want to use C++ then it’s for sure worth a try.

The example below shows how to create an interface “Animal” and implement it in the class “Dog”.

main.c

#include <stdio.h>
#include <stdint.h>

#include "dog.h"

int main()
{
	Animal a = dog_create();

	printf("What does the dog say? %s\n", animal_getsound(a));
	printf("The animal is %d years old\n", animal_getage(a));

	animal_destroy(a);

	return 0;
}

animal.h

#ifndef ANIMAL_H
#define ANIMAL_H

typedef struct Animal_s* Animal;

struct AnimalVtbl_s
{
	const char* (*getsound)(Animal self);
	int (*getage)(Animal self);
	void (*destroy)(Animal self);
};

struct Animal_s                                                                                                               
{                                                                                                                             
        const struct AnimalVtbl_s* vtbl;                                                                                      
        void*                      baseptr;                                                                                   
};

const char* animal_getsound(Animal self);
int animal_getage(Animal self);
void animal_destroy(Animal self);  

#endif /* ANIMAL_H */

animal.c

#include "animal.h"

const char* animal_getsound(Animal self)
{
	return self->vtbl->getsound(self);
}

int animal_getage(Animal self)                                                                                                
{                                                                                                                             
        return self->vtbl->getage(self);                                                                                      
}                                                                                                                             
    
void animal_destroy(Animal self)
{
	return self->vtbl->destroy(self);
}

dog.h

#ifndef DOG_H
#define DOG_H

#include "animal.h"

Animal dog_create();

#endif /* DOG_H */

dog.c

#include 
#include 

#include "dog.h"

typedef struct Dog_s* Dog;

struct Dog_s
{
	struct Animal_s base;
                                                                                                                         
        /* Members go here */                                                                                                 
        int age;
};

static const char* dog_getsound(Animal self)
{
	return "voff";
}

static int dog_getage(Animal super)                                                                                           
{                                                                                                                             
        Dog self = (Dog) super->baseptr;                                                                                      
                                                                                                                              
        return self->age;                                                                                                     
}      

static void dog_destroy(Animal super)
{
	Dog self = (Dog) super->baseptr;

	puts("dog_destroy");

	free(self);
}

static const struct AnimalVtbl_s table =                                                                                      
{                                                                                                                             
        .getsound = dog_getsound,                                                                                             
        .getage   = dog_getage,                                                                                               
        .destroy  = dog_destroy                                                                                               
};                                                                                                                            
                                                                                                                              
Animal dog_create(int age)                                                                                                    
{                                                                                                                             
        Dog self = calloc(sizeof(struct Dog_s), 1);                                                                           
        if (self)                                                                                                             
        {                                                                                                                     
                /* Initialize vtbl */                                                                                         
                self->base.vtbl    = &table;                                                                                  
                self->base.baseptr = self;                                                                                    
                                                                                                                              
                /* Initialize members */                                                                                      
                self->age = age;                                                                                              
        }                                                                                                                     
                                                                                                                              
        return &self->base;                                                                                                   
}

Posted in Uncategorized | Tagged , , , , , , , | 1 Comment

Owncloud

I have been using Dropbox for the last couple of years and it’s a great service. It allows me to share photos, documents and other material across with my friends and across my devices. It also works as a poor mans backup, at least for small amounts of data.

The free Dropbox accounts gives the user a few gigabytes and it possible to upgrade to 50 or 100 GB for a reasonable fee.

Keeping stuff in the cloud is convenient but the recent NSA-scandals and Dropbox security issues gives us reasons to look for more secure (and why not cheaper) alternative solutions.

One such candidate is Owncloud, a free and open source cloud storage application written in PHP. It offers a basic “Dropbox-like” desktop and mobile client for Windows/Mac/Linux/iOS and Android with synchronization and sharing features. It also has a web based interface as an alternative to the native clients. The web interface has support for various extensions, but most of them need some more development time to mature.

The various interfaces are less feature-rich compared to Dropboxs alternative, but hey the basic “bread and butter” functions work, you can store and synch as much data as your harddrives can handle, your data is truly yours and it’s all free. It’s great stuff!

Setting up the server on Ubuntu or Debian is as simple as adding a deb-repository and running

sudo apt-get install owncloud

If you wish to access the server from a shared network such as the internet you most likely want to get a SSL-certificate to secure your login and traffic. If it’s a private server only used by yourself you can save some money by creating a self-signed certificate.

Posted in cloud, dropbox, free, owncloud, privacy | Leave a comment

I have a dream

My dream is to one day write my very own RTS-game ;) Just kidding, only serious. Well, at least now I have completed one step in the process by writing an a-star path finding library :)

https://github.com/krumberg/astar

Posted in Uncategorized | Tagged , , , , , , , | Leave a comment

Podcastfs now works on OS X

I spent some hours this morning hunting down a few bugs that caused podcastfs not to work under OS X (MacPorts). I also verified that it still compiles and runs fine under Debian testing.

https://github.com/krumberg/podcastfs

Posted in Uncategorized | Tagged , , , , , , , , | Leave a comment

RObject

RObject is a library I wrote a while ago that adds single inheritance object oriented programming support to ANSI C. It’s very small and can be customized and extended if so needed. Features that are not implemented but could easily be added include

https://github.com/krumberg/robject

Note that you need my Google Test-like unit testing library “rtest” to build the code.

Posted in Uncategorized | Tagged , , , , , | Leave a comment

git: the stupid NOSQL database

There are so many ways to use git. Manage your source code, handle large directories of files with git-annex, run a decentralized wiki or blog. View this talk and get inspired

The speaker is the author of Gollum, the wiki system used by Github. I tried it yesterday and it’s really easy to setup and seems to work very well.

To get gollum running, open a terminal and execute

gem install gollum
git init
gollum

This will install gollum, create a new git repository (preferably in a new and empty folder but I wanted to keep the example short) and starts a wiki webserver on port 4567 that is reading/writing to this repository.

Posted in Uncategorized | Leave a comment