Alignment and casting for anyopaque pointer

I’m working with a c audio library miniaudio that has a callback function dataCallback with an p_output: ?*anyopaque parameter.

How do I go about checking the alignment and casting to the correct type which is a 32 bit floating point array?

What I have so far:

fn dataCallback(p_device: [*c]c.ma_device, p_output: ?*anyopaque, p_input: ?*const anyopaque, frame_count: c.ma_uint32) callconv(.C) void {
    _ = p_input;

    if (p_device.*.type == c.ma_device_type_playback) {
        if (p_output) |out| {
            // check pointer alignment
            if (@alignOf(@TypeOf(out)) == @alignOf([*]f32)) {
                // safe to cast the pointer since it's properly aligned
                const output_ptr: [*]f32 = @as([*]f32, @ptrCast(out));

Not sure if my syntax is correct, but I’m getting:

error: cast increases pointer alignment
                const output_ptr: [*]f32 = @as([*]f32, @ptrCast(out));


I tried swapping for @alignCast and it errored telling me to use @ptrCast.

Then I had the strangest idea, let’s use both! :person_shrugging:


const output_ptr = @as([*]f32, @ptrCast(@alignCast(out)));

Some time ago I run into similar problems and eventually I made this helper function:

pub fn opaqPtrTo(ptr: ?*anyopaque, comptime T: type) T {
    return @ptrCast(@alignCast(ptr));

and then something like

var pd = util.opaqPtrTo(, *ListenerData);

Well the input was a const so. . . .

// const array = [_][]const u8{"@Cast"} ** 3; heehee
const p_input_frames = @as([*]f32, @constCast(@ptrCast(@alignCast(in))));
1 Like

The 0.11.0 release notes section on pointer casts can be helpful for understanding what’s going on here:

The builtins @addrSpaceCast and @alignCast would become quite cumbersome to use under this system as described, since you would now have to specify the full intermediate pointer types. Instead, pointer casts (those two builtins and @ptrCast) are special. They combine into a single logical operation, with each builtin effectively “allowing” a particular component of the pointer to be cast rather than “performing” it. (Indeed, this may be a helpful mental model for the new cast builtins more generally.) This means any sequence of nested pointer cast builtins requires only one result type, rather than one at every intermediate computation.


test "pointer casts" {
    const ptr1: *align(1) const u32 = @ptrFromInt(0x1000);
    const ptr2: *u64 = @constCast(@alignCast(@ptrCast(ptr1)));
    _ = ptr2;
$ zig test pointer_cast.zig
1/1 test.pointer casts... 
OK All 1 tests passed.