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 */
This entry was posted in Uncategorized and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s