我们可以在不违反标准的情况下向标准函数声明添加属性吗?

问题描述 投票:0回答:2

我想知道向 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 posix glibc
2个回答
1
投票

我们可以在不违反标准的情况下向标准函数声明添加属性吗?

标准 C 中还没有任何属性,因此您无法使用它们并保持标准兼容/可移植性。即将推出的 C23 计划添加一些属性。

此外,带有 2 个前导下划线的标识符(例如

__stream
)是为标准库(ISO 9899:2018 7.1.3)保留的,因此除非您正在编写 C 标准库,否则不应这样命名标识符。根据经验,它们根本不应该以下划线开头。


1
投票

使用

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
函数中删除,并使用它来避免警告。

© www.soinside.com 2019 - 2024. All rights reserved.