简体   繁体   English

如何在 Zig 中分配不完整类型的结构?

[英]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 ).我想在 Zig 中使用 POSIX 正则表达式例程,但我不知道如何分配模式缓冲区 ( 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.我认为问题在于它被定义为typedef struct re_pattern_buffer regex_t (在我的情况下,在 GNU 系统上),因此 Zig 无法判断它有多大。

In a standard zig init-exe project (Zig master as of today), I have the following main.zig :在一个标准的zig init-exe项目(今天的 Zig master)中,我有以下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();在其他默认的build.zig中,我添加了一行exe.linkLibC(); . .

Then, zig build gives the following error:然后, zig build给出以下错误:

./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;直接使用struct re_pattern_buffer 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 ).但这不是可移植的,因为它不是 POSIX 标准的一部分(尽管至少 musl 和 glibc 将它用作regex_t的底层结构)。
  2. Write a C function that malloc s and returns a regex_t , and use that.编写一个malloc并返回regex_t的 C 函数,然后使用它。 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.应该没问题,因为所有 POSIX API 都采用regex_t * ,所以如果只需要处理指针,Zig 就不需要知道regex_t有多大。

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 {}; struct_re_pattern_buffer被定义为opaque {}; in the c import source在 c 导入源中

// 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在 C 头文件中,它被定义为

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 ).它无法正确翻译结构的原因是 zig 尚未处理 cimported 结构中的位域( #1499 )。 It should normally show an error in the cimport file, but it isn't this time for some reason:它通常应该在 cimport 文件中显示错误,但由于某种原因这次不是:

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;直接使用struct re_pattern_buffer; 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).但这不是可移植的,因为它不是 POSIX 标准的一部分(尽管至少 musl 和 glibc 将它用作 regex_t 的底层结构)。

This will not work as zig was unable to translate the struct at all because of the bitfields.这将不起作用,因为由于位域,zig 根本无法翻译结构。

Write a C function that mallocs and returns a regex_t, and use that.编写一个 malloc 并返回 regex_t 的 C 函数,并使用它。 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.应该没问题,因为所有 POSIX API 都采用 regex_t *,所以 Zig 不需要知道 regex_t 有多大,如果它只需要处理指针。

This will work, and is generally the easiest workaround to any cimport issues.这将起作用,并且通常是任何 cimport 问题的最简单的解决方法。

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:如果您想使用 zig 分配器分配它,解决方法非常复杂:

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.在 zig 中,将其分配到指定了自定义对齐方式的字节数组中。

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.这变得更加复杂,因为在 zig 中,分配器在分配和释放时需要知道对象的大小和对齐方式。 C's malloc stores the size internally in order to free properly and always uses a single alignment value for all things it allocates. C 的 malloc 在内部存储大小以便正确释放,并且始终对它分配的所有内容使用单个对齐值。

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.注意:或者,如果您使用 c abi 中结构的最大对齐方式进行对齐,并且在释放它之前使用@alignCast指针 @alignCast ,则可以使用普通allocWithOptionsfree方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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