Console I/O

Global IO Functions

Convenience functions for simple console I/O.

cin, cout & cerr

The I/O streams for current input and output cin, cout, and cerr are thread-safe in C++, so they allow simultaneous access from multiple threads.

Cilia follows this approach with ThreadSafeTextStreams.

The “raw”, not thread-safe in/out streams are three globals:

With these plain TextStreams, simultaneous access from multiple threads would be undefined behaviour (UB) and sooner or later lead to memory corruption.

Then there are three thread-safe globals:

ThreadSafeTextStreams can be redirected, e.g. with cerr.setBaseStream(system::console::out)

ThreadSafeTextStream

The thread-safe streams have the same interface as the plain TextStreams, but access to the “user-level” buffer is restricted via a lock, allowing simultaneous access from multiple threads.

Due to the locking the performance is reduced, especially when reading/writing in small chunks (i.e. single bytes, integers, etc.). Use a local TextBufferStream to improve on that.

Note
While simultaneous access is “safe”, the output of two threads might/will still be intertwined. A local TextBufferStream will fix that, too.

Note
While the API is the same as that of TextStream, the ABI (i.e. the memory layout) is not the same. So you can not use a ThreadSafeTextStream* as TextStream*. Again, a local TextBufferStream will fix that.

class ThreadSafeTextStream : ThreadSafeTextOutStream, ThreadSafeTextInStream

ThreadSafeTextInStream

A ThreadSafeTextOutStream automatically flushes buffered output if no explicit flush occurs for 0.5 seconds.

class ThreadSafeTextInStream {
    ThreadSafeTextInStream(TextInStream baseInStream)

    setBaseStream(TextInStream baseInStream)

    read() -> String
    // ...

protected:
    Lock inLock
    Byte* inBuffer
    Int inPosition
    Int inCapacity
    TextInStream* baseInStream
}

ThreadSafeTextOutStream

class ThreadSafeTextOutStream {
    ThreadSafeTextOutStream(TextOutStream baseOutStream)

    setBaseStream(TextOutStream baseOutStream)

    write(String)
    // ...

protected:
    Lock outLock
    Byte* outBuffer
    Int outPosition
    Int outCapacity
    TextOutStream* baseOutStream
}

TextBufferStream

A TextBufferStream buffers text locally. Small reads and writes are done efficiently without locking, the underlying stream is accessed in big chunks only (e.g. full lines or full buffers). The buffer of the base stream will mostly be circumvented.

It is used to buffer ThreadSafeTextStreams (like cin, cout, cerr), as those are slow for small reads/writes.

class TextBufferStream : TextBufferOutStream, TextBufferInStream

TextBufferInStream

A TextBufferInStream reads text from the underlying base stream (done efficiently in big chunks) and buffers it locally for small reads (done efficiently without locking).

class TextBufferInStream {
    TextBufferInStream(ThreadSafeTextInStream baseThreadSafeInStream)

    setBaseStream(ThreadSafeTextInStream baseThreadSafeInStream)

protected:
    ThreadSafeTextInStream* baseThreadSafeInStream
}

TextBufferOutStream

A TextBufferOutStream buffers small writes locally (done efficiently without locking) and writes it to the underlying base stream (done efficiently in big chunks).

class TextBufferOutStream {
    TextBufferOutStream(ThreadSafeTextOutStream baseThreadSafeOutStream)

    setBaseStream(ThreadSafeTextOutStream baseThreadSafeOutStream)

protected:
    ThreadSafeTextOutStream* baseThreadSafeOutStream
}

Platform-specific Raw Streams

system::console::in, out, and err are implemented by platform-specific streams. They wrap an operating-system handle or file descriptor and implement TextOutStream::writeRaw() / TextInStream::readRaw().

They are meant to wrap the three standard streams stdin/stdout/stderr. They could wrap any compatible handle or descriptor (console, pipe, file, socket), but that is rarely used.

They are not thread-safe. Use cin / cout / cerr for concurrent access.

The file descriptor / OS handle is not owned: close() flushes and marks the stream closed, but does not close the file descriptor / OS handle (as that would close stdin/stdout/stderr for the whole process).

FileDescriptorStream

Unix, Linux, macOS only, wraps a POSIX file descriptor (an Int32).

class FileDescriptorStream : FileDescriptorOutStream, FileDescriptorInStream

I/O uses read() / write().

isTerminal() is isatty().

FileDescriptorInStream

class FileDescriptorInStream : TextInStream {
    FileDescriptorInStream(Int inFD)

protected:
    Int32 inFD
}

FileDescriptorOutStream

class FileDescriptorOutStream : TextOutStream {
    FileDescriptorOutStream(Int outFD)

protected:
    Int32 outFD
}

FileHandleStream

Windows only, wraps a Win32 HANDLE (typically from GetStdHandle(), but also from CreateFile(), CreatePipe(), …).

class FileHandleStream : FileHandleOutStream, FileHandleInStream

If the handle is a console (GetFileType → FILE_TYPE_CHAR):

If the handle is a file or pipe:

isTerminal() is GetConsoleMode().

FileHandleInStream

class FileHandleInStream : TextInStream {
    FileHandleInStream(Handle handle)

protected:
    Handle inHandle
}

FileHandleOutStream

class FileHandleOutStream : TextOutStream {
    FileHandleOutStream(Handle handle)

protected:
    Handle outHandle
}