Opened 4 years ago
Closed 4 years ago
#966 closed ожидается проверка (задача сдана)
HW #3
Reported by: | Gleb Marin | Owned by: | Egor Suvorov |
---|---|---|---|
Component: | HW #3 (Huffman) | Version: | 3.0 |
Keywords: | Cc: |
Description
Change History (3)
comment:1 Changed 4 years ago by
Version: | 1.0 → 3.0 |
---|
comment:2 Changed 4 years ago by
Component: | HW #1 (BMP) → HW #3 (Huffman) |
---|
А ещё, как я понимаю, это всё-таки Хаффман.
comment:3 Changed 4 years ago by
Resolution: | → задача сдана |
---|---|
Status: | assigned → closed |
Первая и последняя посылка — по акции, с весом 0.5. Проверялась единственная ревизия 4055.
Корректность 3/9
test_hw_03
должен лежать рядом сhw_03
, а не в подпроекте- На ваших же тестах Valgrind ловит кучу неинициализированных чтений (это UB и FATALITY) в
binary io end-to-end
/small case, big numbers
. - Не работает под Windows: файлы надо открывать в бинарном режиме.
- Падает на пустом тесте.
__CHAR_BIT__
-->CHAR_BIT
, иначе это что-то внутреннее конкретного компилятора.- Не работает с файлом с именем, например
--file
. - Не работает с файлами размером больше 4 ГБ (потому что размер файла в
uint32_t
не хранят).
Тесты 5/8
- Тесты должны быть реализованы не в
TestMain.cpp
, а в отдельных файлах. Это сильно усложняет чтение. - Не проверен парсинг аргументов командной строки вообще никак.
- Не проверяется посчитанная статистика.
Архитектура 0.5/5 — идеи правильные, но очень перегружено и намешано
- Лучше поставить один раз
static_assert(CHAR_BITS == 8)
и дальше честно хардкодить число 8. Всё равно наверняка не получится работать, если в байте не 8 бит. Например, вы всё выражаете черезwrite(uint8_t)
/read(uint8_t)
, это не сработает, если в байте, скажем, 16 бит. Или вот вread<uint8_t>
вы сравниваете не с 8 битами, а сCHAR_BIT
. - Странно, что битовые "потоки" жёстко завязаны на вектор, а не поддерживают произвольные итераторы.
- Не стоит помечать
noexcept
функции, которые могут веруть что-то сложное по значению. Им надо вызвать конструктор копирования, а он может кинуть. Bytes
- Неясно, зачем подключать
Archiver.h
. - Нехорошо, что
write
шаблонная, хотя на самом деле у неё ровно три вида. Лучше три перегрузки, будет точнее выражаться намерение. writeBytes
должен приниматьconst Bytes&
- Нехорошо выносить поля в общий базовый класс
BitsStream
, если всю логику всё равно реализует каждый наследник отдельно. Это поля, подозреваю, имеют совершенно разный смысл вIstream
/Ostream
. Плюс в каждом из наследников дополнительно возникают какие-то_bitsFilled
/_bitsLeft
/_firstBatch
. - Странный
flush()
. Особенно в сочетании с тем, что указатель приходится всё равно руками двигать. - Странно, что вы выразили чтение "не больше 8 бит", а потом всё остальное. Получилось две сложные функции. В итоге код разбора на биты перемешан с кодом считывания из итератора. Лучше было бы сделать чтение одного байта, потом чтение одного бита и потом чтение N бит, получилась бы две простых функции и одна посложнее.
- Аналогичная претензия к записи: "могу записать не больше 8 бит" — какое-то довольно произвольное ограничение, плюс код усложняет.
- Очень много где вам требуется явно указывать шаблонный параметр — это плохо.
- Неясно, зачем подключать
CLI
triggers()
возвращает строчку, которая используется ровно для одной цели: сразу сделатьsplit()
(что само по себе нетривиальная функция, которой хорошо бы приниматьstring_view
, кстати). Причём возвращаемая строчка обычно захардкожена на этапе компиляции. Это очень, очень плохо: ни типы не подсказывают о происходящем, ни упрощения кода нет. Пусть лучше возвращаетvector<string_view>
.addFlagHandler(flag)
может казаться, что можно написатьaddFlagHandler(FlagHandler)
, хотя это не так. Понятно, почему: вы не берёте владение параметром. Но это странно. Было бы гораздо удобнее не заводить отдельные переменные к классуCLI
. Пусть лучше принимает, скажем,unique_ptr<FlagHandler>
и вызывается какmake_unique<>
.char **argv
— плохая идея, прибивает гвоздями устаревший сишный интерфейс из указателей. Лучше сконвертировать типы в, скажем,vector<string_view>
/span<string_view>
и работать с ним. Заодно можно будет избавиться от костыльногоint flagInd
и передавать, скажем, сразу суффикс аргументов. И возвращать суффикс аргументов - без обработанного.- Типы
flagHandlersMap
/flagHandlersList
(второй, кстати, ещё и неlist
) — явно детали реализации, не надо добавлять этиusing
. А если и добавлять, то называть консистентно с типами вродеFlagHandlers
, с большой буквы. logic_error
— это для ошибок программиста. А "флагов не хватило" — это ошибка пользователя. И вообще для ошибок парсинга, кажется, лучше отдельный класс завести.
Huffman
NodesComparator
— точно деталь реализации, нужна только в.cpp
- У
Node
слишком большой интерфейс, слишком много всего хранит. Например,_terminal
явно вычисляется, равно как и_code
. - При этом у самого
HuffmanTree
мы почти ничего сделать не можем. Только ссылку на корень взять. Это довольно странно. followingNode
должен приниматьbool
. Откудаuint8_t
— непонятно.fillByteCodes
— какая-то сложная штука, которую сложно вызывать и начинать надо с определённого вида параметров => деталь реализации, приватный метод.- Конструктор
HuffmanTree
модицифирует свой параметр. Это очень странно выглядит. - На этом фоне
getTotalBitsCount()
смотрится безумно странно. Это какая-то случайная статистика про дерево. - Неясен смысл обёртки
ByteCode
.
*Archiver
- Лишний
ExtractedBytes
и рядом. DummyArchiver
нигде не используется => неясно, зачем тут полиморфизм.- Неясно, зачем публичный
setAdditionalDataSize()
. - Из-за прибития гвоздями массивов в качестве способа ввода-вывода вы не только потребляете линейное количество памяти, но и обязаны очень, очень аккуратно посчитать нужный размер массива. Выглядит жутко.
- Лишний
Стиль 4/8
- Не
x.good()
, а простоx
Bytes
- В одном месте параметр зовётся
bits
, в другом —count
inline
для шаблонных функций сам поставится- Странно, что в
BitsIstream
потребовался и_bufferEnd
, и_bitsLeft
, и `_first - В
read
стоит поставитьstatic_assert
, что переданный тип — целочисленный и беззнаковый. Иначе будет переполнение знакового типа, что UB. Или неверное чтение вещественных типов. - Количество байт также стоит проверять на
< 0
. static_cast<size_t>(count)
- плохо, тут этот каст не по делу и заглушает предупреждение. Возможно, лучше приниматьsize_t count
сразу.- Буфер лучше сбрасывать сразу после того, как дописали последний бит, а не перед следующей записью.
- По
vector<bool>
тоже лучше итерироваться черезfor (const auto & : ...)
. a << (b)
— скобки не нужны (write<uint8_t>
)- Много промежуточных переменных вроде
cur
/continuingBits
/shift
/cnt
и скобок вокруг. Из-за этого сложно читать. Кажется, можно половину выкинуть и сократить код. readBytes
— лишнее условие вwhile
. Почему-тоeof()
проверяется в двух местах. Должно хватать одного.
- В одном месте параметр зовётся
CLI
- Третий параметр у
FlagHandler
не читается. Лучше подсказку:, /*required=*/true
. DataFlagHandler
--> FlagWithArgumentHandler?`, возможно..?required()
-->isRequired()
для симметрии сhasFlag()
.std::string _data = ""
— лучше простоstd::string _data;
, конструктор по умолчанию.
- Третий параметр у
- НЕ ИСПОЛЬЗУЙТЕ
rand()
, это не потокобезопасно как минимум. Используйте плюсовый рандом. HuffmanArchiver
- Довольно длинные методы, сложно читать. Хотя бы добавьте пустые строчки между.
- Зачем-то указываете шаблонный параметр при записи вектора булов. И ещё указываете его как
const vector<bool>&
, а не хотя быvector<bool>
.
main
- Очень много кода подряд и переменных. Хотя бы выделили функции чтения из файла (раз уж вы всё равно читаете файл целиком) или пустых строк добавили.
Note: See
TracTickets for help on using
tickets.
Первая и последняя посылка — по акции.