Skip to content

Repository files navigation

About

Interface.zig is object oriented programming library for Zig language.

Primary goal is to reach C++-like polimorphism into Zig ecosystem.

Design decisions

Below design decision are worth to be aware of:

  1. Virtual functions can be declared only inside interface and not inside base / child structs.
  2. Interface contains pure virtual functions ( = 0 from C++). It's verified at comptime when .interface() is called
  3. Methods can be overridden in childs, so base classes are possible to be implemented
  4. Fields can't be overidden except of 'base' field
  5. Base class must be added as first field by now, trying to find solution to automatize this
  6. new/delete is automatically added to interface implementations to create owning interface instance
  7. Interface uses fat pointer technique, so owner of VTable and pointer to object is interface, this may be changed if I find reason to move it into child structs

How to use it

Firstly add dependency to your build.zig.zonzig fetch --save=modules/oop git+https://github.com/matgla/oop.zig/#HEAD

Then import module in build.zig. For example:

 const oop = b.dependency("modules/oop", .{});
tests.root_module.addImport("interface", oop.module("interface"));

Example

Let's write below C++ example:

#include<iostream>
#include<memory>// Interface classclassIAnimal {
public:virtualvoidspeak() const = 0;
virtualvoiddescribe() const = 0;
virtualvoidplay(const std::string& toy) const = 0;
virtual~IAnimal() = default;
};
// Base class implementing the interfaceclassAnimal : publicIAnimal {
protected:
std::string name;
int age;
public:Animal(const std::string& name, int age) : name(name), age(age) {}
voiddescribe() constoverride {
std::cout << name << " is " << age << " years old." << std::endl;
}
};
// Derived class 1classDog : publicAnimal {
std::string breed;
public:Dog(const std::string& name, int age, const std::string& breed)
: Animal(name, age), breed(breed) {}
voidspeak() constoverride {
std::cout << name << " says: Woof!" << std::endl;
}
voiddescribe() constoverride {
std::cout << name << " is " << age << " years old " << breed << "." << std::endl;
}
voidplay(const std::string& toy) constoverride {
std::cout << name << " doesn't like: " << toy << "." << std::endl;
}
};
// Derived class 2classCat : publicAnimal {
public:Cat(const std::string& name, int age)
: Animal(name, age) {}
voidspeak() constoverride {
std::cout << name << " says: Meow!" << std::endl;
}
voidplay(const std::string& toy) constoverride {
std::cout << name << " plays with " << toy << "." << std::endl;
}
};
voidtest_animal(IAnimal *animal) {
animal->describe();
animal->speak();
animal->play("Mouse");
}
intmain() {
std::unique_ptr<IAnimal> cat = std::make_unique<Cat>("Garfield", 7);
std::unique_ptr<IAnimal> dog = std::make_unique<Dog>("Lassie", 12, "Rough Collie");
test_animal(cat.get());
std::cout << std::endl;
test_animal(dog.get());
}
// examples/animals.zigconststd=@import("std");
constinterface=@import("interface");
constIAnimal=interface.ConstructInterface(struct {
pubconstSelf=@This();
pubfnspeak(self: *constSelf) void {
returninterface.VirtualCall(self, "speak", .{}, void);
}
pubfndescribe(self: *constSelf) void {
returninterface.VirtualCall(self, "describe", .{}, void);
}
pubfnplay(self: *constSelf, toy: []constu8) void {
returninterface.VirtualCall(self, "play", .{toy}, void);
}
pubfndelete(self: *Self) void {
interface.VirtualCall(self, "delete", .{}, void);
interface.DestructorCall(self);
}
});
constAnimal=interface.DeriveFromBase(IAnimal, struct {
constSelf=@This();
name: []constu8,
age: u32,
pubfndescribe(self: *constSelf) void {
std.debug.print("{s} is {d} years old.\n", .{ self.name, self.age });
}
});
constDog=interface.DeriveFromBase(Animal, struct {
constSelf=@This();
base: Animal,
breed: []constu8,
pubfncreate(name: []constu8, age: u32, breed: []constu8) Dog {
returnDog.init(.{ .base=Animal.init(.{
.name=name,
.age=age,
}), .breed=breed });
}
pubfnspeak(self: *constSelf) void {
std.debug.print("{s} says: Woof!\n", .{interface.base(self).name});
}
pubfndescribe(self: *constSelf) void {
std.debug.print("{s} is {d} years old {s}.\n", .{ interface.base(self).name, interface.base(self).age, self.breed });
}
pubfnplay(self: *constSelf, toy: []constu8) void {
std.debug.print("{s} doesn't like: {s}.\n", .{ interface.base(self).name, toy });
}
pubfndelete(self: *Self) void {
_=self;
}
});
constCat=interface.DeriveFromBase(Animal, struct {
constSelf=@This();
base: Animal,
pubfncreate(name: []constu8, age: u32) Cat {
returnCat.init(.{ .base=Animal.init(.{
.name=name,
.age=age,
}) });
}
pubfnspeak(self: *constSelf) void {
std.debug.print("{s} says: Meow!\n", .{interface.base(self).name});
}
pubfnplay(self: *constSelf, toy: []constu8) void {
std.debug.print("{s} plays with {s}.\n", .{ interface.base(self).name, toy });
}
pubfndelete(self: *Self) void {
_=self;
}
});
pubfntest_animal(animal: IAnimal) void {
animal.interface.describe();
animal.interface.speak();
animal.interface.play("Mouse");
}
pubfnmain() !void {
vargpa=std.heap.GeneralPurposeAllocator(.{
.safety=true,
}){};
constallocator=gpa.allocator();
defer_=gpa.deinit();
varcat=tryCat.InstanceType.create("Garfield", 7).interface.new(allocator);
defercat.interface.delete();
vardog=tryDog.InstanceType.create("Lassie", 12, "Rough Collie").interface.new(allocator);
deferdog.interface.delete();
test_animal(cat);
std.debug.print("\n", .{});
test_animal(dog);
}

Zig usingnamespace removal

Due to removal of using namespace feature all namespaces are named in current version of framework. To access interface(vtable) functions use .interface on object constructed from ConstructInterface.

To access base object there is base function exported, example usage: interface.base(self).name.

If access to object type is needed then use .InstanceType.

For creating interface object use .interface followed by .new or .create.

Destruction of objects

delete member field is reserved for deinitalization purposes, if your code needs to be deinitialized then add delete as virtual method and call delete.

delete may be called by framework when DestructorCall is executed.

About

No description, website, or topics provided.

Resources

Stars

22 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages