Category: Programming

A non-OO interface system

I got a question recently from a person i used to mentor some years ago. He had trouble with a program he was developing, where he needed to have access to an generic “object” that he could pass around, but he needed this entitity to correpsond to different subtypes, with different implementation specifics, but this was to be unknown to the user of the entity.

He was basically asking for a pattern to create a plugin system, with a central process that will consume, load and call anonymous instances of “modules”. Now, this is an excellent example of programming to a contract, where sublclasses and/or interfaces would save the day.

Problem in this case: he was to use C. On an old compiler. So, question became, how to do “polymorphism in C”?

My answer to him was that yes, it can be done, sort of. Not in the parent-/child-type kind of way, perhaps, but in the interface way. There are some well used patterns existing for this. However, a method i have used exstensively myself is the one i will describe here. It is a method, whereby you hide the implementation of a function, and just use a pointer to it to call it, much like what Java or C++ will do behind the scenes. This pointer you make part of your “interface”, being a struct. Now this type becomes your contract, that you can use to pass multiple types of modules, but using them through the same interface. This also requires a “factory”-like construct, that can create custom types, that will be of the contract type (conforming to the iface).

Some noteable places where patterns like this ARE used are in the source code of Quake2 (renderer plugins) and in the source code of Nexus Filemanager (different tree-viewports loaded dynamically) as well as many early plugin-based software.

For an interesting comparison, i will also show a simplified example written in Go, for the same pattern. Go is also a non-OO language, but the difference ia that Go has many nice things that C does not, for instance it has a native notion of an “interface”, making the same pattern more readable. It also has a native garbage collection despite being a complied language, which helps a bit in larger implementations (as the C variant would need code to clean up and free the pointed to functions, if no type is left using it).

Our example today, will be simple. I will use the age old OO example of animals of different types, making sounds (common in every OO-langauge textbook known to man). We will however not have the classes Animal->Dog etc. The only entity the base system will know of is the general contract Animal. It will then call the makeSound() function on each animal, and through some fake polymorphism it will act different, despite the call being the same. Conclusion being that you do not need to make your animal-base aware of the existance of dogs, cows etc. by using specific types. Instead it is only aware of the base type Animal, the actual animal is built by the “factory”. The contract is thus the same, make_sound() is the only call the base needs to make, no matter what animal we have.

We have now, by doing this, created out of a struct, something that resembles a basic “object”. Meaning that due to the function pointer, it not only holds data, it holds “methods”. If you add in self-reference (send a pointer to the instance holding the function pointer in the call) you can even replicate the true OO paradigm of “data and methods to work on that data”.

So how do we do this in C?

First, let us review the concept of the “function pointer”. In a C program, a function is merely an entry-point in memory. Thus, this can be pointed to by a pointer, and passed around as such. This is what enables us to do things like we are about to do now. For instance you can pass a pointer to a function and use it to call said function from a different place in code (like a callback function).

It is written on the following form:

int (*func_ptr)(int,int);

meaning it is a pointer to a function returning an int, that takes two int parameters as input. The pointer is called func_ptr. To pint it to a function, any function fulfilling this pattern will do. So we can do this:

func_ptr = &some_function;

where some_function is the name of a function in your program.

Now, on to the actual code.

First we write the header file, to define our type:


//animal.h
 
//Sound "iface"
typedef void (*animal_sound)(void);


//A "polymorphic" Animal struct 
typedef struct {
	int type; 

	//Custom "method" for sound
	animal_sound make_sound;

} Animal;

//Factory to create an animal, based on input 
Animal* ConstructAnimal(int type);


//Different sound functions for animals, conforms to contract of FP 
void Bark(); 
void Moo(); 
void Meow();

Note the use of the factory to create “animals”. Also, we need to specify the functions that we will bind to the various types of animals. As opposed to OO, here we can not contain them in their object, we will declare them freely, in our interface code, and instead point to them.

On to the implementations:

#include "animal.h"
#include <stdio.h>

//Implementation of the animal factory 
Animal* ConstructAnimal(int type) {
	Animal* an = malloc(sizeof(Animal));
	switch (type) {
	case 1:
		//Dog
		an->type = 1; 
		
		//Hook up the pointer to the internal func to use 
		an->make_sound = Bark;

		break;

	case 2:
		//Cow
		an->type = 2;

		//Hook up the pointer to the internal func to use 
		an->make_sound = Moo;

		break; 

	case 3:
		//Cat
		an->type = 3;

		//Hook up the pointer to the internal func to use, pass self ref
		an->make_sound = Meow;

		break;

	default:

		//Dog
		an->type = 1;

		//Hook up the pointer to the internal func to use 
		an->make_sound = Bark;
	}
}


//Different sound functions for animals, caller does not need to know which is used, this is set up by the factory
void Bark() {
	printf("I am an animal of type Dog and i bark, bark bark!\n\n");
}
void Moo() {
	printf("I am an animal of type Cow and i moo, moo moo!\n\n");
}
void Meow() {
	printf("I am an animal of type Cat and i meow, meow meow!\n\n");
}

As you can see, the factory is responsible for creating an animal of the requested type. Now, this is a simple example. In real life you would probably have a lot of logic to determine which functions to point to (what type of animal, plugin, renderer etc.), and you would likely have self-reference in the functions pointed to, to access the base data of the struct.

Now for some testing:

#include <stdio.h>
#include "animal.h"

int main() {

	//Spawn an animal by the factory method, we could use more complex input here
	Animal* animal_one = ConstructAnimal(1);
	Animal* animal_two = ConstructAnimal(2);
	Animal* animal_three = ConstructAnimal(3);
	Animal* animal_four = ConstructAnimal(4);

	//Note that this code does not need to know anything about the target animal, only that there is a func for sound. This is a "polymorphic" tendency, albeit not fully 
	//We can now allow for modules to load in like plugins and such
	
	//Note the use of the same call, albeit these are different "types" of animals, returned by the factory 
	animal_one->make_sound();
	animal_two->make_sound();
	animal_three->make_sound();
	animal_four->make_sound();

	//Wait 
	getch();
	
	//Deallocate 
	free(animal_one);
	free(animal_two);
	free(animal_three);
	free(animal_four);

	return 0;
}

Note the use of the same contract make_sound()… despite different functions actually being called. We have now built a polymorphic animal-interface.

This idea can be expanded to a larger plugin structure, where you would have a factory responsible for determining if you have 3D sound or not in your machine, and returning a Sound-interface for instance, that would be tailored to call the right functions, but the caller would only need to know one function call, like in polymorphic OO.

An interesting new development in languages is Go. Go is a C-equivalent, in that it is non-OO and compiled, and it has a C-like syntax. However Go was designed to add some features to development in such a paradigm. It adds native thread support for instance, as well as support for inter-process-messaging, as part of standard syntax. It also has garbage collection of pointers (cleaning up for you). In light of our example above however, it is more interesting that it also supports the notion of an interface, much like Java or C#. This makes code like the above, much easier to grasp and read, as it actually defines a contract type as such.

For reference i implemented the above example in Go, to give you an idea of its structure.

So how to do it in Go, what is the difference?

First we have the Animal-package:

//Animal.go
package Animals

import "fmt"

//Animal iface, for polymorphic behaviour
type Animal interface {
	MakeSound()
}

//Types, these could be declared elsewhere, however these are now implicitly animals
type Dog struct {
	id int
}

type Cow struct {
	id int
}

type Cat struct {
	id int
}

//Spawning factory, can return pointer to local, will auto-move to heap
func CreateAnimal(animaltype string) (ani Animal) {
	switch animaltype {
	case "Dog":
		return &Dog{10}

	case "Cow":
		return &Cow{20}

	case "Cat":
		return &Cat{30}

	default:
		return &Dog{10}
	}
}

//Funcs

//This will implicitly implement the iface, because func exist, attach the func to "objects" of types
func (dog Dog) MakeSound() {
	fmt.Println("I am a dog, woof, woof")
}

func (cow Cow) MakeSound() {
	fmt.Println("I am a cow, moo, moo")

}

func (cat Cat) MakeSound() {
	fmt.Println("I am a cat, meow, meow")
}

The interesting thing here is that we have different ACTUAL animal types, but they are all of interface Animal. You see, in Go, you may tie a function to a specific type. Now as you can see at the end this allows for the functions to be tied implicitly to these types. However, Go specfies that as soon as a function is tied to a type with the name and signature of an interface type, it also IS such an interface contract. There is no implicit definition of this like in Java:

 
class SomeClass implements iface_x {...}

We can now therefore refer to our animals as Animal instead of the actual type:

//Base.go
package main

import "tests/goifaces/animals"

//Printer function, to demonstrate the use of generic iface Animal, note, this method works on the generic Animal iface
//so it does not need to know the type
func PrintAnimalSound(ani Animals.Animal) {
	ani.MakeSound()
}

func main() {
	//Main

	//Get a pointer to an animal, generic interface type
	var ani_one Animals.Animal
	var ani_two Animals.Animal
	var ani_three Animals.Animal

	ani_one = Animals.CreateAnimal("Dog")
	ani_two = Animals.CreateAnimal("Cow")
	ani_three = Animals.CreateAnimal("Cat")

	//Call Printer, note it does not need to know the type of the animal
	PrintAnimalSound(ani_one)
	PrintAnimalSound(ani_two)
	PrintAnimalSound(ani_three)

}

Note here that we declare variables of the type Animal. That way, when we call the print function, we can pass it as is, despite it actually being a dog, cow, cat etc. The caller does not need to know, as it will assume it is of the interface type Animal and thus has the MakeSound() function tied to itself.

Both of these cases can be expanded to fully featured plugin systems based on contracts.

This shows that partial OO-like constructs can and should be used in non-OO languages, when there is a need for it. You just have to work a bit harder to implement it. On the other hand, the Go example shows that Go is really well suited to this kind of development, despite being non-OO, due to some choices made in its design.