Hi Bruce,

You're joining in this discussion starting in the middle, so you probably 
missed the earlier explanation.

On 11 Jul 2013, at 05:21, Bruce Evans <b...@optusnet.com.au> wrote:

> I don't see how any conforming program can access the isnan() function
> directly.  It is just as protected as __isnan() would be.  (isnan)()
> gives the function (the function prototype uses this), but conforming
> programs can't do that since the function might not exist.  Maybe some
> non-conforming program like autoconfig reads <math.h> or libm.a and
> creates a bug for C++.

The cmath header defines a template function isnan that invokes the isnan 
macro, but then undefines the isnan macro.  This causes a problem because when 
someone does something along the lines of using namespace std then they end up 
with two functions called isnan and the compiler gets to pick the one to use.  
Unfortunately, std::isnan() returns a bool, whereas isnan() returns an int.  

The C++ headers are not required to be conforming C code, because they are not 
C, and our math.h causes namespace pollution in C++ when included from <cmath>.

> The FreeBSD isnan() implementation would be broken by removing the
> isnan() function from libm.a or ifdefing it in <math.h>.  Changing the
> function to __isnan() would cause compatibility problems.  The function
> is intentionally named isnan() to reduce compatibility problems.


On OS X this is avoided because their isnan() macro expands to call one of the 
__-prefixed inline functions (which adopt your suggestion of being implemented 
as x != x, for all types).  I am not sure that this is required for standards 
conformance, but it is certainly cleaner.  Your statement that having the 
function not called isnan() causes compatibility problems is demonstrably 
false, as neither OS X nor glibc has a function called isnan() and, unlike us, 
they do not experience problems with this macro.  

It would also be nice to implement these macros using _Generic when compiling 
in C11 mode, as it will allow the compiler to produce more helpful warning 
messages.  I would propose this implementation:


#if __has_builtin(__builtin_isnan)
#define isnan(x) __builtin_isnan(x)
#else
static __inline int
__isnanf(float __x)
{
        return (__x != __x);
}
static __inline int
__isnand(double __x)
{
        return (__x != __x);
}
static __inline int
__isnanl(long double __x)
{
        return (__x != __x);
}
#if __STDC_VERSION__ >= 201112L
#define isnan(x) _Generic((x),     \
        float: __isnanf(x),        \
        double: __isnand(x),       \
        long double: __isnanl(x))
#else
#define isnan(x)                                                \
        ((sizeof (x) == sizeof (float)) ? __isnanf(x)           \
             : (sizeof (x) == sizeof (double)) ? __isnand(x)    \
             : __isnanl(x))
#endif
#endif

For a trivial example of why this is an improvement in terms of error 
reporting, consider this trivial piece of code:

int is(int x)
{
        return isnan(x);
}

With our current implementation, this compiles and links without any warnings, 
although it will have somewhat interesting results at run time.  With the 
__builtin_isnan() version, clang reports this error:

isnan.c:35:15: error: floating point classification requires argument of
      floating point type (passed in 'int')
        return isnan(x);
                     ^
(and then some more about the macro expansion)

With the C11 version, it reports this error:

isnan.c:35:15: error: controlling expression type 'int' not compatible with any
      generic association type
        return isnan(x);
                     ^



David

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to