<cmath> hides isnan in <math.h> in C++14 / C++11? - c++

I have here a small test app which uses isnan from <math.h>:
#include <iostream>
#include <math.h>
int main()
{
double d = NAN;
std::cout << isnan(d) << '\n';
return 0;
}
Build and run under 3 different standards:
$ g++ -std=c++98 main.cpp; ./a.out
1
$ g++ -std=c++11 main.cpp; ./a.out
1
$ g++ -std=c++14 main.cpp; ./a.out
1
Now we also include <cmath>, and test with both isnan and std::isnan:
#include <iostream>
#include <cmath>
#include <math.h>
int main()
{
double d = NAN;
std::cout << std::isnan(d) << '\n';
std::cout << isnan(d) << '\n';
return 0;
}
Build and run:
C++98 works
$ g++ -std=c++98 main.cpp; ./a.out
1
1
C++11 and C++14 don't, isnan is not found.
$ g++ -std=c++11 main.cpp
main.cpp: In function ‘int main()’:
main.cpp:10:25: error: ‘isnan’ was not declared in this scope
std::cout << isnan(d) << '\n';
^
main.cpp:10:25: note: suggested alternative:
In file included from main.cpp:3:0:
/usr/include/c++/5/cmath:641:5: note: ‘std::isnan’
isnan(_Tp __x)
^
$ g++ -std=c++14 main.cpp
main.cpp: In function ‘int main()’:
main.cpp:10:25: error: ‘isnan’ was not declared in this scope
std::cout << isnan(d) << '\n';
^
main.cpp:10:25: note: suggested alternative:
In file included from main.cpp:3:0:
/usr/include/c++/5/cmath:641:5: note: ‘std::isnan’
isnan(_Tp __x)
^
Note the order of inclusion is not important. If I include <cmath> before <math.h> or after, the result is the same.
Questions
Why is isnan gone?
Without having to go back and change old code to compile under the new standard, is there any way to fix this?

Briefly summarizing the pertinent points, mostly from Jonathan Wakely's excellent blog post:
glibc <2.23's math.h declares the obsolete X/Open int isnan(double); that is incompatible with the C99/C++11 version (bool isnan(double);).
glibc 2.23's math.h fixes this by not declaring the isnan function in C++11 or later.
All of them still define an isnan macro. #include <cmath> nukes that macro as required by the C++ standard.
GCC 6's libstdc++ provides its own special math.h header that declares a bool isnan(double); in the global namespace (unless the libc math.h declares the obsolete signature) and also nukes the macros as required by the standard.
Before GCC 6, #include <math.h> simply included the header from your libc, so the macro isn't nuked.
#include <cmath> always nukes the macros.
Net result, in C++11 mode:
glibc < 2.23, GCC < 6: <math.h> uses the macro; <cmath> uses obsolete signature
glibc >= 2.23, GCC < 6: <math.h> uses the macro; <cmath> results in error
glibc < 2.23, GCC >= 6: <math.h> and <cmath> use obsolete signature
glibc >= 2.23, GCC >= 6: <math.h> and <cmath> use standard signature

If you look inside <cmath> from GCC, it has this:
. . .
#include <math.h>
. . .
#undef isnan
That's why the order doesn't matter - whenever you #include <cmath>, <math.h> is auto-included and its contents is (partially) nuked.
Attempting to include it again will have no effect because of #ifndef _MATH_H.
Now, what does the standard have to say about this behavior?
[depr.c.headers]:
... 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
([basic.scope.namespace]) of the namespace std and are then injected
into the global namespace scope by explicit using-declarations
([namespace.udecl]).
[ Example: The header <cstdlib> assuredly provides its declarations
and definitions within the namespace std. It may also provide these
names within the global namespace. The header <stdlib.h> assuredly
provides the same declarations and definitions within the global
namespace, much as in the C Standard. It may also provide these names
within the namespace std. — end example ]
So it's OK that <cmath> does not provide isnan in the global namespace.
But it's a gray area what should happen when both are included in one compilation unit, although one could argue that the statement above implies that both versions must interoperate, in which case it would be a bug in GCC/libstdc++ (some versions).

A lot of the functions inside math.h are actually macros. Since this is not idiomatic c++ the header cmath contains following code:
...
#undef isinf
#undef isnan
#undef isnormal
...
And implements then all those undefined macros as function in the namespace std. This is at least true for gcc 6.1.1. That's why your compiler can't find isnan.

Related

Compiler warning on mix of "stdio.h" and <cstdio>

The following program results in compiler warnings if the include statement in line 4 is added (uncommented).
Compiler: gcc version 8.1.0 (i686-win32-dwarf-rev0, Built by MinGW-W64 project)
#define __STDC_FORMAT_MACROS
// Adding this and -Wall results in compiler warning
//#include "stdio.h"
#include <cstdint>
#include <cinttypes>
#include <cstdio>
int main()
{
int64_t i = 0;
printf("%" PRId64, i);
}
The warnings are:
testlld.cpp:11:14: warning: unknown conversion type character 'l' in format [-Wformat=]
std::printf("%" PRId64, i);
^
testlld.cpp:11:14: warning: too many arguments for format [-Wformat-extra-args]
testlld.cpp:11:14: warning: unknown conversion type character 'l' in format [-Wformat=]
testlld.cpp:11:14: warning: too many arguments for format [-Wformat-extra-args]
Can someone explain what happens?
I can of course fix this by using only <cstdio>, which would be the correct thing in this case.
But it leads to another question...
Say I have a header file that is included by two implementation files - one compiled with a C compiler and one compiled with a C++ compiler. The header file would need to use "stdio.h" since it needs to compile with a C compiler. Does this mean that all code including that header file must also use "stdio.h" and NOT <cstdio>, even if it is C++ code?
This seems to be a known bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60434
This makes sense since - despite the warning - the 64 bit integer is printed correctly.
By the way, when compiling as C (changing the included headers to .h), the format string defined by PRId64 is I64d. Using this format string gives no warning neither when compiling as C nor as C++. Hence a possible workaround is to use
printf("%I64d", i);
Another option is to build with -D__USE_MINGW_ANSI_STDIO.
Following the suggestions above, I did the following, which compiles without warnings.
// Needed to define the macros for data types in inttypes.h
#define __STDC_FORMAT_MACROS
// This part in header file (.h) re-used by this file as well as .c files compiled with C compiler
#ifdef __cplusplus
#include <cstdio>
#else
#include "stdio.h"
#endif
#include <cstdint>
#include <cinttypes>
#include <cstdio>
int main()
{
int64_t i = 0;
printf("%" PRId64, i);
}
This looks like a good (the right?) solution for me.

math.h macro collisions

Macro DOMAIN in math.h collides with enums and possibly other types. I don't know what to make of it.
#include <algorithm>
enum Type { DOMAIN };
int main(){
Type t = Type::DOMAIN;
return 0;
}
Compile with flag -std=c++11. The C99 version of this code compiles perfectly fine though:
#include <algorithm>
enum Type { DOMAIN };
int main(){
Type t = DOMAIN;
return 0;
}
I checked the source code and the library is to blame. algorithm includes stl_algo.h, in which there is ifdef:
#if __cplusplus >= 201103L
#include <random> // for std::uniform_int_distribution
#include <functional> // for std::bind
#endif
The following code compiles fine on c++11 compiler:
#include <random>
#include <iostream>
int main(){
std::cout << DOMAIN << std::endl;
return 0;
}
Is it a feature or a bug?
EDIT* dirty fix:
#ifdef DOMAIN
#undef DOMAIN
#endif
It's a bug (or a "wart" if you want to be generous).
All the rest of this answer refers only to GCC and the Gnu standard C library headers. The man page references are to a linux system (but I've added links to man7.org).
The DOMAIN macro comes from math.h's System V support. (See man matherr.) System V support is normally enabled by defining the _SVID_SOURCE feature-test macro (see man feature_test_macros), but it is enabled along with a raft of other extensions if _GNU_SOURCE is defined, or by default if no feature test macros are defined.
gcc predefines _GNU_SOURCE for C programs if the --std option is omitted or set to gnu##. The various --std=c## options cause __STRICT_ANSI__ to be defined. Consequently, compiling C code with some explicit C standard will suppress the System V extensions. That needs to be done because the System V extensions are not standards-compatible, not even with Posix, because they pollute the global namespace. (DOMAIN is just one example of this pollution.)
However, g++ defines _GNU_SOURCE even if --std=c++## is specified, and consequently the System V extensions will sneak in. (Thanks to #dyp for the link to this libstdc++ FAQ entry. and this long and inconclusive discussion from 2001 on the GCC mailing list)
An ugly workaround is to set up the features yourself, and then undefine __USE_SVID:
#include <features.h>
#undef __USE_SVID
#include <random>
#include <iostream>
int main(){
std::cout << DOMAIN << std::endl;
return 0;
}
(Live on coliru)
IMHO, this should not be necessary. But there it is.
§ 17.6.5.2 [res.on.headers] / 1 of N4140 says:
A C++ header may include other C++ headers. A C++ header shall provide the declarations and definitions that appear in its synopsis. A C++ header shown in its synopsis as including other C++ headers shall provide
the declarations and definitions that appear in the synopses of those other headers.
Therefore, it is valid for <algorithm> to #include <cmath> which injects the offending macro constant into your namespace.
Note, however, that your “quick and dirty fix” is disallowed by the standard (§ 17.6.4.3.1 [macro.names] / 1):
A translation unit that includes a standard library header shall not #define or #undef names declared in any standard library header.
You'll have to choose a name different from DOMAIN for your enum constant.

Mingw doesn't compile due to "'isblank' was not declared in this scope" error

I try to compile & link the following program with mingw. When I use the default version it works well. But when I use c++11 version it doesn't compile and gives me the following error newfile.cpp:22:18: error: 'isblank' was not declared in this scope.
for testing the following program it's enough to call the _isblank function in main.
For compiling the Netbeans 8.0.2 uses g++ -c -g -Wall -std=c++11 -MMD -MP -MF "build/Debug/MinGW_1-Windows/newfile.o.d" -o build/Debug/MinGW_1-Windows/newfile.o newfile.cpp.
The mingw version is 4.8.1 and everything is configured well(Default).
I tried by adding/removing namespace std. The problem seems to be in cctype header! But I wonder how to solve it. The project will have to be compiled with g++ on linux! Will these problems remain?
#include <cstdlib>
#include <cctype>
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <map>
#include <functional>
#include "newfile.h"
using namespace std;
conf_info_t CONF_INFO;
#define CONF_FILE_ADDRESS "confs.txt"
//
//typedef std::map<std::string, std::function<void (const std::string&)>> confMap_t;
//confMap_t confMap;
int _isblank(int c){
return isblank(c);
//return c == ' ' || c == '\t';
}
In my version of GNU library the declaration of isblank in <ctype.h> header is protected by some conditional compilation directives, which nevertheless should make this function available in C++.
However, in <cctype> header this function is is separated from all other declarations and given a special treatment for some reason. It is only made available in namespace std if macro _GLIBCXX_USE_C99_CTYPE_TR1 is defined. This is what it looks like inside <cctype>
#if __cplusplus >= 201103L
#ifdef _GLIBCXX_USE_C99_CTYPE_TR1
#undef isblank
namespace std
{
using ::isblank;
} // namespace std
#endif // _GLIBCXX_USE_C99_CTYPE_TR1
#endif // C++11
I don't know what the purpose that _GLIBCXX_USE_C99_CTYPE_TR1 macro is supposed to serve. In my GCC installation this macro is defined, which makes isblank available in my case.
You might want to check what your <cctype> looks like and see if something like that is happening on your side as well.
It worked when I undefined STRICT_ANSI in the translation unit adding -U__STRICT_ANSI__ to the compiler options. But I wonder which part of my program violates C++ standards.
It should have been compiled in this way:
g++ -U__STRICT_ANSI__ -c -g -Wall -std=c++11 -MMD -MP -MF "build/Debug/MinGW_1-Windows/main.o.d" -o build/Debug/MinGW_1-Windows/main.o main.cpp

PRIuPTR preprocessor bug in GCC?

The following program, when compiled as C++ with GCC 4.8.1
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
int main() {
uintptr_t i = 0;
i--;
printf("%" PRIuPTR "\n", i);
return 0;
}
gives the following error message
a.cc: In function 'int main()':
a.cc:8:13: error: expected ')' before 'PRIuPTR'
printf("%" PRIuPTR "\n", i);
^
It compiles and runs correctly with the Microsoft compiler, and even with GCC when compiled as C.
Am I missing something, or is this a bug in GCC? If the latter, is there a workaround?
The C standard says this about including the C header from C++:
C++ implementations should define these macros only when __STDC_FORMAT_MACROS is defined
before <inttypes.h> is included.
and it seems that GCC follows this recommendation, while Microsoft doesn't.
Rather than defining this macro and including the deprecated C header, a better solution is to use the C++ header <cinttypes>, which defines these macros unconditionally. (As noted in the comments, the C++ standard specifically says that the macro has no effect on the C++ header.)
Alternatively, stop using the C library when there's a more convenient (and typesafe) C++ alternative, std::cout << i;

C++ programing error

I am new to C++ programming.
So I was trying my luck executing some small programs.
I am working on HP-UX which has a compiler whose
executable is named aCC.
I am trying to execute a small program
#include <iostream.h>
using namespace std;
class myclass {
public:
int i, j, k;
};
int main()
{
myclass a, b;
a.i = 100;
a.j = 4;
a.k = a.i * a.j;
b.k = 12;
cout << a.k << " " << b.k;
return 0;
}
When I compile this it gives me an error:
> aCC temp.cpp
Error 697: "temp.cpp", line 2 # Only namespace names are valid here.
using namespace std;
^^^
What exactly is the problem?
Is std not considered as a namespace in the aCC compiler or is there some serious drawback with aCC?
If I change the <iostream.h> to <iostream>, I get some more errors added as below.
>aCC temp.cpp
Error 112: "temp.cpp", line 1 # Include file <iostream> not found.
#include <iostream>
^^^^^^^^^^
Error 697: "temp.cpp", line 2 # Only namespace names are valid here.
using namespace std;
^^^
Error 172: "temp.cpp", line 14 # Undeclared variable 'cout'.
cout << a.k << " " << b.k;
Which version of aCC are you using? Older versions used a pre-standard STL implemenntation that put everything in the global namespace (i.e. didn't use the std namespace)
You might also need to use the -AA option when compiling. This tells the compiler to use the newer 2.x version of HP's STL library.
>aCC -AA temp.cpp
And it should always be
<iostream>
<iostream.h>
is from a pre-standard implementation of the language, though it is usually shipped so as to maintain backwards compatibility with older code.
Try with:
#include <iostream>
Instead of:
#include <iostream.h>
iostream.h is an old style header in which all functions are exposed in global namespace. naturally in such a case, using namespace std may not work since std namespace is probably not exposed by iostream.h header (in this compiler). As explained above, try with # include which is a new style C++ standard library header. (thanks Shailesh Kumar for the comment! included it in the answer).