C++ 1MIT весна 1 2020 — различия между версиями

Материал из CSC Wiki
Перейти к:навигация, поиск
(Практика 4: Алфёров)
Строка 266: Строка 266:
 
* Почта для домашек: [mailto:vasily.v.alferov@gmail.com vasily.v.alferov@gmail.com]
 
* Почта для домашек: [mailto:vasily.v.alferov@gmail.com vasily.v.alferov@gmail.com]
 
* Телега для телег: [https://t.me/vasiliyalferov vasiliyalferov]
 
* Телега для телег: [https://t.me/vasiliyalferov vasiliyalferov]
 +
 +
Материалы с практик:
 +
 +
* 9 апреля: [https://gist.github.com/vasalf/aa36e02d305fc4cbfc3a066a6ecf6c64 (Tag dispatching, Erase-remove idiom & Mutable lambdas)]

Версия 09:10, 9 апреля 2020

Осенний семестр

Лекции

Лектор: Суворов Егор Федорович (egor_suvorov@mail.ru)

Весенний семестр

Планы лекций Егора: github.com/yeputons/hse-2019-cpp

Прочие материалы

Ссылки:

Книги:

  • Язык C++
    • Б. Страуструп, Язык программирования С++
    • Б. Эккель, Философия C++
  • Дополнительно
    • Б. Страуструп, Дизайн и эволюция языка C++
    • С. Майерс, Эффективное использование С++/Эффективное использование STL
    • Г. Сеттер, Решение сложных задач на C++/Новые сложные задачи на C++
    • Р. Седжвик, Алгоритмы на C++

Осенний семестр

Презентации первого семестра Евгения Линского (I поток):

Планы лекций Егора (II поток): github.com/yeputons/hse-2019-cpp

Презентации ликбеза по С:

Оценка за курс

Лабораторные и домашние

Практика 1: Соколов

Преподаватель: Вячеслав Соколов (vi.soksok@gmail.com, +7 921 780 11 12)

Trac: sokolov

Письма просьба идентифицировать префиксом в теме [HSE][CPP]

Linux

  • Исследование бинарных артефактов
    • nm list symbols from object files
    • readelf displays information about ELF files
    • c++filt demangle symbols
Ограничение процесса по ресурсам в Linux
  • Рекомендуется использовать systemd-run: systemd-run --scope -p MemoryLimit=2M -p MemoryAccounting=yes ./a.out. В этом примере ограничивается память 2 мебибайтами.

Дальнеший текст - для тех, кто хочет более глубокого погружения.

Краткий перечень ключевых слов и какие есть проблемы при использовании:

  • cgroups - можно просто взять и использовать, но придется повозиться, не очень удобно. Требует понимания, как что устроено, соответственно и времени потребует.
  • ulimit - много разных версий, от конкретного shell-а зависит как пример использования, так и доступная функциональность. Легко может не сработать.
  • Использование LD_PRELOAD и собственного аллокатора памяти. У меня аллокатор libmemrestrict.so по ссылке из статьи крашится с SEGFAULT, возможно, это я где-то напортачил. Если захотите идти этим путем - спрашивайте меня, есть несколько нетрививальных моментов.
  • docker - популярный способ работы с контейнерами

Для тех, кому хочется поэкспериментировать с консолью

  • Эмуляторы терминала
    • terminator
    • tilix
    • а также много других, легко ищутся
  • Shell
    • bash по умолчанию, но есть кое-что поудобнее, например
    • fish - interactive shell, создан, чтобы быть удобным
    • zsh - для любителей писать скрипты

Системы сборки

Для наших нужд достаточно Make

Если проект становится большим, нужно что-то посложнее, например (альтернативы):

Полезные сайты

  • godbolt - compiler explorer (удобно делиться примерами)
  • ideone - возможность делиться кодом, поддержка разных ЯП, исполнение кода online
  • stackoverflow - огромное количество ответов на разные вопросы. Прежде чем самому задавать вопрос, будет полезно ознакомиться с текстом https://habr.com/ru/post/339038/
  • [1] - список библиотек из 1-2 файлов
  • [2] - список open-source библиотек на языке Си++
  • [3] - история языка Си++

Статьи

Практика

16.01.20

23.01.20



30.01.20

06.02.20

20.02.20

27.02.20

05.03.20

12.03.20

Требования корректности, предъявляемые к работам

1. Проверка контрактов функций.

На мой взгляд, чуть ли не единственной обязательной к использованию парадигмой и доступной во всех языках программирования, выше ассемблера, является [4]. Ключевые понятия: предусловия, постусловия и инварианты. В языках C/C++ соблюдение контрактов контролируется с помощью assert, возведения сигналов, бросания исключений.

Данный подход является обязательным, потому что гарантирует максимально раннее обнаружение проблемы. Игнорирование подхода приводит к проблемам в самых неожиданных местах. Программа может вести себя как корректная, хотя содержит в себе большое количество ошибок, пока очередная проблема не приведет к лавине. Примеры неожиданно всплывших проблем: https://software-testing.ru/library/testing/general-testing/2082-horrible-bugs

Требуется производить проверку контрактов:

  • предусловий: с помощью assert в начале функции. Пример: Указатель: может быть нулевым? Нет? assert.
  • инвариантов: по ходу выполнения функции. Пример: по ходу алгоритма нужно произвести удаление элемента из контейнера. Можно проверить, что этот элемент вообще присутствует в контейнере.
  • постусловий: В конце функции и с помощью тестов. Пример: в результате удаления вершины из списка она не должна быть достижима из головы списка. Это можно провалидировать с помощью assert.

Проверки контрактов упрощают восприятие кода и могут при определенном стечении обстоятельств заменять документацию.

Q: но ведь проверка контракта может быть медленной, теряем производительность

A: используйте assert для медленных проверок, если нужна производительность - используется release режим сборки, туда эти проверки не попадут

2. Стиль кода совпадает во всем проекте.

Либо везде табуляции для отступов, либо везде пробелы. Именование переменных либо везде snake_case, либо везде camelCase. Желательно отделять разные сущности друг от друга написанием. Например, можно выделять:

  • МАКРОСЫ()
  • КОНСТАНТЫ
  • функцииКоторыеЧтоТоДелают()
  • переменныеКоторыеЗачемТоПонадобились
  • ИменаКлассовИСтруктур

Допустимы конфликты написания (когда по имени нельзя однозначно восстановить, какая это языковая конструкция), но не стоит для всего использовать одно и то же написание.

3. Каждая строчка кода что-то делает.

Если строчку кода можно удалить без изменения поведения программы, значит, ее нужно удалить.

4. Отсутствие утечек памяти, Undefined Behaviour и других проблем Ваша программа должна компилироваться и корректно завершаться, а соответствующая утилита не должна находить какие-либо проблемы:

  • в debug и release (-DNDEBUG) режимах сборки
  • будучи запущенной из-под gdb
  • будучи запущенной из-под valgrind
  • после компиляции с -fsanitize=address
  • после компиляции с -fsanitize=undefined
  • после компиляции с -fsanitize=leak
  • на разных версиях разных компиляторов (gcc, clang, msvc, ...)

включая комбинации этих опций, кроме случаев ошибок в самих утилитах и несоответствия компилятора Стандарту. (Да, и то, и то бывает.)

5. Именование должно быть понятным кому угодно, не только лишь Автору, но и всем. Имя функции должно отражать особенности ее поведения. Имя переменной должно быть говорящим. Лучше всего, если вне контекста можно понять, за что отвечает / что делает та или иная функция, структура, переменная. Стандарт языка в этом смысле не является примером для подражания.

Этот раздел не может быть формализовать и ложится целиком и полностью на здравый смысл. Здесь зачастую нет "идеального" решения, и если вам не удается достичь совершенства - не расстраивайтесь, таких как вы - легион. Проблема именования является одной из ключевых в программировании, вызывает большое количество дискуссий, требует много времени и внимания разработчиков.

6. Если была допущена какая-то проблема в реализации функции, должен быть написан тест, детектирующий эту проблему.

7. Использование сторонних функций только из стандартной библиотеки языка, если противное явно не оговорено в задании.

В частности, написание C++ кода без привязки к платформе Linux; отсутствие POSIX-специфичных функций и структур. Не смотря на то, что целевая платформа - Linux, привычка писать платформенно-специфичный или компиляторо-специфичный код может однажды сыграть злую шутку. Комитет по стандартизации стремится к тому, чтобы не было необходимости писать такой код, включая новые и новые платформенно-независимые вещи в стандарт языка. Если "совсем никак", то лучше обернуть специфичную функцию: сделать size_t mygetline(char **lineptr, size_t *n, FILE *stream) {return getline(lineptr, n, stream);}

8. Не использовать exit где-либо, кроме main.cpp Представьте, что вы пользуетесь какой-то библиотекой. Позвали функцию из нее, а ей что-то не понравилось (не хватило памяти, запись на диск не удалась, ...) и она позвала в своих недрах exit. Это полная катастрофа, потому что на этом работа программы завершается, а в месте вызова вы об этом даже не узнаете. Это может быть очень болезненно: может быть, в оперативной памяти хранится результат длительных вычислений, или ценная информация, это могли быть данные, за которые нужно платить (обращения к платным сервисам) и много что еще. Главное - за вас кто-то другой решил, как программа должна себя вести. Никогда не используйте exit в библиотеках. Во всех заданиях прослеживается структура: сделать некоторый набор действий в рамках некоторой модели. Модель находится в одном месте, управляющая логика - в другом. Здесь проходит условная черта: модель - это библиотека, управляющая логика - конечный пользовательский код. Обработка ошибок должна осуществляться с помощью кодов возврата либо исключений.

9. Пространства имен Не писать ничего в глобальное пространство имен в заголовочных файлах. И не использовать `using namespace` на неограниченный scope (например, в начале .cpp файла).

To be continued.

Практика 2: Свиридкин

Репозиторий с примерами https://github.com/Nekrolm/hse_cpp_examples

Практика 3: Лапшин

Практика 4: Гулецкий

Преподаватель: Артур Гулецкий (hatless.fox@gmail.com)

Префикс в теме письма: [C++TA]

Trac: huletski

Код с практик

Практика 4: Алфёров

Материалы с практик: