不写C包装器,从Hare读取errno的野路子方案
原创不写C包装器,从Hare读取errno的野路子方案
errno为什么是个麻烦:宏定义与跨语言困境
errno在C语言标准里被定义为宏,它的展开方式在不同平台上完全不同,而且编译器通常不会为此提供任何稳定性保证 来源:ART。这意味着当你从C以外的语言调用libc函数时,想要拿到错误码,要么依赖编译器特定的实现细节,要么老老实实写一段C代码来做桥接。作者选的是Hare语言,一个正在发展中的系统语言,它的FFI机制虽然能直接导入C函数,但宏展开这件事天然不在FFI的能力范围内。Hare的FFI只认符号名,不认预处理指令。
更棘手的是,libc里没有任何函数能直接返回errno的值 来源:ART。errno不是一个变量,不是一个指针指向的对象,它就是一个展开后可能变成全局变量、可能变成线程本地存储引用的宏。从C外面拿它,没有标准办法。作者给自己定了个约束,要求找到可移植方案,从C外部获取errno的值,且不写C包装函数 来源:ART。这个约束排除了最常见的思路,写一小段C代码让C函数把errno的值通过返回值传出来。
作者在文章标题里管自己的方案叫"a hacky fucked up way",发布日期是2026年9月25日 来源:ART。HN上这篇帖子只收了1个点,0条评论 来源:HN。这种社区反响本身也说明了一件事,这个方案确实hacky,但它解决的问题是真的。跨语言获取errno这件事,在Hare等新一代系统语言里迟早要面对。
strerror家族的取舍:只有perror能直接读errno
C标准定义了strerror和perror两个与错误码相关的函数;POSIX在此基础上又加了strerror_r和strerror_l 来源:ART。四个函数里,strerror系列函数的签名都是接受一个错误编号作为参数,返回对应的描述字符串。问题在于,它们都需要你告诉它错误编号是多少。而你想问libc的就是"errno现在是多少"。这些函数解决不了这个问题。它们的设计初衷是把任意数字翻译成人类可读的描述。strerror_r的变体虽然增加了线程安全性,但仍然要你先知道错误编号。
perror是例外。它是strerror家族中唯一直接读取errno的函数 来源:ART。调用perror之后,它会把错误字符串打印到stderr。但这里出现了作者面对的核心矛盾,能自动读取errno的函数把结果写到stderr去了,能返回错误字符串的函数却不读errno。要拿到perror输出的内容,你得从stderr里读回来。要拿到返回值形式的错误信息,你得先知道errno是多少。这是个死循环。
劫持stderr:Hare FFI的完整实现细节
作者的选择是劫持stderr。具体做法是在程序初始化阶段,先用dup保存fd 2(stderr)的原始文件描述符,然后用mkstemp创建一个临时文件,再用dup2把fd 2指向这个临时文件 来源:ART。此后perror的调用会把错误字符串写进这个临时文件。等需要的时候,通过read从临时文件里把内容读出来,就能拿到perror的输出,也就拿到了当前errno的描述信息 来源:ART。
Hare的FFI层通过@symbol注解导入了10个libc函数,close、dup、dup2、lseek、mkstemp、perror、read、strerror、unlink和write 来源:ART。代码里定义了SEEK_SET=0和STDERR_FILENO=2i两个常量,后者直接用于操作stderr的文件描述符 来源:ART。临时文件名模板是一个硬编码的字符数组/tmp/XXXXXX,交给mkstemp去填后面的随机后缀 来源:ART。错误类型被定义为export type error = !str,用字符串承载错误信息。临时文件捕获完成后用unlink清理,用close关掉临时文件描述符。
这个方案的代价显而易见,它有并发安全问题。perror不是线程安全的,因为errno本身就不是线程安全的。劫持stderr在多线程环境下更糟。但作者也明确说了这是a hacky fucked up way,目标是从C外部可移植地获取errno,不写C包装。如果这些约束成立,这个方案的荒谬程度就是你能接受的荒谬程度。我不确定这是否算"好"方案,但它在给定约束下是唯一不需要写一行C的解法。
原文:
A hacky f*d up way to portably access errno from outside of C · 来源:Hacker News
作者:itfan123 值班编辑 · 发布于 2026年10月26日
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123






