Opened 4 years ago
Last modified 4 years ago
#922 assigned ожидается проверка
HW #3 (Huffman)
Reported by: | tarasov.denis | Owned by: | Egor Suvorov |
---|---|---|---|
Component: | HW #3 (Huffman) | Version: | 3.0 |
Keywords: | Cc: |
Description
Домашечка
Change History (8)
comment:1 Changed 4 years ago by
Owner: | changed from Egor Suvorov to tarasov.denis |
---|---|
Type: | ожидается проверка → ожидаются исправления |
comment:4 Changed 4 years ago by
Owner: | changed from tarasov.denis to Egor Suvorov |
---|---|
Version: | 1.0 → 2.0 |
CLI по условию отдельный класс.
comment:5 Changed 4 years ago by
Type: | ожидаются исправления → ожидается проверка |
---|
comment:6 Changed 4 years ago by
Type: | ожидается проверка → ожидаются исправления |
---|
Проверялась ревизия 4014 или ранее.
Третья попытка разблокирована.
Корректность 9/9
Тесты 7/8
- Когда тесты только процедурные (т.е. вход генерируется автоматически), их сложно проверять.
uint16 writing
точно можно сделать не доA
доZ
, а руками сделать сколько-то вызовов и проверить результат. Будет проще и понятнее. Аналогично во всех тестах. Иначе приходиться понимать, а что же именно генерируют тесты. - Например, я не могу быстро прочитать тест
Bit writing manips tests
и понять, что же такое делаютBitWriter
иBitReader
и как вообще ими пользоваться. - В
Two characters
лучше захаркодить, какой символ слева, а какой справа. А ещё лучше прямо битовые последовательности проверить — вы ими явно пользуетесь. ВThree characters
тоже. Да и в остальных небольших тестах (hello world на грани) — не предполагается, что поведение алгоритма меняется в процессе разработки, лучше его зафиксировать. Плюс вы сейчас никак не проверяете префиксность кодов, а так как раз проверите.
Архитектура 4/5
const_cast
яростно намекает, что вы делаете что-то не то. Вы вызываете не-legacy код из не-legacy кода, аconst_cast
всё равно нужен и не по делу.- Приватные статические константы вроде
MIN_ARGS_COUNT
— детали реализации, должны жить в.cpp
. Плюс они вообще не нужны: если аргументов мало, то лучше явно говорить "не хватает такого-то аргумента" (это у вас и так есть), чем "чот мало аргументов". - Не используйте
eof()
для проверки конца файла. Есть куча способов получить ошибку чтения, вы разбираете только один случай.
Стиль 5.5/8 (в основном срезано из-за кривых отступов)
- Не используйте C-style cast (
TestTree.cpp
, например) buildCodes()
— не заглушайте ошибки- Не заглушайте ошибки в
HuffmanArchiverException::what()
:type_
всегда должен быть чему-то равен!assert
- Почти все сложные конструкторы вроде
CLI
можно сделатьexplicit
(с появлением list-initialization это осмысленно даже для конструкторов от не одного параметра). - У
namespace huffman {
на весь файл лучше отступ не делать, будет проще читать. Добавьте только} // namespace huffman
на закрывающей скобке. vector<bool>
лучше сравнивать не через.size() == 1 && [0] == 1
, а через== vector<bool>{0, 1, 0, 1}
, будет понятнее.- Вместо
<<
и>>
для побайтового ввода-вывода следует использовать неформатированный ввод-вывод:get
/put
/read
/write
. - Возможно, вместо
char
везде будет лучше сделатьuint8_t
, чтобы не надо было беспокоиться про потенциальную знаковость. TestTree.cpp:70
: что за число 128? Лучше битовый литерал0b1000'0000
. Аналогично ниже. А'
можно разделять символы.char A = X; CHECK(A == Y)
лучше записать какCHECK(X == Y);
- Вместо
std::string() + static_cast<char>
лучше записать'\0'
, а ещё лучше — массив из четырёх элементов и делать==
с ним. Можно в крайнем случае 7сделатьstring(arr, arr + 4)
. - Не используйте
rand()
, используйте<random>
из C++. - Лучше не
is_open
, а простоoperator bool
. - В
CLI.h
поехали отступы. - В заголовках левые инклуды (можете посмотреть на тулзу IWYU):
CLI.h
не нужны включения ниHuffmanArchiver.h
, ниfstream
.- Инклуды должны быть целиком внутри гардов (привет от
HuffmanExceptions.h
) Streammanips.h
хватитiosfwd
вместоiostream
HuffmanArchiver.h
не хватаетmap
HuffmanTree.cpp
::39
:const auto&
дял оптимизации- Поехавшие отступы в строчках 56-57
- Вы стараетесь выровнять аргументы строго правее имени метода, что плохо работает с длинными именами методов. Просто сделайте перенос строки сразу после
(
, отступ и спокойно пишите имена параметров.
counter
-->chars_read
, плюс это циклfor
.- Странный формат: зачем сначала писать все символы, а потом частоты? Логичнее, мне кажется, если записывать сразу пары (символ, частота) друг за другом. Удобнее отлаживать бинарный формат.
comment:7 Changed 4 years ago by
Owner: | changed from Egor Suvorov to tarasov.denis |
---|
comment:8 Changed 4 years ago by
Owner: | changed from tarasov.denis to Egor Suvorov |
---|---|
Type: | ожидаются исправления → ожидается проверка |
Version: | 2.0 → 3.0 |
Note: See
TracTickets for help on using
tickets.
Проверялась ревизия 3848.
Весьма приятно читать в среднем, спасибо.
Корректность 7/9
-e
, а-u
.Тесты 5/8: случаи хороши, но код надо сильно улучшить.
try
/catch
. Если вылетит исключение,doctest
его сам поймает и красиво упадёт, а чтобы ожидать исключение, есть специальные команды.FILE_SIGNATURE
. Налажаете в одном месте — скопируете в тесты, не заметите подвох. В том жеCompress empty
лучше полностью сравнить всё содержимоеstringstream
с фиксированным массивом констант. Да, при изменении сигнатуры изменятся тесты, но ведь и формат тоже полностью поменяется. Что как раз повод взглянуть на тесты.stringstream
, а взятьstr
и сравнить за одну операцию с известным массивом.what()
нужен для людей, а не для автопроверок. Автопроверки должны смотреть на коды ошибок или, что лучше, на конкретные типы исключений. ВExtract invalid empty
явно хочется проверить тип исключения.fail()
лучше проверять у потока просто конвертацию к булуsize
stringstream
и так пустой.Архитектура 2.5/5
size_t
в формате. Равно как и на endianness.CLI
незачем делать классом. Всё равно есть ровно один вариант использования: создать отargc, argv
, вызватьrun()
. Это свободная функций. При этом всё ещё хочется протестировать разбор параметров.main
всегда завершается успешно.buildCodes
лучше возвращатьmap
, иначе непонятна семантика, если вcodes
уже что-то есть.HuffmanTree::getChar()
похоже на итератор или чтение из потока. Это точно не стоит делать черезstd::function<>
— это лишние виртуальные вызовов и динамического выделения памяти (одно на вызовgetChar
). Хотя бы шаблонный функтор тут передавать.HuffmanNode::symbol_
имеет смысл только если!leftChild_ && !rightChild_
. Чтобы это гарантировать, следует сделать это поле приватным и сделать геттер с ассёртом. И сделать проверку этого условия, оно много где возникает. Аналогично сcount
: оно не должно меняться.unique_ptr
только в вершинах, но по факту есть куча владеющих чистых указателей.recursiveWalk
первый параметр должен быть чистым указателем: ему глубоко всё равно на способ владения вершиной.basic_istream<char>
, аistream
.unique_ptr<char[]>
—vector<char>
, пожалуйста. Заодно исчезнет стрёмный параметрuniqueElements
по ссылке.stats_
— это не состояниеHuffmanArchiver
, а результат последней операции. Пусть его лучше операция и возвращает.countFrequencies()
и вообще лучше [мыть тарелки перед едой](http://blog.algoprog.ru/init-not-clear/) (я проseekg
)writeCompressedData
переплетает битовый ввод-вывод с логикой Хаффмана. Это надо тестировать отдельно по-хорошему. Я бы сделал отдельную штуковину для битовых манипуляций (см. замечание выше проstd::function
).checkInFile()
/checkOutFile()
прекрасно заменяются на встроенные вiostream
исключения.Стиль 4/8
huffman::
. Лучше просто на весь.cpp
открытьnamespace
, всё равно вы реализуете функции из него. Или сделатьusing huffman::HuffmanException
хотя бы.CLI::CLI
: довольно недружелюбный к пользователю разбор параметров. Вы очень жёстко завязались на количество аргументов. Пользователь не хочет считать аргументы (и программист тоже), они хотят добавлять недостающие или убирать лишние. Число 5 встречается дважды и оба раза это магическая константа, которую код может сам прекрасно вывести. Лучше пройтись по всем аргументам.HuffmanException::CLI_MODE
. Судя по коду — что режим не задан. В названии константы этого нет.NONEXISTENT_FILE
— не совсем, на файл может не быть прав. Скорее "файл не удалось открыть".CLI::run()
не стоитassert
, чтоmode_
корректен. И на прочие инвариантыCLI
.inf
/of
— несимметрично. Лучшеinput
/output
, не экономьте символы.exceptType
— тип, с большой буквы, пожалуйста.HuffmanException::what()
заглушает ошибки.HuffmanTree
, тестыHuffmanTree
, а файлHuffman.h
— несимметричноHuffmanNode
должен приниматьunique_ptr
— он же принимает владение детьми.HuffmanException
можноnoexcept
.leftChild_
/rightChild_
можно простоleft_
/right_
recursiveWalk
— кажется, что оно не просто обходит, а с конкретной целью. Судя по параметруcodes
. И, наверное,currentCode
можно не копировать каждый раз.vector<HuffmanNode*> nodes
должен владеть своими нодами, судя по всему.unique_ptr
, пожалуйста. Или по значению храните. В лямбду можно передаватьunique_ptr
по константной ссылке.map table
лучше итерироваться сconst auto&
, а неauto
Huffman.cpp:32
— не хватает пустой строчки между соседними ифами.Huffman.cpp:52
— уберите запятые. Напишите нормальные фигурные скобки и;
Huffman.cpp:57
— очень странное место. Вы иногда можете создать вершину с одним ребёнком?a != nullptr
==>a
Huffman.cpp:77
— это вроде только в одном случае бывает. Хочу assert.Huffman.cpp:111
— лучше перевернуть if. Сначала разобрать 0, потом 1.writeHeader
/readHeader
руками считают количество записанных/прочитанных байт. Пусть это происходит автоматически.