Why does <cmath> expose entities outside the std namespace?

Viewed 118

As far as I understand it C-entities used by C++ like those in <math.h> can be included in a safe way inside an std namespace by including their corresponding <c...> variant (except macros, obviously). cppreference seems to confirm this.

However, including <cmath> appears to pull in the log function outside the std namespace:

#include <cmath>

namespace log {}

int main() {}

Compiled with g++ -Wall -Wextra -pedantic -std=c++17 a.cpp yields:

a.cpp:3:11: error: ‘namespace log { }’ redeclared as different kind of entity
    3 | namespace log {}
      |           ^~~
In file included from /usr/include/features.h:446,
                 from /usr/include/x86_64-linux-gnu/c++/9/bits/os_defines.h:39,
                 from /usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h:524,
                 from /usr/include/c++/9/cmath:41,
                 from a.cpp:1:
/usr/include/x86_64-linux-gnu/bits/mathcalls.h:104:1: note: previous declaration ‘double log(double)’
  104 | __MATHCALL_VEC (log,, (_Mdouble_ __x));
      | ^~~~~~~~~~~~~~

Is my standard library broken? Can I do something to avoid this?


I originally stumbled over this using <random>, which means that more headers might be affected by apparently random C-entities being spewed all across the top level namespace.

2 Answers

Why does <cmath> expose entities outside the std namespace?

Because of history.

<cmath> header is a header inherited from the C standard library (where it is named <math.h>). In the C language, there is only the global namespace1 where all names are declared.

Since many C++ implementations are also C implementations, they often implement the inherited C standard header by including it as-is2 which means that it declares global names.

While there may be techniques to avoid (except in case of standard macros) declaring global names, it is unlikely that implementations that have been doing it since before standardisation would change the behaviour because that would break backward compatibility.

Is my standard library broken?

No. C++ standard allows this; All C standard library names are reserved for this (or any) use by the language implementation. You may not define them yourself.

Can I do something to avoid this?

You generally cannot prevent a standard library doing this.

You can technically avoid most of it by choosing to use a freestanding language implementation. But you'll lose nearly the entire standard library if you choose that.

You can minimise chances of name clashes by avoiding declaration of any global names yourself, except for a single namespace with sufficiently unique name. Something like:

namespace usr_bitmask::log {

}

1 Note that the concept of "name space" in the C language is something else.

2 And additionally, re-declares the names in the std namespace when using the <c... named header, as well as adds C++ specific overloads in some cases.

Maybe someone with more C/C++ Standard knowledge will prove me wrong, but for a C++ compiler to also being able to process C-Code, functions from the C-Library need to be outside of namespaces, in fact you should just find the C-Library definitions enclosed in a extern "C", so you can use the old C functions inside you C++ code transparently

Related