33

This is a follow-up to a different question.

The original question had other problems but I had to realize that the main one (according to CLang) was a redefinition of time as a different symbol while only nice C++ includes were used.

So here is a stripped-down version:

#include<iostream>

using std::cout;
using std::endl;

class time
{
public:
    int h, min, sec;
};

const int full = 60;

void canonify(time& pre)     // Error here (line 14)
{
    pre.min += pre.sec / full;
    pre.h += pre.min / full;
    pre.sec %= full;
    pre.min %= full;
}
int main()
{
    time a;                  // and here (line 23)
    a.h = 3;
    a.min = 128;
    a.sec = 70;
    canonify(a);
    cout << a.h << ":" << a.min << ":" << a.sec << endl;
}

Of course, replacing time with a different symbol or using struct time is enough to get rid of the problem. Said differently my question is not how to make the code run, but only whether we have to see symbols from the C library as reserved tokens in C++. Clang 11 (on MSVC19) chokes with:

1>ess.cpp(14,15): error : must use 'class' tag to refer to type 'time' in this scope
1>...\ucrt\time.h(518,42): message : class 'time' is hidden by a non-type declaration of 'time' here
1>ess.cpp(23,5): error : must use 'class' tag to refer to type 'time' in this scope
1>...\ucrt\time.h(518,42): message : class 'time' is hidden by a non-type declaration of 'time' here

So the question is: where does the C++ standard forbid freely using symbols from the C standard library when they are not explicitly included in a compilation unit?


Interestingly enough the same code (once translated...) works fine in C:

#include <stdio.h>

//
typedef struct 
{
    int h, min, sec;
}time;
//
const int full = 60;
//
void canonify(time* pre)
{
    pre->min += pre->sec / full;
    pre->h += pre->min / full;
    pre->sec %= full;
    pre->min %= full;
}
int main()
{
    time a;
    a.h = 3;
    a.min = 128;
    a.sec = 70;
    canonify(&a);
    printf("%d:%d:%d\n", a.h, a.min, a.sec);
    return 0;
}
Zahra Bayat
  • 903
  • 3
  • 11
  • 14
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • 2
    https://stackoverflow.com/questions/37589226/can-a-c-standard-library-header-include-a-c-standard-header This seems like a dupe to me, but I am not confident to use my dupe hammer. – bolov Jul 20 '21 at 12:42
  • 4
    The global namespace is very polluted with identifiers. Titus Winters called it the wild west. Your code is not forbidden from using symbols in the global namespace from the C standard library. Including the C++ wrapped C header files are not obligated to provide the C symbols in the global namespace (but most of them do — most, but not all), regardless they are *reserved*. – Eljay Jul 20 '21 at 12:46
  • 2
    @bolov: It is indeed the same problem. But the language-lawyer tag ask answers with precise reference to the standard. But I must admit that I had found the other question first I would not have asked this one ;-) – Serge Ballesta Jul 20 '21 at 12:56
  • 1
    FYI, `std::endl` is totally redundant here. Returning from `main` will flush `std::cout`. And in general there's usually no need to force-flush as part of printing a `'\n'` - cout is usually already line-buffered in cases where that's useful (i.e. when stdout is a TTY). – Peter Cordes Jul 21 '21 at 00:55
  • 2
    The reason your translation to C works, is that the C standard _forbids_ C standard-library headers to (behave as-if they) include each other (with a small and documented set of exceptions), whereas the C++ standard _allows_ C++ standard library headers to include each other. So `iostream` can, and does, expose symbols from `ctime`, but `stdio.h`, _when compiled as C_, may _not_ expose symbols from `time.h`. – zwol Jul 21 '21 at 13:16
  • @bolov: Rightly so - neither question is tagged with a C++ Standard, and this is the sort of question which requires a precise Standard reference. In particular, the proposed dupe would be based on C++14, while we now have C++20 – MSalters Jul 21 '21 at 14:11

2 Answers2

40

[extern.names]

3 Each name from the C standard library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace.

Note that this paragraph reserves the name itself. So aliasing time in the global namespace violates this contract.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 5
    The solution being to wrap your own `time` inside your own `namespace`. – Eljay Jul 20 '21 at 12:48
  • 3
    @Eljay - Under the caveat you may not use C language linkage for it. – StoryTeller - Unslander Monica Jul 20 '21 at 12:50
  • 3
    @Eljay Which is what one should always do for every declaration (except for that one namespace and things like `main`. And I suppose cross language interfaces.) – eerorika Jul 20 '21 at 12:52
  • 2
    Thank you, I could not find that reference. But the number of symbols in C standard library is *laaaarge*, and I would expect many programmers to not know them all... – Serge Ballesta Jul 20 '21 at 12:54
  • @SergeBallesta And if you depend on third party C libraries (or lazy C++ libraries), then the number of symbols is even larger. This is part of why one should always avoid using the global namespace. – eerorika Jul 20 '21 at 12:56
  • 1
    @SergeBallesta - Cautious use of namespaces avoids the problem, as was mentioned already. At least you have recourse in C++. [C pretty much does the same](https://port70.net/~nsz/c/c11/n1570.html#7.1.3), but relies on convention rather than a language mechanism to avoid collisions. – StoryTeller - Unslander Monica Jul 20 '21 at 12:57
  • @M.M - They do https://timsong-cpp.github.io/cppwp/n4868/basic.link#2 . Unless you don't count them as types? – StoryTeller - Unslander Monica Jul 21 '21 at 04:21
18

Where does the C++ standard forbids to use freely symbols from the C standard library

Latest draft:

[extern.names]

Each name from the C standard library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace.

Each function signature from the C standard library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.


when they are not explicitely included in a compilation unit?

If you use the standard library at all, then all name reservations of the standard library are in effect.

If you include any standard header (or, any header whose exact content you don't control and thus may include standard headers), then you may be indirectly including other standard headers, including those inherited from C.

eerorika
  • 232,697
  • 12
  • 197
  • 326