Для успешной сдачи задачи нужно реализовать базовые условия всех подзадач. Поэтому сначала сделайте базовые условия всех подзадач, и только после этого беритесь за допы. Допы можно делать свободно из разных подзадач в любом порядке.
Реализуйте оператор для печати std::tuple
. Должен работать код наподобие следующего:
// кортеж может быть любым возможным, не обязательно 3 этих типа
std::tuple<int, std::string, double> t = {5, "abcd", 3.14};
// выводит (5, abcd, 3.14). Вместо std::cout может быть файловый поток (ofstream)
std::cout << t;
Вы ограничены возможностями стандарта С++11. Это означает, что нельзя, например, использовать if constexpr
, std::integer_sequence/std::index_sequence
и fold expressions, т.к. они появились в более поздних версиях. (Этот запрет действует только здесь; в допах этой подзадачи и в следующих подзадачах его нет.)
Дополнительно:
- Сделайте другую реализацию, на этот раз используя
if constexpr
. - Сделайте ещё одну реализацию, используя выражения свёртки (fold expressions) и, если понадобится,
std::integer_sequence/std::index_sequence
. - Реализуйте собственный вариант шаблонного класса
tuple
. - Напишите программу, которая компилируется дольше 1 минуты. Успешная компиляция не требуется.
Формат CSV: табличные данные могут быть представлены как текстовый файл с разделителем \n
между строками и символом ,
для разделения ячеек внутри строки. Считаем, что данные символы не встречаются внутри данных.
Напишите класс, делающий возможным следующую потоковую работу с CSV:
int main()
{
ifstream csv_stream("test.csv");
CsvParser<int, std::string, double> parser(csv_stream, 0 /*skip first lines count*/);
for (std::tuple<int, std::string, double> rs : parser) {
std::cout << rs << "\n";
}
}
Потоковая обработка подразумевает lazy (ленивое) чтение строк, то есть вы читаете строку только тогда, когда она реально нужна. Таким образом необходимо реализовать InputIterator для чтения данных в CSV файле.
При сдаче продемонстрировать:
- Чтение нескольких валидных файлов и печать их содержимого в консоль.
- Чтение из
std::cin
. - Чтение нескольких ошибочных файлов и/или не подходящих по типам.
Дополнительно:
- Реализуйте проверку на этапе компиляции, что все шаблонные параметры парсера могут быть получены из строк (то есть, вы можете строку распарсить в этот тип). Если это не так, выведите ваше собственное понятное сообщение об ошибке.
- Добавьте в конец кортежа, который возвращает итератор, параметр типа
std::vector<std::string>
, куда будут записаны все остальные колонки, если их в файле больше, чем шаблонных параметров парсера. То есть, для примера выше итератор должен возвращать типstd::tuple<int, std::string, double, std::vector<std::string>>
. - Выделите логику приведения строки из CSV-файла к нужному типу в отдельную стратегию. Для этого сделайте так, чтобы первым шаблонным параметром CsvParser можно было указать шаблонный функтор для приведения строки к нужному типу. Сделайте реализацию этого функтора, которая использует
operator<<
, и другую реализацию, которая строку возвращает как есть, для числовых типов использует функцииstd::sto*
(например,std::stoi
дляint
), а остальные типы по умолчанию не поддерживает (при попытке их использовать возникает ошибка компиляции).
Превратите ваш класс FlatMap
из первой задачи в шаблонный класс, поддерживающий любые типы ключей (для которых определён operator<
) и любые типы значений (для которых определён конструктор без параметров).
template <class Key, class Value>
class FlatMap {
// ...
}
Note
Если в первой задаче вы не сделали итераторы, реализуйте их сейчас. Итераторы - часть минимальных требований данной задачи. Другие допы первой задачи делать не обязательно.
Дополнительно:
-
К шаблонным параметрам
Key
иValue
добавьтеCompare
иAllocator
, определяющие стратегии сравнения ключей и выделения памяти.template <class Key, class Value, class Compare = std::less<Key>, class Allocator = std::allocator<...> > class FlatMap { // ... }
Работайте с аллокатором не напрямую, а через шаблон
std::allocator_traits
.1.1. Продемонстрируйте, как можно использовать в качестве ключа тип, для которого не перегружены операторы сравнения (например,
<
). -
Реализуйте поддержку инициализации списком (List-Initialization), а именно, чтобы работал код наподобие
FlatMap<std::string, int> m = { {"a", 1}, {"b", 2} };
-
Реализуйте собственный аллокатор, который выделяет большой кусок памяти (пул) один раз при создании. Его метод
allocate
не делает реального выделения памяти, а лишь возвращает указатель на свободный участок из этого пула, а методdeallocate
просто ничего не делает. Убедитесь, что с вашим аллокатором корректно работает как ваш классFlatMap
, так и стандартные контейнеры (например,std::vector
илиstd::list
). Продемонстрируйте разницу в производительности контейнераstd::list
со стандартным аллокатором и с вашим.3.1. Реализуйте метод
deallocate
так, чтобы освобожденное место в пуле можно было использовать повторно. -
Реализуйте проверку на этапе компиляции, что тип
Key
поддерживает сравнение поoperator<
, а типValue
имеет конструктор без параметров. -
Сделайте специализацию таблицы для ключей целочисленных беззнаковых типов и значений типа
bool
(то есть, например,Key = unsigned int, Value = bool
). Эта специализация для хранения данных должна использоватьstd::vector<bool>
и не хранить дополнительно больше никаких данных. Из допов данной задачи эта специализация может использовать аллокаторы и реализовывать итераторы, другие допы для неё делать не надо. -
Реализуйте шаблонный метод
try_emplace
, который позволит вставлять элементы в таблицу следующим образом:class A { public: A(int x, double y, const std::string& z) { // ... } } FlatMap<std::string, A> map_of_a; std::pair<iterator, bool> x = map_of_a.try_emplace("key1", 3, 5.6, "test"); // создан и вставлен в таблицу объект A(3, 5.6, "test") по ключу "key1"
Если такой ключ уже есть в таблице, метод ничего не делает. Метод возвращает пару
std::pair<iterator, bool>
, гдеiterator
- это ваш тип итератора. В этой паре итератор указывает на элемент по данному ключу, а булево значение указывает на то, был ли этот элемент вставлен в результате вызоваtry_emplace
(true
), или он существовал раньше (false
). -
Сделайте так, чтобы ваш шаблонный класс
FlatMap
корректно работал с типами ключей и значений, у которых запрещено копирование или перемещение (но не оба сразу). Например, уstd::unique_ptr
запрещено копирование. -
Сделайте так, чтобы
FlatMap
корректно работал для случаев:Key = const T
,Value = const V
,Key = T&
,Value = V&
(гдеT
иV
- произвольные типы) и их комбинаций (например,Key = const T&, Value = V&
). Некоторые вызовы методов для некоторых комбинаций могут не компилироваться, это нормально (например,m[key] = value
для случаевValue = const V
). Но в целом работоспособность класса должна сохраняться.8.1. Принимается также частичная реализация (например, только для констант).