I wonder if adding attributes to a C method will break standard compliance or not.
Because in glibc 2.38, a nonnull
attribute is added to fclose()
and freopen()
like this:
extern int fclose (FILE *__stream) __nonnull ((1));
In C++, this change is a headache as well because all following lines creates a warning now:
std::unique_ptr<FILE, decltype(&fclose)> fptr(fp, fclose);
But there is no such attribute in POSIX standard. I wonder if adding this attribute by glibc breaks POSIX compliance. Any idea?
When compiling with gcc
or g++
(or clang
/clang++
), the new declaration with the __nonnull
macro in glibc 2.38 is expanded to
extern int fclose (FILE *__stream) __attribute__ ((__nonnull__ (1)));
These compilers can handle the non-standard __attribute__ ((__nonnull__ (1)))
. For a compiler that can't handle that attribute, __nonnull
ultimately expands to nothing.
However, when compiling this line
std::unique_ptr<FILE, decltype(&fclose)> fptr(fp, fclose);
then g++
emits this warning:
warning: ignoring attributes on template argument ‘int (*)(FILE*)’ [-Wignored-attributes]
std::unique_ptr<FILE, decltype(&fclose)> fptr(fp, fclose);
and you'll get the same warning even if you package fclose
in a function where you provide the same attribute using the C++ [[attribute]]
syntax on a free function:
[[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);
}
Both gcc and clang have bug reports about this, but fixing it would involve an ABI addition "Due to the way templates cause name managling to come into play", so the attribute is dropped and is not part of the Deleter type. The gcc report is in status SUSPENDED.
Since std::fclose
is not on the list of designated addressable functions, you should never take its address directly. Either you put the std::fclose
call in a function like above and live with the warning - or package the call in a functor
like a lambda:
auto closer = [] (std::FILE* fp) [[gnu::nonnull]] { std::fclose(fp); };
std::unique_ptr<std::FILE, decltype(closer)> fptr(fp, closer);
or in a user-defined type:
struct closer {
[[gnu::nonnull]] void operator()(std::FILE* fp) const {
std::fclose(fp);
}
};
// ...
std::unique_ptr<std::FILE, closer> fptr(fp);
and with that, the warning goes away since the attribute is not on the type supplied but on the call operator.
Of course, if you are willing to skip the potential optimization given by supplying [[gnu::nonnull]]
you can just remove it from the free closer
function above and use that to avoid the warning.