LibC++ v3.8 - Problems with ISO C wrapper headers

While getting our implementation of the compiler ready for v3.8 we came across a significant problem in the new implementation of LibC++’s handling of the ISO C Standard headers that needs to be fixed before the v3.8 release.

We discovered the problem when running some tests that were derived from real-world code. The initial example involved a C++ program including ‘<math.h>’ and using ‘fabs’ but the issue applies equally to the other headers, for example:

#include <math.h>

__fp16 y; // We use FP16 a lot

fabs(y); // really → fabs((double)y);

In C there is only one ‘fabs’ with the signature:

double fabs(double)

With the new implementation of the headers for LibC++, there is now a file named ‘c++/math.h’ that includes the C version and then supplements it for C++. When I use this, the above use of ‘fabs’ above results in an overload ambiguity error.

It’s easy to say “It’s C++, use ‘’”, but in the real-world C++ programmers very commonly use the C headers whether they should or not. In any case, ‘<math.h>’ does not belong to C++ and the C++ implementation should not interfere with the interface expressed by a foreign header when that header is included directly.

After looking further into this I realised that there is an inadvertent bug in the new implementation rather than an intent to interfere with the C interface. It also breaks C++’s ‘’ interface requirements. The supplementary elements used to be in ‘’ itself, but since they were extracted they are in the global namespace and not in ‘namespace std’ where they are intended to be, and they are consequently introducing overloads into the C usage. I will describe this for ‘fabs’ but the pattern appears to be in all of the new ISO C wrapper headers. In ‘c++/math.h’ we have (abbreviated for illustration):

#ifndef _LIBCPP_MATH_H

#define _LIBCPP_MATH_H

#include <__config>

#include_next <math.h>

#ifdef __cplusplus

// Missing: _LIBCPP_BEGIN_NAMESPACE_STD

#if !(defined(_LIBCPP_MSVCRT) || defined(_AIX))

inline _LIBCPP_INLINE_VISIBILITY float fabs(float __lcpp_x) _NOEXCEPT {return fabsf(__lcpp_x);}

inline _LIBCPP_INLINE_VISIBILITY long double fabs(long double __lcpp_x) _NOEXCEPT {return fabsl(__lcpp_x);}

#endif

template

inline _LIBCPP_INLINE_VISIBILITY

typename std::enable_if<std::is_integral<_A1>::value, double>::type

fabs(_A1 __lcpp_x) _NOEXCEPT {return fabs((double)__lcpp_x);}

// Missing: _LIBCPP_END_NAMESPACE_STD

#endif // __cplusplus

#endif // _LIBCPP_MATH_H

but because this is no longer between ‘_LIBCPP_BEGIN_NAMESPACE_STD’ and ‘_LIBCPP_END_NAMESPACE_STD’, the new declarations introduce overloads at the global namespace and not in the ‘std’ namespace.

Now when ‘<math.h>’ is included explicitly you get ‘fabs’ as follows:

double ::fabs(double) // From ‘include/math.h’

float ::fabs(float) // From ‘include/c++/math.h’

long double ::fabs(long double) // Also from ‘include/c++/math.h’

With the namespace correction explicitly including ‘<math.h>’ would produce:

double ::fabs(double) // From ‘include/math.h’

float std::fabs(float) // From ‘include/c++/math.h’

long double std::fabs(long double) // Also from ‘include/c++/math.h’

Which will not break the C compatible use case.

However, I think that using the ISO C math header explicitly like this should not be supplemented with partial ISO C++math header requirements, it’s neither C’s ‘<math.h>’ nor C++’s ‘’ and has odd incongruities such as:

#include <math.h>

float (*pf)(float) = std::fabs; // Okay, we have this

long double (*pld)(long double) = std::fabs; // Okay, we have this too

double (*pd)(double) = std::fabs; // Oops, no such function

This is not as serious as the namespace bug, but it is strangely inconsistent and shouldn’t really be there. What I propose for this is an equally simple change. In ‘’ replace:

#include <math.h>

with:

#define _LIBCPP_INCLUDING_STDC_HEADER

#include <math.h>

#undef _LIBCPP_INCLUDING_STDC_HEADER

and then in ‘c++/math.h’, replace:

#ifdef __cplusplus

with:

#ifdef _LIBCPP_INCLUDING_STDC_HEADER

I’ve only referred to ‘fabs’ and ‘<math.h>’, but of course it is all the overloaded “from C” functions in the ‘std::’ namespace that are affected, and the same problem is present in the other ISO C wrapper headers.

I will work on this over the weekend and hope to be able to submit a tested patch file on Monday or Tuesday that will correct this. I’m at SVN revision #258500 for the v3.8 branch at the moment, but I will update and merge to the most recent version before I submit a patch file for your consideration.

Thanks,

MartinO

Hi Martin,

While getting our implementation of the compiler ready for v3.8 we came
across a significant problem in the new implementation of LibC++'s handling
of the ISO C Standard headers that needs to be fixed before the v3.8
release.

You're mistaken about a lot of the things you say below, but I agree
that your example code ought to work, assuming that libc++ intends to
support __fp16 (but I don't think it currently does). See the bottom
of my mail for a suggestion of how to move forward with this.

We discovered the problem when running some tests that were derived from
real-world code. The initial example involved a C++ program including
'<math.h>' and using 'fabs' but the issue applies equally to the other
headers, for example:

#include <math.h>

...

__fp16 y; // We use FP16 a lot

fabs(y); // really -> fabs((double)y);

In C there is only one 'fabs' with the signature:

double fabs(double)

That's largely irrelevant. In C++, <math.h> is a C++ standard library
header, whose contents are specified by C++'s [c.math] and
[depr.c.headers]. These say that <math.h> provides the same set of
overloads in the global namespace that <cmath> provides in namespace
std. libc++ has recently changed to implement this rule (that has
*always* been part of the C++ standard). Thus your suggested approach
to fixing this is not appropriate.

With the new implementation of the headers for LibC++, there is now a file
named 'c++/math.h' that includes the C version and then supplements it for
C++. When I use this, the above use of 'fabs' above results in an overload
ambiguity error.

It's easy to say "It's C++, use '<cmath>'",

That wouldn't help. In namespace std, libc++ has always provided
overloads resulting in the ambiguity you're now seeing (and FWIW, this
is the overload set required by the C++ standard).

[...]

and has odd incongruities such as:

#include <math.h>

float (*pf)(float) = std::fabs; // Okay, we have this
long double (*pld)(long double) = std::fabs; // Okay, we have this too
double (*pd)(double) = std::fabs; // Oops, no such function

That seems a consequence of your (incorrect) patch. libc++'s current
<math.h> doesn't provide any of these in namespace std, consistent
with the requirements of the C++ standard. The intent is that no set
of #includes gives you a partial overload set for any of these
functions (except cases like 'abs', where C++ specifies that the
overload set is split across multiple headers).

The real issue is that libc++ doesn't support __fp16 (you should be
able to observe this with both past versions of libc++ and with trunk
if you change your testcase to use <cmath> and std::fabs). The right
way to fix this would be to add __fp16 overloads to <math.h> (in the
cases where the underlying compiler and language mode provide such a
type) and ensure its __promote does the right thing for __fp16. It's
up to the libc++ maintainers whether they consider this to be a
regression and whether they'd accept a patch for it in the 3.8
release.

But it's not a bug that libc++'s <math.h> now follows the C++ standard
when compiling in C++ mode.

I disagree. I'm not new to C++ either, I built my first C++ compiler in 1986 and have been involved in C++ standardisation for a very long time, and the recent changes are wrong.

Including "math.h" should NOT introduce overloaded versions of 'fabs' or other functions at global scope. It may only introduce the overloaded functions within "namespace std". The recent changes overload these functions at global scope. That is a bug.

My tests are running now, and when they are complete I will propose the patch, but I can assure you that overloading global symbols from the C headers is not compliant with C++.

  MartinO

Hi Martin,

If you disagree you’ll need to cite (or change) the standard. IMO the standard is explicitly clear on this topic.

15.2 [library.c] says:

The C ++ standard library also makes available the facilities of the C standard library, suitably adjusted to
ensure static type safety.

D.3 [depr.c.headers] says:

  1. every C header, each of which has a name of the form name.h, behaves as if each name placed in the standard
    library namespace by the corresponding cname header is placed within the global namespace scope. It is

unspecified whether these names are first declared or defined within namespace scope (3.3.6) of the namespace

std and are then injected into the global namespace scope by explicit using-declarations (7.3.3).

/Eric

It was *never* intended to add the C++ overloads to the global namespace.

I can now see how that is indeed what is written. Believe me, it was the farthest thought from *anyone’s* mind when these words were voted in. This is demonstrable by looking at the issue which introduced these words:

http://cplusplus.github.io/LWG/lwg-defects.html#456

The description includes:

The C headers are often beyond the direct control of C++ implementors. In some organizations, it's all they can do to get a few #ifdef __cplusplus tests added. Third-party library vendors can perhaps wrap the C headers. But neither of these approaches supports the drastic restructuring required by the C++ Standard. As a result, it is still widespread practice to ignore this conformance requirement, nearly seven years after the committee last debated this topic. Instead, what is often implemented is:

  • Including the header <xxx.h> declares a C name in the global namespace.
  • Including the header <cxxx> declares a C name in the global namespace (effectively by including <xxx.h>), then imports it into namespace std with an individual using declaration.

This was the issue: C++ lib vendors *not having control* over the C headers. The furthest thing from our mind was adding *more* stuff to the C headers!

Martin is 100% correct as far as historical intent goes. The mis-interpretation of these words by Richard and Eric is evidence that the standard remains buggy in this area.

Howard

Thanks, Howard, it’s useful to know how we got here.

For what it’s worth, though, I think the words we have in the standard now do say the best thing for users, striking a balance between C compatibility and bug avoidance (even if they don’t match the historical intent), and would argue against changing them – especially as we do now have an implementation that follows them.

This thread started with evidence from the field to the contrary.

Howard

We have evidence from the field that the lack of overloads of ::fabs causes (usually hard to detect precision loss) bugs. This thread concerns a nonstandard extension that I would imagine is only used by a small minority of libc++ users, and we can handle it by supporting that extension directly in libc++. (Doing so would fix a couple of other issues: wrong return type, no support for __fp16 in std::fabs.)

The discussion on the Standards reflector at the time that this text was ratified was about how the C names were introduced into the global namespace, otherwise all that needed to be said was:

<math.h> is simply equivalent to:

#include

using namespace std;

I understand your perspective on what is now section “D.5 C standard library headers [depr.c.headers]” in the WP, but this also has to be interpreted in the context of ISO’s fundamental charter which is “to standardise existing practice”. I do not think anyone could argue with what is meant by existing practice concerning the inclusion of C headers in the context of a C++ program. It looks to me now like the wording here introduced an unintended ambiguity in the language of the C++ Standard.

I do try to educate people to do C++ the C++ way, and include ‘’ and then ‘using namespace std;’ instead of ‘<XXXX.h>’, and it bugs me when they don’t; but in practice they ignore such advice. In fact, the majority of programs that are ported from C to C++ start out life by simply being renamed from “.c” to “.cpp”. I would say that a good 90% of programmers using C++ for embedded systems are actually C programmers trying to “move up” and to take advantage of the extraordinary compile-time optimisations possible when templates are used (I still think that this is awesome versus C).

‘fabs’ and ‘__fp16’ was simple the observable failure that drew my attention to the issues but another example is the simpler ‘abs’ from ‘<stdlib.h>’. In this case (in C) there is just one function:

int abs(int);

so code such as:

#include <stdint.h>

int a;

float b;

double c;

__fp16 d; // I’ve left FP16 in just to show that it is not really different

abs(a); // implicitly abs(a)

abs(b); // implicitly abs((int)b)

abs(c); // implicitly abs((int)c)

abs(d); // implicitly abs((int)d)

previously chose ONLY ‘int ::abs(int)’, but by introducing overloads to the C functions at global scope, the semantics and behaviour of ‘abs(b)’ and ‘abs(c)’ changes, and because ‘__fp16’ is not a recognised type, it becomes ambiguous. At least I see from the error message that the ‘__fp16’ case is now broken, but the ‘float’ and ‘double’ variants silently do something very different by calling different functions whose semantic implementation of ‘abs’ I can only trust is true. I mentioned ‘__fp16’ earlier because that was the unhandled data type that made an otherwise silent and hidden semantic change visible to me. We also found problems with ‘putc’ by the way, where it silently went from being a MACRO to an externally linked function resulting in unresolved link errors. This was slightly different, but the underlying pattern change was the same.

The same ambiguity will arise whenever a class with a UDC to one of the overloaded types is involved, it is not just the tacitly implemented ‘__fp16’ data type.

This change is not existing practice, and I am surprised it was not vetoed, I would have certainly would have advised vetoing this if I was still active in our national Standardisation panel.

It also breaks previously previously “compliant” C++, for example:

#include <stdlib.h>

struct Foo {

operator double () const;

operator int () const;

};

Foo aFoo;

int x = abs(aFoo);

I’m not advocating such code, but without your interpretation of this clause, the above was valid, compliant and unambiguous. It chose ‘int ::abs(int)’ and used ‘Foo::operator int()const’ to satisfy it. But with your interpretation it is now a compile-time error - ambiguous selection.

Even worse are existing compliant C++ programs such as:

#include <stdlib.h>

struct Bar {

operator double () const;

};

Bar aBar;

int x = abs(aBar);

The effective code subtly changes silently from:

int x = abs((int)aBar.operator double()); // choosing ‘int ::abs(int)’

to:

int x = (int)abs(aBar.operator double()); // choosing ‘double ::abs(double)’

My interpretation of the clause in [depr.c.headers] is not that it is equivalent to:

using std::abs;

which brings in the whole overload set, but rather something like:

using std::abs(int); // sic! - but selects an element of the overload set

these have very different meanings and intent. The inclusion of a Standard header does not mean that textual inclusion or syntactic equivalence is required, it only requires that the Standard names become available to the program with their intended meaning. And I believe that this is why the Standard does not specify “how” the names get into the global namespace - textual inclusion is just a common way of doing implementing.

My original message was not criticise the new implementation, but to “critique” it and highlight that it has changed the expected semantics and hence the “existing practice” tenet of Standard C++. If it wasn’t for the fact that we are looking at v3.8 code freeze very soon, I would not have brought it up as an urgent issue.

MartinO

The discussion on the Standards reflector at the time that this text was ratified was about how the C names were introduced into the global namespace, otherwise all that needed to be said was:

<math.h> is simply equivalent to:

#include

using namespace std;

I understand your perspective on what is now section “D.5 C standard library headers [depr.c.headers]” in the WP, but this also has to be interpreted in the context of ISO’s fundamental charter which is “to standardise existing practice”. I do not think anyone could argue with what is meant by existing practice concerning the inclusion of C headers in the context of a C++ program. It looks to me now like the wording here introduced an unintended ambiguity in the language of the C++ Standard.

I do try to educate people to do C++ the C++ way, and include ‘’ and then ‘using namespace std;’ instead of ‘<XXXX.h>’, and it bugs me when they don’t; but in practice they ignore such advice. In fact, the majority of programs that are ported from C to C++ start out life by simply being renamed from “.c” to “.cpp”. I would say that a good 90% of programmers using C++ for embedded systems are actually C programmers trying to “move up” and to take advantage of the extraordinary compile-time optimisations possible when templates are used (I still think that this is awesome versus C).

‘fabs’ and ‘__fp16’ was simple the observable failure that drew my attention to the issues but another example is the simpler ‘abs’ from ‘<stdlib.h>’. In this case (in C) there is just one function:

int abs(int);

so code such as:

#include <stdint.h>

int a;

float b;

double c;

__fp16 d; // I’ve left FP16 in just to show that it is not really different

abs(a); // implicitly abs(a)

abs(b); // implicitly abs((int)b)

abs(c); // implicitly abs((int)c)

abs(d); // implicitly abs((int)d)

previously chose ONLY ‘int ::abs(int)’, but by introducing overloads to the C functions at global scope, the semantics and behaviour of ‘abs(b)’ and ‘abs(c)’ changes, and because ‘__fp16’ is not a recognised type, it becomes ambiguous. At least I see from the error message that the ‘__fp16’ case is now broken, but the ‘float’ and ‘double’ variants silently do something very different by calling different functions whose semantic implementation of ‘abs’ I can only trust is true. I mentioned ‘__fp16’ earlier because that was the unhandled data type that made an otherwise silent and hidden semantic change visible to me. We also found problems with ‘putc’ by the way, where it silently went from being a MACRO to an externally linked function resulting in unresolved link errors. This was slightly different, but the underlying pattern change was the same.

The same ambiguity will arise whenever a class with a UDC to one of the overloaded types is involved, it is not just the tacitly implemented ‘__fp16’ data type.

This change is not existing practice, and I am surprised it was not vetoed, I would have certainly would have advised vetoing this if I was still active in our national Standardisation panel.

It also breaks previously previously “compliant” C++, for example:

#include <stdlib.h>

struct Foo {

operator double () const;

operator int () const;

};

Foo aFoo;

int x = abs(aFoo);

I’m not advocating such code, but without your interpretation of this clause, the above was valid, compliant and unambiguous. It chose ‘int ::abs(int)’ and used ‘Foo::operator int()const’ to satisfy it. But with your interpretation it is now a compile-time error - ambiguous selection.

Even worse are existing compliant C++ programs such as:

#include <stdlib.h>

struct Bar {

operator double () const;

};

Bar aBar;

int x = abs(aBar);

The effective code subtly changes silently from:

int x = abs((int)aBar.operator double()); // choosing ‘int ::abs(int)’

to:

int x = (int)abs(aBar.operator double()); // choosing ‘double ::abs(double)’

Sorry for being out of synch (its Saturday and after 10pm in Ireland), I was replying (verbosely) to an earlier message in this thread when these replies arrive (USA is awake and alive); but in response to the comment below, I agree 100% with Richard’s observation that this is a problem. But what is really needed is that we need to teach programmers to use ‘’ and not ‘<math.h>’ in their C++ programs, and the ‘std::’ names not the ‘::’ names, not that we make ‘<math.h>’ more cuddly - it’s the programmer’s problem if they are not writing their code properly, not the compiler or its libraries.

If anything, this is a programmer pattern aberration that the static analysers should look for and diagnose (perhaps they do already?). And with this moving closer to becoming a standard part of the LLVM/CLang offering, I think that it would be a good place to put such precision and performances concerns.

From a performance perspective (which the Standard does not speak of), overloading means that the implementation can choose the most optimal algorithm suited to the data type - this is WHY I love C++ - and it also why the committee chose to overload the C Standard names, though within its own namespace so as not to interfere with the understood and expected semantics of C programmers. Every time I have won-over a C programmer to doing things the C++ way, it is because I have been able to demonstrate a performance advantage to doing so. Using ‘std::fabs’ etc. after including ‘’ versus including ‘<math.h>’ proves this to them; but from the perspective of semantics, I do not think that it was ever the intent of the C++ Standards committee to alter the semantic meaning of naively using C headers in a C++ program, even if that meant they suffered silent problems such as precision. It was intended that such programs would do exactly what they would have done in C. If they adapt their program to use C++ properly, these things don’t’ happen and they can reap the advantages of adapting their code to being C++ and not C. Bjarne Stroustrup famously stated a very long time ago “C++, as close to C as possible, but no closer”.

The headers ‘’, ‘’ and ‘<stdio.h>’ really show how the committee tried very hard to maintain semantic compatibility between C and C++, right down to details such as “tied streams” and automatic flushing of ‘stdout’ (implied, not stated). This was hard to word, but for the most part the committee got it right.

Another example is ‘isnan’. It is not even semantically valid to attempt to convert a ‘float’ NaN to a ‘double’ NaN in order to call ‘bool isnan(double)’, hence the use of implementation defined MACROS (ugh) in C and overloaded functions in C++.

And Richard, you are absolutely right - that is “what the words” in the Standard say, but I genuinely believe that this is a bug in the Standard and not its intent. We need to temper the implementations with the intent, and get the Standard (C++’17) to fix the words.

Please ignore ‘__fp16’, it is not really relevant since it is not a Standard data type, though I would love to see ‘short float’ someday, and have it incorporated as a first class citizen into the rules for “Usual Arithmetic Conversions”, “Standard Conversions” and “Overloading”. It’s not there yet, but it’s importance is growing. I would have liked to have used ‘int’ in my example, but the bug in the new headers did not make this ambiguous, it just quietly chose a different function.

MartinO

The effective code subtly changes silently from:

int x = abs((int)aBar.operator double()); // choosing ‘int ::abs(int)’

to:

int x = (int)abs(aBar.operator double()); // choosing ‘double ::abs(double)’

Of all of your examples, this is the most important. The other changes result in a compilation failure, and are easy to fix. However, it is certainly my experience that, in almost all cases, the semantic change you highlight is actually a silent bug fix, as the author almost always intended the behavior we now have, not the behavior we provided previously. Obviously this will not be 100% true, but on balance, the change has seems positive. Either way, good compiler warnings are essential. As I recall, Clang does have a good warning for this.

-Hal

Hmm, yes and no. If they wanted different behaviour, they should have included ‘’ and not ‘<math.h>’. It is not our place to assume to fix their possible intent. An expert C++ programmer might expect exactly the C behaviour. The very fact that they chose the C header and not the C++ prescribed equivalent makes it more evident to me that this is the realm of the static analyser’s diagnostics, and not the realm of the Standard library implementation to assume it knows what they might have meant.

MartinO

One thing seems crystal clear to me: Different people have interpreted these words in different ways. Different enough to have testable semantic differences. And such differences are not good for providing portability to the C++ programmer. This looks like an issue that should be decided by the C++ committee in an official vote.

Someone should open a LWG issue. It isn’t going to be me. Myself nor my employer use the <name.h> headers, so I am not sufficiently motivated. But if anyone is motivated, and not sure how to open an issue, I’m happy to help with those logistics.

Howard

From: "Martin J. O'Riordan" <martin.oriordan@movidius.com>
To: "Hal Finkel" <hfinkel@anl.gov>
Cc: "Marshall Clow" <mclow.lists@gmail.com>, "Richard Smith"
<richard@metafoo.co.uk>, "Clang Dev" <cfe-dev@lists.llvm.org>, "Eric
Fiselier" <eric@efcs.ca>
Sent: Saturday, January 23, 2016 4:23:21 PM
Subject: RE: [cfe-dev] LibC++ v3.8 - Problems with ISO C wrapper
headers

From: Hal Finkel [mailto:hfinkel@anl.gov]
Sent: 23 January 2016 21:57
To: Martin ORiordan <Martin.ORiordan@Movidius.com>
Cc: Marshall Clow <mclow.lists@gmail.com>; Richard Smith
<richard@metafoo.co.uk>; Clang Dev <cfe-dev@lists.llvm.org>; Eric
Fiselier <eric@efcs.ca>
Subject: Re: [cfe-dev] LibC++ v3.8 - Problems with ISO C wrapper
headers

> The effective code subtly changes silently from:

> int x = abs((int)aBar.operator double()); // choosing ‘int
> ::abs(int)’

> to:

> int x = (int)abs(aBar.operator double()); // choosing ‘double
> ::abs(double)’

Of all of your examples, this is the most important. The other
changes result in a compilation failure, and are easy to fix.
However, it is certainly my experience that, in almost all cases,
the semantic change you highlight is actually a silent bug *fix*, as
the author almost always intended the behavior we now have, not the
behavior we provided previously. Obviously this will not be 100%
true, but on balance, the change has seems positive. Either way,
good compiler warnings are essential. As I recall, Clang does have a
good warning for this.

-Hal

Hmm, yes and no. If they wanted different behaviour, they should have
included ‘ <cmath> ’ and not ‘ <math.h> ’. It is not our place to
assume to fix their possible intent. An expert C++ programmer might
expect exactly the C behaviour.

I'd have a difficult time calling such an author an expert C++ programmer, as such a person would clearly understand how easy it would be for some future maintainer to break their code by a seemingly-innocuous improvement in which headers are included, or by placing the code in a different environment (e.g. by someone copying-and-pasting the code into some other file).

The very fact that they chose the C header

But it is often not the choice of the original author, at least not intentionally.

and not the C++ prescribed equivalent makes it more evident to me
that this is the realm of the static analyser’s diagnostics, and not
the realm of the Standard library implementation to assume it knows
what they might have meant.

I think your position is completely reasonable, but there happen to be strong practical advantages to the wording as-stated that are definitely worth considering (regardless of how we got here).

-Hal

It is fascinating that these words have been there since the original C++98 Standard, and we never noticed it before - a fresh pair of eyes are always good to show up unintended ambiguities.

From the very formation of the Standards Committee in 1989, it was always our intention to preserve as much compatibility with C as possible so as to allow existing code written in C to be migrated to C++ with minimum hassle. There are unavoidable things like new keywords, and the whole notion of scope changed, but on the whole I think that the committee did a good job at achieving this goal. New keywords were always discussed at great depth, and in many cases obscure syntax that could not exist in C was used in preference to adding a new keyword.

My suspicion is that the wording in “D.5 C standard library headers [depr.c.headers]” was written without quite thinking about the consequences that some of the names in C are now overloaded in C++. Since C++ does not provide a form of using-declaration that can select discrete members from an overloaded name, it is not actually possible to achieve what was the intent of this section in the language, and why it used the “behaves as if” fuzzy wording rather than being explicit. Section “C.5 C standard library [diff.library]” (non-normative) does try to list the set of difference, but curiously does not mention that many of the functions in ‘<math.h>’ are overloaded, nor that ‘abs’ from ‘<stdlib.h>’ is overloaded in both ‘’ and ‘’.

I completed a set of changes to these headers to restore the behaviour to that which people normally expect - I don’t know of another implementation which interprets these words as literally as has been exposed here - and that interpretation in English is perfectly valid. When I ran my tests again with the modified versions, it did successfully correct the failing tests. As I indicated in the first place, these are real world programs that will build successfully and as expected on all other platforms that we have used, some are existing programs ported from other targets, so it is not a target specific issue (leaving ‘__fp16’ aside; it was just ‘__fp16’ that drew my attention to it).

I left the changes for names like ‘fpclassify’ as they are, overloaded in the global namespace because this looks like a perfectly acceptable way of manifesting a C macro implanted semantic in C++ using overloading, and I can’t think of an observable differences that a C program is likely to depend on.

At first I thought that this was just an accident due to the refactoring of ‘’ into ‘’ and ‘<c++/math.h>’; but it is clear from this thread that it was an intentional act based on valid reading of this statement in the Standard; though in my opinion this is a bug in the Standard and never the intent.

It has often been observed that the C++ Standard would have benefitted from the provision of a Rationale document as was provided with the C Standard, but C++ is a much larger and more complex language, and such a rationale would have taken considerable time and resources to write.

Tomorrow I will synch up to the current head revision of the v3.8 branch and compute a patch file for these changes relative to that revision. I can submit it to this list if a decision is made to accept this this change; I will have to make this change in my version either-way, as I have too many existing programs dependent on the commonly understood meaning. Also, while I am no longer an active participant on the Standard, I will draw attention of this issue to some of my colleagues who are, so that they can discuss it in the formal context of C++17.

Thanks,

MartinO

A little while ago, on FreeBSD we rewrote some of the [tg]math.h macros. The old versions used sizeof(arg) == sizeof({float,double,long double}) to select the versions. The new versions to use _Generic (with fallback __builtin_type_select and fallback to sizeof). Unlike C++ overloading, _Generic does not permit implicit conversion. We found (and reported / fixed) several issues that were real bugs in people’s code that had been silently returning the wrong value on other platforms (and older versions of FreeBSD) for years.

Implicit casts are probably the most dangerous misfeature to ever end up in mainstream programming languages, or at least in the top three with arbitrary pointer arithmetic and null-terminated strings.

David

Couldn't agree more, and when 'explicit' was been added for constructors with a single argument, I argued at the time that it should also apply to overloaded conversion operators for the same reason that 'explicit' was added to constructors. It was not approved in C++98, but thankfully it was added later. I personally detest implicit conversions because of the number of subtle bugs they introduce.

  MartinO

https://github.com/freebsd/freebsd/blob/master/lib/msun/src/math.h

line 80+

While I bow to Howard's experience and to his recollection, I have to
disagree with his characterization of Richard and Eric's posts as
"mis-interpretation".

I believe that they have accurately reflected what is written in the
standard.

But that's just a nit.
The real questions are - where do we want to be, and how can we get there?

(and let's not forget http://cplusplus.github.io/LWG/lwg-defects.html#2086)
as well, which implies that there is no support for user-defined types in
<cmath>.

-- Marhsall

With regards to “where do we want to be”, we shouldn’t take the complete opposite approach and have the libcxx provide none of the C headers. At a minimum, we need a libcxx version of tgmath.h that hides the C version of tgmath.h. The generic macros from tgmath.h would play havoc with the overloaded functions in cmath.

Maybe all the other libcxx C headers could forward to the underlying C headers (or maybe not), but tgmath.h (and maybe others I’m forgetting) are special, and should not forward to the underlying C header.