math.h macro collisions - c++

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.

Related

Why does map not include out_of_range?

Consider the following code that doesn't compile:
#include <map>
//#include <stdexcept> // uncommenting this works
int main() {
std::map<int, int> test;
try {
test.at(10);
} catch(std::out_of_range& e) {
}
return 0;
}
Why does std::map not include std::out_of_range, even if it uses it? (The .at() can throw a std::out_of_range).
When I also include <stdexcept> it compiles, and works fine.
Whether a standard header does include another header is an implementation detail unless explicitly specified.
With templates its a little more involved, but just to point you in some direction, consider this toy example:
// header: my_map.h
struct my_map {
int at(int);
};
Only in the source file the header for the exception has to be included:
// source: my_map.cpp
#include <stdexcept>
int my_map::at(int) {
throw std::out_of_range("bla");
}
std::map surely looks different but it may hide the exception from the header as well.
Is it also ok for a header to not include a decl for std::pair
The headers that are specified to be included via <map> are <compare> (since C++20) and <initializer_list> (since C++11). Nothing more.
<map> may include other headers and thats one of the "implementation details" mentioned in comments. The part of the standard that explicitly allows standard headers to include others is [todo.put reference here].
The simple rule of thumb that avoids such headace is: Include what you use.
but throwing std::out_of_range is not an "implementation detail" - it is part of the specification of std::map!
Consider that this code compiles with gcc 10.2:
#include <map>
int main() {
std::map<int, int> test;
try {
test.at(10);
} catch(...) {
return 1;
}
return 0;
}
The out_of_range exception is thrown and catched, and 1 is returned. We know what at may throw and catch(...) will catch it, yet no include is needed for the exception.
On the other hand, the same compiler rejects your example, but compiles it when we add a seemingly unrelated header:
#include <map>
#include <sstream> // why this?
int main() {
std::map<int, int> test;
try {
test.at(10);
} catch(std::out_of_range& e) {
}
return 0;
}
However, this only works by coincidence. Apparently <sstream> does include <stdexcept> somewhere along the line. But this may change with compiler version or between differernt compilers.
The implementation of std::map doesn't have to be all in the header file. Yes this is a class template, but it is free to use any internal non-template components.
Consequently, the header file doesn't have to mention std::out_of_range (it may well be hidden inside one of those non-template components) and so doesn't have to have its definition visible anywhere, or behave as if it includes <stdexcept>. It is explicitly allowed to, but there is no implicit or explicit obligation in the standard for it to do so. So it might, or might not.
In fact g++-9 behaves as if <map> includes <stdexcept>, and g++-10 does not, and in both cases they are correct.
Why does std::map not include std::out_of_range
Assuming you are using GCC 10, GCC developers decided to optimize header dependencies in C++ Standard Library code. From Porting to GCC 10:
Header dependency changes
Some C++ Standard Library headers have been changed to no longer
include the <stdexcept> header. As such, C++ programs that used
components defined in <stdexcept> or <string> without explicitly
including the right headers will no longer compile.
Previously components such as std::runtime_error, std::string and
std::allocator were implicitly defined after including unrelated
headers such as <array> and <optional>. Correct code should include
the appropriate headers for the classes being used.
Also from GCC 10 Release Notes:
Reduced header dependencies, leading to faster compilation for some
code.

Why can't I include the standard algorithm library after defining 'epsilon' in C++?

When I include the algorithm library before defining epsilon, the following code compiles:
#include <iostream>
#include <algorithm>
#define epsilon 0.00001
int main() {
std::cout << epsilon;
return 0;
}
When I switch them around, it doesn't:
#include <iostream>
#define epsilon 0.00001
#include <algorithm>
int main() {
std::cout << epsilon;
return 0;
}
It gives the following error 19 times:
epsilon_algorithm.cpp:3:17: error: expected unqualified-id before numeric constant
3 | #define epsilon 0.00001
|
On http://www.cplusplus.com/reference/algorithm/ and https://en.cppreference.com/w/cpp/algorithm there is no mention of anything named 'epsilon'. I know I can avoid the issue by simply always including <algorithm> before I define epsilon, I want to know what causes this error to broaden my understanding of C++ and prevent these types of errors in the future.
I compile with MinGW (32 bit, installed a few weeks ago) in an updated Windows 10 (64 bit) environment.
Standard library headers are allowed to include any other standard library header.
It's possible that <algorithm> includes <limits> and there exists std::numeric_limits::epsilon() there. And of course macros ignore namespaces and classes, so it would try to declare a function called 0.00001.
Don't use macros. Use C++ constants:
constexpr double epsilon = 0.00001;
And if you absolutely need macro, always define them after all includes. Defining them before makes your code very brittle - any change in those headers in the future might blow up your code with cryptic compiler errors.
Don't define macros in header files, for the same reason.
Prefer very localized macros when possible - define them where needed and #undef after you are done. This way they won't leak to the outside (although you can still inadvertently override an existing macro).

<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.

C++ --- Shouldn't these mathematical functions/constants be undefined?

I would not expect the following code that prints the value of sin(pi/2) to work, without the inclusion of an additional header:
#include <iostream>
int main()
{
std::cout << sin(0.5*M_PI) << std::endl;
return 0;
}
and, as expected, upon compilation I get an error reading ‘sin’ was not declared in this scope and a similar error for the use of M_PI.
However, I am confused by the fact that if I include seemingly any boost library header, take just for example the lexical_cast.hpp, and instead run
#include <iostream>
#include <boost/lexical_cast.hpp>
int main()
{
std::cout << sin(0.5*M_PI) << std::endl;
return 0;
}
then the code works and it prints 1.
Why should including this boost header, which contains no definition of M_PI or sin(), allow this constant and function to be defined? Shouldn't I need to include a header, like math.h that includes these things for this to work?
Yes, it should work that way.
The boost headers you tried all have implicit dependencies (pow(), modf(), fmod(), log() etc).
This is a common thing in the C++ compilation model. Nothing to be alarmed by.
Guideline: always explicitly include the headers you directly depend on. And only those.
This prevents portability issues on platforms where the library header dependency tree differs e.g. <algorithms> and <numeric> aren't implicitly included with some other standard library headers (e.g. MSVC)

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;