简体   繁体   中英

How to allocate a struct of incomplete type in Zig?

I would like to use POSIX regex routines in Zig, but I can't work out how to allocate a pattern buffer ( regex_t ). I think the problem is because it is defined as typedef struct re_pattern_buffer regex_t (in my case, on a GNU system), and Zig cannot therefore tell how big it is.

In a standard zig init-exe project (Zig master as of today), I have the following main.zig :

const std = @import("std");
const c = @cImport({@cInclude("regex.h");});

pub fn main() anyerror!void {
    var re: c.regex_t = undefined;
    std.log.info("address of re {}", .{re});
}

In an otherwise default build.zig , I add a line exe.linkLibC(); .

Then, zig build gives the following error:

./src/main.zig:5:5: error: variable of type '.cimport:2:11.struct_re_pattern_buffer' not allowed
    var re: c.regex_t = undefined;
    ^

This is fair enough, but I cannot work out what I should do! I can see a couple of potential workarounds:

  1. Use struct re_pattern_buffer directly; but this is not portable, as it's not part of the POSIX standard (though at least musl and glibc use it as the underlying struct of regex_t ).
  2. Write a C function that malloc s and returns a regex_t , and use that. Should be fine, as all the POSIX APIs take a regex_t * , so Zig doesn't need to know how big a regex_t is if it only ever has to handle pointers.

But it would be nice to be able to do without either of these workarounds. Any hints?

struct_re_pattern_buffer is defined as opaque {}; in the c import source

// found by adding `--verbose-cimport` to the zig build
pub const struct_re_pattern_buffer = opaque {};
pub const regex_t = struct_re_pattern_buffer;

In the C header file, it's defined as

struct re_pattern_buffer
{
  … // http://hte.sourceforge.net/doxygenized-0.8.0pre1/structre__pattern__buffer.html
};
typedef struct re_pattern_buffer regex_t;

The reason it's not able to translate the struct correctly is because zig does not yet handle bitfields in cimported structs ( #1499 ). It should normally show an error in the cimport file, but it isn't this time for some reason:

pub const struct_re_dfa_t = opaque {}; // (no file):28:1: warning: struct demoted to opaque type - has bitfield

Use struct re_pattern_buffer directly; but this is not portable, as it's not part of the POSIX standard (though at least musl and glibc use it as the underlying struct of regex_t).

This will not work as zig was unable to translate the struct at all because of the bitfields.

Write a C function that mallocs and returns a regex_t, and use that. Should be fine, as all the POSIX APIs take a regex_t *, so Zig doesn't need to know how big a regex_t is if it only ever has to handle pointers.

This will work, and is generally the easiest workaround to any cimport issues.

regex_t* alloc_regex_t(void);
void free_regex_t(regex_t* ptr);

If you want to allocate it using a zig allocator, the workaround is pretty complicated:

To allocate it, you need to know its size. You could:

// header:
#include <stdint.h>
size_t my_sizeof_regex_t();
uint16_t my_alignof_regex_t();

// source:
#include <stdint.h>
#include <stdalign.h>
#include <regex.h>

size_t my_sizeof_regex_t() {
    return sizeof(regex_t);
}

uint16_t my_alignof_regex_t() {
    return alignof(regex_t);
}

In zig, allocate it into a byte array with a custom alignment specified.

var re_zig_slice = try allocator.allocBytes(
    c.my_alignof_regex_t(),
    c.my_sizeof_regex_t(),
    0,
    @returnAddress(),
);
var re = @ptrCast(*c.regex_t, re_zig_slice.ptr);
defer try allocator.rawFree(@ptrCast([*]u8, re)[0..c.my_sizeof_regex_t()]), c.my_alignof_regex_t, @returnAddress());

This is made extra complicated because in zig, allocators need to know the size and alignment of the object when they are allocating and when they are freeing. C's malloc stores the size internally in order to free properly and always uses a single alignment value for all things it allocates.

note: alternatively, you can use normal allocWithOptions and free methods if you use the maximum alignment for a struct in the c abi for the alignment, and @alignCast the ptrCasted pointer before freeing it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM