C++ comptime, can we do this in Zig?

Many of us probably know about C++ template meta programming etc. They’ve been comptime programming over there for a few decades, and the language has been slowly improving to make this slightly less torturous by generating better error messages (C++ concepts), enforcing predictable semantics (constinit and consteval), etc.

I am probably an advanced beginner with some of the techniques, but I had no idea things like the rest of this post were possible. My challenge for the community is to see if we can do this with Zig. If we can, I’m certain it will be much more pleasing than the following.

Have a look at the following code. It’s from an open source library published by an Intel firmware team. They use it to program their power management components, if I understand correctly. These are severely resource constrained components, but comptime is unlimited.

In particular, have a look at the say_hello_world struct. It extends another struct by adding a function, and this is activated by explicit composition in the hello_world struct.

#include <cib/cib.hpp>
#include <iostream>

struct say_message : public cib::callback_meta<>{};

// the 'core' component exposes the 'say_message' service for others to extend
struct core {
    constexpr static auto config = cib::exports<say_message>;
};

// the 'say_hello_world' component extends 'say_message' with its own functionality
struct say_hello_world {
    constexpr static auto config =
        cib::extend<say_message>([](){
            std::cout << "Hello, world!" << std::endl;
        });
};

// the 'hello_world' project composes 'core' and 'say_hello_world'
struct hello_world {
    constexpr static auto config =
        cib::components<core, say_hello_world>;
};

// the nexus instantiates the project
cib::nexus<hello_world> nexus{};

int main() {
    // the fully extended and built services are ready to be used
    nexus.service<say_message>();
    return 0;
}

I’m interested to see if we can do the same thing with Zig. I don’t have the brain power right now to tackle this but I thought it might be of broader interest as a challenge of sorts. I hope to dive into it a bit at some point myself.

The library is here: intel/compile-time-init-build: C++ library for composing modular firmware at compile-time. (github.com)

In Zig, the code would be much simpler:

const std = @import("std");
const cib = @import("cib");

var nexus: cib.Nexus(struct {
   pub fn sayHelloWorld() {
       std.debug.print("Hello, world!\n", .{});
   }
}) = .{};

pub fn main() void {
    nexus.services.sayHelloWorld.start();
}

Nexus() would scan the namespace given to it for public functions, for each of which a custom struct is defined at comptime. All of these then get added as fields into the struct returned by Nexus().

Have a look at my project. I think it’ll give you a good idea on what can be done with the help of Zig comptime programming.

3 Likes

Come join us in the Zig Embedded Group Discord! I’m actually familiar with this Intel project, I watched a couple talks on it a year or so ago. We do things I would consider fairly similar with comptime in MicroZig, things like checking clock configuration validity, peripheral config validity, etc. all at compile time! @chung-leong’s answer definitely captures the spirit of what the Intel team is after in their project: extensive complicated verification at compile time with no run-time performance cost for deeply constrained systems. If I remember correctly they were doing all sorts of complicated state verification as well at compile time, which again, should in theory be possible in Zig comptime, although at times I would question if it’s worth the code readability hit.

3 Likes