这是非常具体的,有点难以解释,而且很可能不可能,但是这里有。
我想实现< errno.h> 。 (我的爱好项目正在实施一个标准的 C 库。)
这种天真的方式是:
// in <errno.h>
extern int errno;
// in some .c file
int errno = 0;
这很有效。但它有一个缺点:如果调用数学库函数,总是必须在执行后查询FPU状态以适当地设置errno
。这使得FPU在数学繁重的应用程序中停滞不前。
C 标准通过将其设为宏来支持errno
的延迟评估:
int * __errno();
#define errno *__errno()
这样,errno
仅在实际请求其值时“设置”:
// "__errno()" returns the location of the current errno value,
// the "errno" macro turns it into a value.
x = errno;
在库的其余部分给出一些逻辑,只有在调用的最后一个库函数实际上是使用 FPU,和{{1}的值时,才需要查询FPU状态。实际上是请求。
到目前为止,一切都很好。但另一种方法是让我头疼:
errno
根本不要求errno = 0;
的值。 但是errno
不知道这一点,并且如果调用的最后一个库函数正在使用FPU,它将查询FPU状态。
现在我没有办法避免这种情况(即__errno()
宏或errno
函数以某种方式有所不同,具体取决于它们是否用于左侧或右侧赋值算子),我几乎满足于接受这个。
但也许你们中的任何一个人都有一个好主意?
答案 0 :(得分:4)
这里没有精彩的想法,但允许通过函数调用实现afaik errno
,以使标准库的线程安全实现成为可能。我不认为懒惰地查询FPU是一个好主意:在这种情况下你将如何避免不一致:
errno
errno
呼叫__errno()
是否应该清除标志?
根据标准,errno
是否必须由数学函数设置取决于math_errhandling
的值(具体而言,math_errhandling & MATH_ERRNO
)。
我要做的是通过预处理程序指令使错误报告可选,并允许程序员在编译时设置math_errhandling
的值。这意味着数学函数不能事先静态编译,但必须驻留在标题中,无论如何,这可能是一个好主意,允许没有链接时优化的编译器内联这些函数。
答案 1 :(得分:2)
你在这里有一个很好的分析,为什么整个errno
机制是如此令人不满意。您也可以在Kernighan的“标准C库”中找到类似的分析。
线程安全实现也需要errno
的函数形式,其中不同的线程具有通过特定于线程的errno
访问的单独错误值。
为了安全起见,您的宏应该在它周围加上括号:
#define errno (*__errno())
答案 2 :(得分:1)
更多评论而不是答案,但如果没有代码格式化,则无法理解。
如果懒洋洋地检查FPU标志,那么你将如何在这里获得正确的行为(数学函数是特殊的,因为它们保证在没有问题时不会修改errno)?
errno = 0;
x = acos(y);
z = <exp>;
if (errno != 0) {
/* can't come here even if y is a valid argument but <exp> modified the flags if it didn't call any functions */
}