[libcxx] Why the type of (w)cin/(w)cout/(w)cout are char []?

Hello all,

I am porting libcxx to Windows (not mingw). I am curious about the design of (w)cin/(w)cout/(w)cout. Currently we define (w)cin/(w)cout/(w)cout as char under src/iostream.cpp and initialise them as istream/ostream in function ios_base::Init::Init(). Why don’t we defined them as istream/ostream object directly? The drawback of current design in my mind is that cause linking error under Windows. Windows mangler takes data type into account. Reference of std::cin would be translated into ?cin@__1@std@@3V?$basic_istream@DU?$char_traits@D@__1@std@@@12@A (class std::__1::basic_istream<char,struct std::__1::char_traits > std::__1::cin). Definition of std::cin, in contrast, would be translated into ?cin@__1@std@@3PADA (char * std::__1::cin). There is a linking error because two symbols are different.

Any suggestion or comment is appreciated,
Yi-Hong

Hello all,

I am porting libcxx to Windows (not mingw). I am curious about the
design of (w)cin/(w)cout/(w)cout. Currently we define
(w)cin/(w)cout/(w)cout as char under src/iostream.cpp and initialise
them as istream/ostream in function ios_base::Init::Init(). Why don't we
defined them as istream/ostream object directly?

I /think/ the reasoning behind this is to get tight control over their initialization order.

Jon

This was done because the standard forbids the destruction of these objects in the atexit chain.

Howard

Hello Jon & Howard,

Thanks for your reply. I took a look of C++ specification. It mentioned constructors and destructors for static objects can access these objects (cin/cout/cerr) to read input from stdin or write output to stdout or stderr. As far as I know, cin/cout/cerr is initialized by the constructor of __start_std_streams under src/iostream.cpp. I am wondering how libcxx guarantee ios_base::Init::Init() would be invoked before execution of constructor of any other static objects? Is there any potential ‘static initialization order fiasco’ issue?

Thanks for your help,
Yi-Hong

It has been a long time since I wrote this code, but at the time I was able to convince myself that the libc++ initialization of global statics would happen prior other statics programmed by the customer. At that time, libc++ was being written just for OS X.

Howard

It has been a long time since I wrote this code, but at the time I was
able to convince myself that the libc++ initialization of global statics
would happen prior other statics programmed by the customer. At that time,
libc++ was being written just for OS X.

I don't think this holds any longer, including on OS X. I know it doesn't
hold for things linked in statically (ie libc++experimental). However the
alternative is to emit a static construction in every TU that include
<iostream> (
http://llvm.org/docs/CodingStandards.html#include-iostream-is-forbidden). I
would be pretty unwilling to make that change until *not* doing it becomes
a large issue.

One alternative to using char arrays to prevent destruction would be to
use a C++11 union with an empty destructor. That could be a possible
implementation for Windows.

It has been a long time since I wrote this code, but at the time I was able to convince myself that the libc++ initialization of global statics would happen prior other statics programmed by the customer. At that time, libc++ was being written just for OS X.

I don't think this holds any longer, including on OS X.

Interesting.

I know it doesn't hold for things linked in statically (ie libc++experimental).

Agreed. At the time I made this decision, I was consciously deciding that OS X would have only a dylib std::lib option.

However the alternative is to emit a static construction in every TU that include <iostream> (http://llvm.org/docs/CodingStandards.html#include-iostream-is-forbidden). I would be pretty unwilling to make that change until *not* doing it becomes a large issue.

At the time, Apple was (and almost certainly still is) allergic to namespace scope statics requiring dynamic initialization. By moving __start_std_streams into a source, as opposed to its traditional place in a header, I reduced the number of namespace scope statics associated with <iostream> from once per inclusion to only one for linking against the std::lib. This was a deliberate design choice to deal with Apple’s allergies.

I have no strong opinion on if, when or where this design decision should be reversed. I’m merely documenting the rationale that I used in case that is helpful.

One alternative to using char arrays to prevent destruction would be to use a C++11 union with an empty destructor. That could be a possible implementation for Windows.

That sounds interesting. Another option is a clean-sheet optimal implementation for each platform separated out with #if. It isn’t that much code.

Howard

Dropping the list. I probably should have been clearer. I haven't tested it
yet, but I had static initialization order issues on OS X while writing the
global PMRs. I just assumed the same issue may transfer to the std::stream
case.
It's very possible I'm wrong and ignorant.

/Eric

Dropping the list.

Or not.



From: Eric Fiselier via cfe-dev
Sent: Tuesday, May 17, 2016 10:17
To: Howard Hinnant
Reply To: Eric Fiselier
Cc: Clang Dev
Subject: Re: [cfe-dev] [libcxx] Why the type of (w)cin/(w)cout/(w)cout are char ?

|

  • |

Dropping the list.

Or not.

‎Nothing to see here… move along