0.16.0 Released

The usual solution would just be to write a small .h file with this content:

#include <stdio.h>
#include <stdlib.h>

You would add build.zig logic to translate that one header, and then you would use it everywhere. There’s really no need to only allow one part of your Zig code to use stdio while only allowing another part to use stdlib: it’s no more efficient (in fact it’s considerably less efficient!) nor is it safer in any way that I can see.

However, if you really are just using headers from the C standard library (stdio and stdlib), there’s a good chance you shouldn’t be using @cImport or translate-c whatsoever! Instead, std.c contains appropriate definitions for many symbols in those headers, and might cover your use cases. Those definitions are objectively superior to using @cImport/translate-c, because they have more precise types, improving type safety.

9 Likes

Really happy to see the 0.16.0 release!

One thing that seems to be omitted from the release notes is that packed containers can no longer contain vector types. For example, this worked in 0.15.2 and before, but not in 0.16.0.

const bar: @Vector(6, u8) = @splat(0);
const Foo = packed struct {
    bar: @Vector(6, u8),
};
const foo: Foo = .{
    .bar = bar,
};
_ = foo;

I think the upgrade recommendation should be similar to that of the recommendation for pointers in packed containers - to use the backing int type of the Vector for the field, and @bitCast assignment.

const bar: @Vector(6, u8) = @splat(0);
const Foo = packed struct {
    bar: @Int(.unsigned, @bitSizeOf(@TypeOf(bar))),
};
const foo: Foo = .{
    .bar = @bitCast(bar),
};
_ = foo;
1 Like

Another typo:

@andrewrk

In addition, two more error styles are available, verbose_clear and minimal_clear. These are similar to verbose and minimal (<- was clear) respectively,

Great, that it’s finally here!

Little quirk still on the webpage: The link to the std lib documentation under Documentation - The Zig Programming Language still points to master not 0.16.0: https://ziglang.org/documentation/master/std/

1 Like

If you already have some C library installed and you want to write a single Zig file that imports and calls some functions from it, you can still do that:

// script.zig
const std = @import("std");

pub fn main() void {
    const c = @import("c");
    const log = std.log.scoped(.main);
    log.info("running '{s}'", .{@src().file});
    log.info("imported {d} declarations from c", .{std.meta.declarations(c).len});
    log.info("strerror(0) = {s}", .{c.strerror(0)}); // for demo purposes
}

// the rest below
./zig-0.16.0/zig build --build-file script.zig 
info(main): running 'script.zig'
info(main): imported 602 declarations from c
info(main): strerror(0) = Success

The “trick” is that there’s nothing stopping you from having both main and build in the same file.

script.zig
// the rest
pub fn build(b: *std.Build) void {
    const src = @src();
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const headers =
        \\#include <string.h>
    ;

    const write_tmp_file = b.addWriteFiles();
    const tmp_file = write_tmp_file.add(src.module ++ ".h", headers);

    const translate_c = b.addTranslateC(.{
        .optimize = optimize,
        .target = target,
        .root_source_file = tmp_file,
    });

    const exe = b.addExecutable(.{
        .name = src.module,
        .root_module = b.createModule(.{
            .root_source_file = b.path(src.file),
            .target = target,
            .optimize = optimize,
            .imports = &.{.{
                .name = "c",
                .module = translate_c.createModule(),
            }},
        }),
    });

    const run_cmd = b.addRunArtifact(exe);
    b.getInstallStep().dependOn(&run_cmd.step);
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }
}

I agree it isn’t exactly super convenient now, but it is still possible.

(The pattern is from the “cursed Zig” thread, but hey, if it’s relevant and useful…)

5 Likes

This isn’t a single-file Zig project, though. It’s a multi-file Zig project, with only one .zig file. What you’re describing has dependencies, channeling those through the dependency-management system seems reasonable.

I don’t want the Zig team treating “what’s been blogged about” as a decision criterion for the future of the language. The first-class integration with C is still a key feature of the language, and that won’t change. What’s changed is how to do it, that’s all.

2 Likes

That Zig includes C (“depends on a C compiler”) is one of the most appealing features of Zig. Some C interface can be found in any language. Zig delivers more, and that allowed me to convince other programmers to have a look on Zig. Now that this disappears it will be harder.

The more complicated it is to integrate with existing C code the less appealing will be Zig. I never saw anything easy like @cImport in any other language. As said, sad to see such an important feature being removed.

2 Likes

This is a long-standing misconception. While rooted in (IMHO) poor communication on the part of the team, when LLVM-independence was first decided on, that was a long time ago.

I just put a project up, which you shouldn’t use btw, use Night Watch, but it’s a convenient illustration:

Here’s the build.zig, more complex than usual btw because file-watching is quite platform dependent, and here’s the one line in the wrapper where it all gets used.

It’s just FUD to say that things have gotten harder. They’ve moved around, that’s it. Configuration is in the dependency manager where it belongs.

3 Likes

They are not getting rid of this, or other c related features, they are just decoupling things to make their own lives easier and so zig doesn’t have a hard dependency on any particular c compiler.

This, and other c related features, will almost certainly always be supported. They are just changing a minor detail; that being removing the in language feature that was just an over simplified, foot gun prone, and less flexible abstraction over the build system feature.

2 Likes

In your link “one line” I find:

pub const c = @cImport({

@cInclude("efsw/efsw.h");

});

Now I’m confused – this will go away, won’t it?

Btw, this little nugget from the release notes:

This is progress towards transitioning from a library dependency on LLVM to a process dependency on Clang.

I love that sentence. It’s much less catchy than “LLVM divorce” but so much more informative :wink:

Hindsight is 20/20 of course, but I wonder how much of the confusion (and drama!) could have been avoided if “transitioning from a library dependency on LLVM to a process dependency on Clang” would have been used from the start.

4 Likes

FWIW I think moving the translateC step into the build system (e.g. with a proper “configuration API” opens up a couple of opportunities which would be awkward with the @cImport builtin - mostly towards transforming the C API into a more ‘Zig idiomatic’ API.

This is basically the reason why for the sokol C headers I don’t rely on the automatic translation, but instead use a ‘semi-automatic’ approach via clang ast-dump where I’m adding special language ‘mappings’ to the C API, e.g. things like:

  • remove ‘namespace prefixes’ (sg_, sapp_, etc…), those are redundant in Zig
  • change symbol naming style to Zig conventions (e.g. map between snake-case, camelCase and PascalCase)
  • maybe even things like transparantly marshalling from Zig string-slices to C zero-terminated strings and back (tricky though)

None of this exists yet, but I imagine that such things make more sense when the translateC step is a build system API.

7 Likes

…yes:

const c = @import("efzw");

With some similarly trivial changes to the build file.

1 Like

I think the official translate c package does the first 2, though maybe not with the exact behaviour, but it should be configurable to at least be close.

1 Like

Here’s a neat tool when upgrading to 0.16. Grab the Zig source code (surely you have it already!) and git pickaxe search for something you wonder how to upgrade, and optionally grep within that result to sharpen the result:

git log -S"openSelfExe" -p -n 2 | rg -C5 "openSelfExe"
     const self_path = try std.process.executablePathAlloc(io, gpa);
     defer gpa.free(self_path);

-    var self_exe = try std.fs.openSelfExe(.{});
+    var self_exe = try std.process.openExecutable(io, .{});
     defer self_exe.close(io);

and variations thereof. Works nicely for a lot of stuff.

9 Likes

I don’t know, I think it was clarified the very first day but a lot of people just never took the time to understand it anyway.

1 Like

Moving @cImport into the build system is very frustrating for competitive programming.

Indexing into vectors will be missed.

The new metaprogramming built ins seem very fun.

// zig fmt: off
inline fn asArray(v: anytype) 
  [@typeInfo(@TypeOf(vector)).vector.len]
   @typeInfo(@TypeOf(vector)).vector.child { 
    return v;
}
// zig fmt: on

Cursed? Cursed. But you don’t have to look at it.

5 Likes

I think my issue with it personally is that it’s one more thing I have to check on compiler explorer to make sure that it doesn’t do something obviously bad, not necessarily having to write the boilerplate. I guess you can argue the same about indexing, but I guess I trusted that more subconsciously.

Congratulation to the team on the release! It’s a big release.

Regarding “@Type Replaced with Individual Type-Creating Builtin Functions” does that mean @Type has been deprecated? Or is it enhanced with the individual type constructing directives?