PRIuPTR preprocessor bug in GCC? - c++

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;

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.

ISO C++ does not include variadic templates

Good day, guys. I can't really get what's going on with this and where is my mistake? It looks like I am missing small detail, but the code looks okay.
#include <pspkernel.h>
#include <pspdebug.h>
#include <stdlib.h>
#include <string.h>
PSP_MODULE_INFO("CONTROLTEST", 0, 1, 1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
int done = 0;
template<typename... Args>
void Printf(Args... params)
{
pspDebugScreenPrintf("Test %d\n", params...);
}
int main(void)
{
pspDebugScreenInit();
Printf(1);
while(!done){
}
sceKernelExitGame();
return 0;
}
Error I'm getting looks like this:
main.cpp:12: error: ISO C++ does not include variadic templates
main.cpp:13: error: ISO C++ does not include variadic templates
I'm using gcc 4.3.5
According to the GCC docs, GCC 4.3 added support for variadic templates, but C++0x (which became C++11) was still experimental at that point.
If you really want to use C++11 or newer standards, update to a recent compiler. Specifically, GCC 4.8.1 was the first feature-complete implementation of the 2011 C++ standard, so you should aim at least for that version.

Why does VC++ compile the code while clang doesn't?

I use VS 2015 (Update 3) to compile the following code:
#include <codecvt>
#include <cctype>
#include <functional>
int main()
{
std::function<int(int)> fn = std::isspace;
}
If I use VC++ to compile it, it's ok. However, if I change the compiler to Visual Studio 2015 - Clang with Microsoft CodeGen (v140_clang_c2) in Visual Studio, clang reports an error:
main.cpp(7,26): error : no viable conversion from '' to 'std::function'
std::function fn = std::isspace;
More surprising, if I comments the first line as follows, clang will also be ok.
//#include <codecvt> // now clang feels happy
#include <cctype>
#include <functional>
int main()
{
std::function<int(int)> fn = std::isspace;
}
What's the root cause?
std::isspace is overloaded in the standard library.
Due to the structure of their standard library headers, one compiler sees two different declarations of the name.
Then its use without arguments or casting is ambiguous.
std::isspace is ambiguous, it can either refer to the function found in <cctype> which is for compatibility with C, or the function template found in <locale>.
You can resolve the ambiguity with
std::function<int(int)> fn = static_cast<int(*)(int)>(std::isspace);
Or by omitting the std:: namespace, although technically there's no requirement for implementations to import the C functions into the global namespace.
The Clang and GCC implementations of <codecvt> both seem to include declarations of the template from <locale>, hence the error; presumably VS doesn't.

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

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.

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.