Вам требуется реализовать консольное приложение ./lab-10_cppio, которое
хранит упорядоченный список сотрудников некой компании.
Приложение позволяет просматривать существующий штат из
разработчиков и продавцов, добавлять новых сотрудников,
а также сохранять и загружать список в файл в бинарном виде.
Вам потребуется:
- Дописать реализации классов
Developer(сотрудник-разработчик) иSalesManager(сотрудник-продавец) вinclude/employees.hи соответствующем.cpp-файле. Эти классы уже хранят все необходимые данные и корректно рассчитывают зарплату при помощи методаsalary(). - Вынести общие для всех сотрудников логику, интерфейс и данные в базовый класс
Employee. Проектируйте классEmployeeс учётом того, что в будущем могут быть добавлены новые должности. - Реализовать класс
EmployeeArray, хранящий гетерогенный массив сотрудников (массив, в котором могут быть сотрудники разных типов).
Ввод/вывод наследников Employee, а также класса EmployeeArray должен быть реализован в стиле C++:
std::ostream& operator<<(std::ostream&, const Employee&);выводит текстовое описание сотрудника произвольного типа. Используется в командеlistчерезoperator<<(std::ostream&, const EmployeeArray&).std::istream& operator>>(std::istream&, Employee&);считывает свойства сотрудника конкретного типа (неизвестного на этапе компиляции). Используется в командеadd.std::ofstream& operator<<(std::ofstream&, const Employee&);выводит бинарное представление сотрудника произвольного типа, включая пометку о типе сотрудника. Используется в командеload.std::ifstream& operator>>(std::ifstream&, Employee&);считывает бинарное представление сотрудника конкретного типа (неизвестного на этапе компиляции), не включая пометку о типе сотрудника. Используется в командеsave.
Для этого вам потребуются виртуальные методы в классе Employee и наследниках.
Для упрощения работы с бинарным представлением также реализуйте собственные манипуляторы
в отдельных файлах bin_manip.h и bin_manip.cpp.
Разрешается использовать std::vector/std::string для упрощения кода,
в том числе как поля.
В этом задании не очень хорошо:
- То, что вывел
operator<<, нельзя сразу считать при помощиoperator>>. Нарушается симметрия. - Разделение текстового/бинарного формата происходит в зависимости от типа потока.
Это неочевидно и усложняет автотесты.
Нельзя записать бинарно в
std::stringstreamили записать текст в файл (например, для отладки).
Не забудьте:
- добавить/запретить конструкторы, деструкторы, конструкторы копирования и операторы присваивания, если это необходимо;
- переопределить операторы ввода/вывода с корректной обработкой ошибок;
- добавить
virtualкуда нужно; - добавить
friendкуда нужно.
Смотри стандартные требования из предыдущих заданий (они теперь доступны в общей папке), название папки и файла — lab-10_cppio.
Новое требование: всё, кроме main, должно лежать в каком-нибудь пространстве имён; обязательно использовать анонимные пространства имён, где разумно.
- Объект-сотрудник владеет C-style строчкой с именем.
- Экземпляр класса
EmployeeArrayвладеет всеми своими сотрудниками. - Назначение полей (может немного измениться после выделения базового класса
Employee):Developer: имя_name, базовая зарплата_base_salary, наличие бонуса_has_bonus.SalesManager: имя_name, базовая зарплата_base_salary, количество совершённых продаж_sold_nm, выручка с одной продажи_price.
- Все операторы ввода-вывода и вспомогательные манипуляторы должны корректно обрабатывать
ошибки и сигнализировать о них.
Например, если
read_c_strне нашёл нулевой символ до конца файла, это ошибка формата ввода.
- Все целые числа записываются в файл как четыре байта в порядке little-endian (от младших к старшим). Все числа знаковые, но от 0 до 2^31-1.
- Все булевские значения записываются как один байт: 0 соответствует
false, 1 соответствуетtrue. - Строчки записываются в бинарный файл в стиле Си: байты строчки, за которыми следует один нулевой байт.
Бинарный файл содержит один объект EmployeeArray в следующем формате:
<число сотрудников, int32><сотрудник 1>...<coтрудник N>
В начале каждого сотрудника идёт одно число — его тип, после чего набор полей, специфичный для типа:
- Формат
Developer:<тип := 1, int32><имя, c-string><оклад; int32><наличие премии; bool, 1 байт> - Формат
SalesManager:<тип := 2, int32><имя, c-string><оклад, int32><совершено продаж, int32><выручка с одной продажи, int32>
Для бинарного ввода-вывода реализуйте и используйте вспомогательные манипуляторы, а не работайте с байтами каждый раз заново.
Манипуляторы должны позволять читать и записывать в произвольные потоки (необязательно файловые) бинарные данные. Примеры кода ниже должны компилироваться и корректно работать.
Ввод-вывод знаковых 32-битных little endian чисел (тип std::int32_t из заголовка <cstdint>),
отрицательные числа не поддерживаются:
std::stringstream s;
int x = 10;
s << write_le_int32(x); // Теперь в s лежит четыре байта: 0A 00 00 00.
s >> read_le_int32(x); // Обратная операция: присваивает x = 10.
s.write("\x12\x34\x56\0", 4);
s >> read_le_int32(x); // x = 0x00563412 = 5649426
Ввод-вывод булевских значений (тип bool):
std::stringstream s;
bool y = true;
s << write_bool(y); // Теперь в s лежит один байт: 0 (false) или 1 (true).
s >> read_bool(y); // Симметрично: присваивает y = true.
Манипуляторы для ввода-вывода строк в стиле Си (тип char*):
char z[10] = "Hm";
s << write_c_str(z); // Теперь в s лежит 3 байта: 48 6D 00
s >> read_c_str(z, sizeof z); // Симметрично: считывает строку в стиле C (с завершающим нулём) и
// записывает в буфер z определённого размера.
// Если строка не помещается — ошибка формата.
s.write("Hello", 6);
s >> read_c_str(z, sizeof z);
assert(!strcmp("Hello", z));
Приложению требуется уметь по команде list выводить информацию об N
хранящихся сотрудниках в следующем виде:
1. <Должность сотрудника 1>
<информация о сотруднике 1>
2. <Должность сотрудника 2>
<информация о сотруднике 2>
...
N. <Должность сотрудника N>
<информация о сотруднике N>
== Total salary: <суммарная заработная плата всех сотрудников>
<пустая строка>
Обратите внимание на дополнительную пустую строку после конца списка.
- Для разработчика:
- Должность:
Developer - Информация:
Name: <имя> Base Salary: <базовая зарплата> Has bonus: <наличие бонуса, один символ, плюс или минус>
- Должность:
- Для продавца:
- Должность:
Sales Manager - Информация:
Name: <имя> Base Salary: <базовая зарплата> Sold items: <количество продаж> Item price: <выручка с одной продажи>
- Должность:
Приложение запускается следующей командой:
./lab-10_cppio
В процессе работы программа хранит в памяти список сотрудников,
считывает команды из stdin и выводит результат в stdout
(в стиле C++).
Каждая команда занимает одну строчку:
exit— завершение работы приложения, аналогично концуstdin.load <file-name>— добавить в конец текущего списка данные из файла, см. формат бинарных файлов.save <file-name>— сохранить текущий список в файл, см. формат бинарных файлов.add <type> <args>*— добавить сотрудника. Здесь<type>— это число-тип сотрудника (как в формате бинарных файлов), а<args>*— специфичные для типа параметры:- Для разработчика (
<type>=1): имя, базовая зарплата, наличие бонуса (0 или 1). - Для продавца (
<type>=2): имя, базовая зарплата, количество совершённых продаж, выручка с одной продажи.
- Для разработчика (
list— вывести текущий список сотрудников, см. формат вывода списка.
Ограничения:
- Все команды корректны, числа в текстовом формате записываются в обычном десятичном виде.
- Имена сотрудников не длиннее 100 символов и состоят из латинских букв, цифр, знаков дефиса и нижнего подчёркивания.
- Имена могут совпадать.
- Все числа-параметры сотрудников неотрицательны и не превосходят 10^9.
- Общая зарплата всех сотрудников в списке не превосходит 10^9.
Пример запуска приложения:
> ./lab-10_cppio
add 2 Joe 100 20 300
add 1 Billy 1000 1
list
1. Sales Manager
Name: Joe
Base Salary: 100
Sold items: 20
Item price: 300
2. Developer
Name: Billy
Base Salary: 1000
Has bonus: +
== Total salary: 2160
save example.edb
add 1 Bobby 500 0
load example.edb
list
1. Sales Manager
Name: Joe
Base Salary: 100
Sold items: 20
Item price: 300
2. Developer
Name: Billy
Base Salary: 1000
Has bonus: +
3. Developer
Name: Bobby
Base Salary: 500
Has bonus: -
4. Sales Manager
Name: Joe
Base Salary: 100
Sold items: 20
Item price: 300
5. Developer
Name: Billy
Base Salary: 1000
Has bonus: +
== Total salary: 4820
save merged.edb
exit
> hexdump -C merged.edb
00000000 05 00 00 00 02 00 00 00 4a 6f 65 00 64 00 00 00 |........Joe.d...|
00000010 14 00 00 00 2c 01 00 00 01 00 00 00 42 69 6c 6c |....,.......Bill|
00000020 79 00 e8 03 00 00 01 01 00 00 00 42 6f 62 62 79 |y..........Bobby|
00000030 00 f4 01 00 00 00 02 00 00 00 4a 6f 65 00 64 00 |..........Joe.d.|
00000040 00 00 14 00 00 00 2c 01 00 00 01 00 00 00 42 69 |......,.......Bi|
00000050 6c 6c 79 00 e8 03 00 00 01 |lly......|
00000059
<корень-личного-репозитория>
|--lab-10_cppio
|--include
| |-- bin_manip.h
| |-- employees.h
|--src
| |-- bin_manip.cpp
| |-- employees.cpp
| |-- main.cpp
|--Makefile
Папку obj, объектные и исполняемые файлы класть в репозиторий не разрешается.
Уточняйте у своего преподавателя.
- Задание оценивается в 10 баллов: 7 за корректность и 3 за стиль.
- Точные критерии оценки каждой из частей остаются на усмотрение преподавателя.