/druzhba

Experimental statically-composed component framework written in Zig

Primary LanguageZigApache License 2.0Apache-2.0

Druzhba

[ˈdruʐbə] with a rolled R

An experimental statically-composed toy component framework written in Zig.

const druzhba = @import("druzhba");

const Counter = @import("components.zig").Counter;
const addApp = @import("components.zig").addApp;

fn addSystem(comptime ctx: *druzhba.ComposeCtx) void {
    // Instantiate a cell (the smallest unit of encapsulation and message passing)
    const counter = ctx.new(Counter).withAttr(100);
    const counter_in_count = counter.in("count");

    // Instantiate a subsystem
    const app = addApp(ctx);

    // Wire things up
    ctx.connect(app.out_count, counter_in_count);
    ctx.entry(app.in_main);
}

// Realize the system
const System = druzhba.Compose(addSystem);
var system_state: System.State() = undefined;       // → RAM
const system = comptime System.link(&system_state); // → ROM

pub fn main() !void {
    // Initialize the system and call the entry point (`app.in_main.main`)
    system.init();
    system.invoke("main");
}

Usage

To run the example app: zig build run

To use it in your application: Since Zig doesn't have a package manager yet, import this repository as a Git submodule. Add exe.addPackagePath("druzhba", "path/to/druzhba.zig") to your build.zig.

What is this?

Statically-composed component framework

“[Component-based software engineering (CBSE)] is a reuse-based approach to defining, implementing and composing loosely coupled independent components into systems.” — Component-based software engineering - Wikipedia

Component-based development is realized with the help of a software framework such as AUTOSAR, COM, and JavaBeans. Traditionally, they have been designed for run-time composition of components, meaning components and connections between them are constructed at run-time. For systems that don't change at run-time, this is an unnecessary overhead — it increases the system boot time, reduces the opportunity for compiler optimization, defers error checks, prevents the use of ROM (which is more abundant than RAM in microcontrollers), reduces the system security, and makes memory consumption harder to predict.

Statically-composed component frameworks are designed to address this problem. Components are instantiated based on a system description provided in a format the framework can process without a run-time knowledge. Their memory layouts are statically determined, and method calls between components are realized as direct function calls (when possible), only leaving run-time data that is absolutely necessary. There are a few academic researches focusing on such frameworks, showing their benefits for real-time and/or embedded systems.

Zig

Zig is a system programming language with a powerful compile-time reflection and evaluation feature. One possible way to explain it is that Zig is a fresh take on C/C++ metaprogramming — it provides access to low-level memory operations (including unsafe ones) like C/C++ do, but instead of C's lexical macros and C++'s template metaprogramming as well as gazillions of its retrofitted features, Zig offers a simple yet powerful feature set for metaprogramming that does the same or better job. This project (ab)uses it to create a statically-composed component framework.

I did not choose Rust for this project because it would not be interesting. Rust is much limited on regard to metaprogramming. Macros in Rust are just macros after all and do not have access to the information outside the locations where they are used. A build script could be used to generate code, but it lacks novelty as it is no different from what existing solutions do. Furthermore, whichever way I choose, I would have to implement its own module resolution system when the language already has one.

Style guide

Naming convention (preliminary)

  • PascalCase for signatures (constructed by druzhba.defineSig(struct { ... }), having type druzhba.Sig)
  • PascalCase for classes (constructed by druzhba.defineClass().build(), having type druzhba.Class)
    • Ditto.
  • addPascalCase for subsystems (functions receiving comptime ctx: *druzhba.ComposeCtx)

This does not apply to the internal implementation of this library.

License

This project is dual-licensed under the Apache License Version 2.0 and the MIT License.