我想知道向 C 方法添加属性是否会违反标准合规性。 因为在glibc 2.38中,为
nonnull
和fclose()
添加了一个freopen()
属性,如下所示:
extern int fclose (FILE *__stream) __nonnull ((1));
在 C++ 中,这种更改也很令人头痛,因为以下所有行现在都会创建警告:
std::unique_ptr<FILE, decltype(&fclose)> fptr(fp, fclose);
但是POSIX标准中没有这样的属性。我想知道 glibc 添加此属性是否会破坏 POSIX 合规性。有什么想法吗?
我们可以在不违反标准的情况下向标准函数声明添加属性吗?
标准 C 中还没有任何属性,因此您无法使用它们并保持标准兼容/可移植性。即将推出的 C23 计划添加一些属性。
此外,带有 2 个前导下划线的标识符(例如
__stream
)是为标准库(ISO 9899:2018 7.1.3)保留的,因此除非您正在编写 C 标准库,否则不应这样命名标识符。根据经验,它们根本不应该以下划线开头。
使用
gcc
或 g++
进行编译时,glibc 2.38 中带有 __nonnull
宏的新声明扩展为
extern int fclose (FILE *__stream) __attribute__ ((__nonnull__ (1)));
这些编译器可以处理非标准
__attribute__ ((__nonnull__ (1)))
。对于无法处理该属性的编译器,__nonnull
最终会扩展为空。
但是,在编译这一行时
std::unique_ptr<FILE, decltype(&fclose)> fptr(fp, fclose);
然后
g++
发出此警告:
warning: ignoring attributes on template argument ‘int (*)(FILE*)’ [-Wignored-attributes]
std::unique_ptr<FILE, decltype(&fclose)> fptr(fp, fclose);
即使您将
fclose
打包在一个函数中,并且在自由函数上使用 C++ [[attribute]]
语法提供相同的属性,您也会收到相同的警告:
[[gnu::nonnull(1)]] int closer(std::FILE* fp) {
// (1) may be omitted since there's only one argument to the function
return std::fclose(fp);
}
由于
std::fclose
不在指定可寻址函数列表中,因此您永远不应该直接获取它的地址。要么将 std::fclose
调用放入上面的函数中并接受警告 - 或者将调用打包到函子中
就像 lambda 一样:
auto closer = [] (std::FILE* fp) [[gnu::nonnull]] { std::fclose(fp); };
std::unique_ptr<std::FILE, decltype(closer)> fptr(fp, closer);
或用户定义的类型:
struct closer {
[[gnu::nonnull]] void operator()(std::FILE* fp) const {
std::fclose(fp);
}
};
// ...
std::unique_ptr<std::FILE, closer> fptr(fp);
这样,警告就会消失,因为该属性不在提供的类型上,而是在调用运算符上。
当然,如果您愿意跳过通过提供
[[gnu::nonnull]]
给出的潜在优化,您可以将其从上面的免费 closer
函数中删除,并使用它来避免警告。