Вывод текстовых данных.

Должен быть:
    * Удобен;
    * Эффективен;
    * Универсален.

Два основных случая, когда требуется вывод:
    * Вывод в stdout/stderr;
    * Формирование строк.

Для формирования строк нужно предварительно полностью формулировать задание
(PrintTask). Для вывода в файл это не так важно.

Любое объединение вывода в PrintTask в любом случае носит эпизодический,
прерывистый характер (рзабиение на отдельные предложения). Поэтому сложные
механизмы, привязанные к PrintTask, способны решить проблему лишь отчасти.
Доступ к обычным последовательным файлам должен синхронизироваться извне,
поэтому проблему синхронизации PrintTask'и тоже не решают. Нужны ли они вообще
для файлов? - нет, совсем не нужны. А для строк это актуально (замена snprintf).
Поэтому я введу PrintTask для создания строк.

Как насчёт snprintf в фиксированный буфер достаточно большого размера на стеке?
...похоже на String с preallocated storage. Можно явно выписать функцию,
замещающую snprintf и принимающую на вход PrintTask.

Как организовать вывод в файл? По сути, нужно сначала формировать строковое
представление входных данных (если это не строка). Затем либо складывать
результат в буфер, если строка маленькая, либо немедленно выполнять запись,
если она большая (используя writev там, где это целесообразно).

    а) Stringifier. Превращает всё в строку;
    б) X->toString() - для типов, не являющихся встроенными.

Что делает toString()? В оптимальном варианте он использует внешний буфер
(работает подобно snprintf).

То есть не Ref<String> toString(), а Size toString (Memory const &mem).
Не принимаю реализацию exc->toString(), в которой используется catenateStrings.

Как быть со встроенными типами? -> определить для каждого из них toString()
отдельно. Обобщённый шаблон toString() будет вызывать метод toString()
произвольного класса.

