да, хорошо. Потому что чувствую, что немного сумбурно вышло. Внимание, длиннопост.
Итак, для чего это нужно. Обычная либа с CSV форматом. Почему не взял готовое? Потому что всё, что я нашёл готовое - говно. Это один из немногих случаев, когда приходится садиться за написание своего качественного велосипеда и доведения его до ума.
В чём суть либы (конкретно сейчас мы говорим только про часть либы, но не суть): есть класс для записи DSV-like файлов. DSV-like - это CSV, TSV, SCSV и так далее - что придумаете похожее, то и будет писать. Что мы можем в аднном случае конфигурировать? О, список довольно большой:
- Символ(строка)-разделитель
- Символ(строка)-кавычка
- Символ(строка) конца линии
- Символ(строка) комментария
- Символ(строка) для тримминга
- Policy для выставления кавычек (то есть как ставим кавычки: всегда или только на те поле, которые без кавычек нельзя выводить. А может быть только на не числовые значения? простор для фанатзии велик)
- Policy для тримминга (триммим всё в строке, или только начало, или только конец. Или вообще не тримим)
И это вроде как не всё (я скорее всего что-то забыл). Почему так много? Потмоу что я пишу велосипеды обощённые и не хочу, чтобы кому-то пришлось ещё раз писать всю эту дрянь.
Итак, у нас есть столько параметров, и нам нужно как-то запихнуть их в класс Writer. Окей, как? На ум приходит нескольно вариантов:
1)
template <typename Delimiter, typename Quote, ...>
class Writer{bla-bla-bla};
Какая здесь проблема? Всё очень просто - Вы не сможете передать сами символы сепараторы, кавычки и так далее через параметры шаблона, вы только можете указать их типы. А сами символы вам придётся передавать уже в конструкторе, сохранять их внутри класса и работать с ними. Выглядеть это будет вот так:
Writer<char,char> writer(',', '"');
К тому же, если Вы хотите предоставить пользователю уже готовый класс для работы с тем же TSV (а в TSV в качестве сепаратора используется \t, то у вас не получится обьявить такой ТИП (так как мы не можем символы пихать в тип, потому что у нас везде typename, а не char,int,etc.)). Нам придётся давать пользователю уже готовые ОБЬЕКТЫ для работы с этим, чего делать бы очень не хотелось (так как нам же надо тогда давать интерфейс допольнительный для установки выходного потока, так как обьект уже есть). ДА и вообще, готовый обьект - дерьмо. Пользователь должен сам создать Writer тогда, когда ОН хочет, а не я. А я должен дать ему макисльно удобные средства для этого.
Вариант мне нравится.
2)
template <char Delimiter, char Quote, ...>
class Writer{bla-bla-bla};
Сразу отметаем, так как мы тут привязываемся к типам, которые я задам. А если пользователь захочет wchar_t или ещё какую-нибудь свою штуку, которая будет нормально выводится? Сразу отметаем.
3) Улучшение первого варианта через трейты
template <typename Dialect>
class Writer{bla-bla-bla};
Всё остаётся по аналогии с первым пунктом, только теперь у нас есть набор предефайненых трейтов для разных случаев жизни: TSV, CSV, SCSV, что-нибудь ещё (трейты эти я дам пользователю сам). Если пользователю не понравится, он всегда сам сможет сделать свой трейт и подпихнуть его в мой Writer. (вот пример, как это примерно будет выглядеть: https://pastebin.com/ETfS7r4K)
Проблема в том, что снова же обьекты уже трейта будут предефайнены, потому что снова же мы внутренности их инициализируем не в типе шаблона, а уже в конструкторе (подпихиваем, что там за символы внутри будут)
Мне этот вариант нравится больше всего.
Что скажете?
Ха, из всего описанного выше, складывается впечатление, что самым нормальным вариантом, было бы использование не объекта, а его интерфейса - при том, статического(ну т е статик функции члены и т.п