[CppCon 2017] Бьёрн Страуструп: Изучение и преподавание современного C++
Сейчас проходит конференция CppCon 2017, и на их youtube-канале уже стали появляться видео оттуда. И я подумал, почему бы не попробовать сделать конспекты интересных лекций. Конечно, не очень уверен, надолго ли меня хватит, зависит от того насколько вам это понравится.
Это первое вступительное видео. Оно не такое интересное для меня, но пропустить тоже не мог, это же Страуструп. Далее, текст от его лица. Заголовки взяты из слайдов.
Disclaimer: весь дальнейший текст — достаточно краткий пересказ, являющийся результом работы моего восприятия, и то, что я посчитал «водой» и проигнорировал, могло оказаться важным для вас. Иногда выступление было таким:»(важная мысль 1)(минута воды)(важная мысль 2)». Эти две мысли плавно перетекали друг в друга, а у меня получались довольно резкие скачки. Где можно сгладил, но посчитал нецелесообразным полностью причесывать текст, на это бы потребовалось много времени.
Вступление
Когда меня попросили выступить на открытии конференции, я задумался, о чем же я могу рассказать такого, что важно для вас, и чего вы не слышали миллион раз. И я решил рассказать про обучение языку C++.
Мы все учителя и мы все ученики
Зададимся вопросом кого мы учим, чему, зачем и как. Нужно делать это лучше. Я не критикую кого-то в частности, но чувствую, что мы должны делать это лучше. Не все из нас преподаватели, но тем не менее постоянно возникают случаи, когда мы занимаемся обучением. Например, рассказываем коллегам о последних фичах или даем советы. Общаемся на StackOverflow, Reddit, ведем блоги и т.д. Но нужно давать хорошие советы. Советы, которые двигают мир вперед.
Есть одна вещь, которая сильно беспокоит меня — зачастую у людей бывают очень странные представления о том, что собой представляет C++. Чуть позже я вернусь к этой проблеме.
Когда учите, задумайтесь, чего вы хотите достичь. И от от этого и начинайте. Не отталкивайтесь от «что мы уже сделали» и «что проще, чтобы начать», а если вы преподаватель, то от «что проще проверить».
Обучение программированию
Не нужно фокусироваться на языковых фичах. Например, вы встречали примеры в которых объясняется проблема приведения signed short
к unsigned int
[рассказывается о преподавании языка в общем, а не об особенностях C++]. Это неинтересно и можно увидеть в отладчике или прочитать в руководстве. Учите так, чтобы такая проблема не появлялась.
Не пытайтесь учить всему, вы не сможете. Внимательно выберите подмножество языка.
Одна из встечающихся проблем обучения C++ — то что язык изучается сам по себе, отдельно от библиотек. Вектор на 697 странице, sort через 100 страниц. Это учит, что stl скучная, сложная фигня. И в то же время: свой Linked List или Hash table это круто, круче чем stl.
Не будьте слишком умными
[в выступлении автор использует слово clever с негативным оттенком, что-то вроде человека, который пытается казаться быть умным]
Люди которые хотят и требуют «самое последнее» часто не знают основ. Пересмотрите основы.
Будьте проще. Не бросайтесь в самое сложное и изощренное. Не используйте самый продвинутый алгоритм, который только можно найти. Я бы не выбрал пузырьковую сортировку, но также не выбрал бы и «полный общий алгоритм для всего». Предлагайте самый простой пример, который иллюстрирует технику или фичу.
Фокусируйтесь на общих случаях. Будьте рациональными. Не говорите ученикам «Делай только так, это правильно, это закаляет характер. И можете получить пятерку, если ответите именно так». Нужно объяснить, почему нужно следовать правилам, дать ученикам хорошие идеалы, идеи, техники.
Конечно, обучая, очень заманчиво, стоять перед коллегами, группой людей и всем своим видом показывать: «Смотрите, эта сложная вещь, которую вы не поняли. Это означает, что я умный». Это не очень хорошее обучение.
Обучение программированию
Если изучать только сам язык, то попав в реальность можно просто «утонуть».
Используйте различные инструменты. Не только компилятор и учебник, но и IDE, отладчики, системы контроля версий, профилировщики, модульное тестирорвание, статические анализаторы, онлайн компиляторы. Интрументы должны быть современными (иногда получаю вопросы по Turbo C++ 4.0:()
Нужно изучать принципы и закреплять на практике. Используйте графику, сети, интернет, Raspberry Pi, робототехнику и т.д. Это очевидно для вас, но не очевидно для университетов. Не говорите что это просто и быстро. И помните, что никто не умеет делать все.
Язык это не только синтаксис
Как мы часто учим? Объясняем язык плюс немного стандартную библиотеку. Без всякой графики, пользовательского интерфейса, веба, электронной почты, баз данных… И многие ученики считают, что C++ скучный бесполезный язык. Но это же не так, ведь такие вещи как браузеры, СУБД, САПР и прочие пишутся на C++. Перед началом лекции потратьте 5 минут о практическом применении.
Мы должны сделать лучше
Нам, сообществу C++, очень важно упростить начало работы, возможность пользоваться «прямо сейчас».
Как программирование похоже на занятие фотографией?
Как пользователи в различных отраслях разделяются на группы? Приведем пример с фотографией. Результат зависит от оборудования и от пользователя. Лично я новичок в фотографии. Большинство возможностей профессиональной фотокамеры будут для меня бесполезными. Она много весит, дорого стоит. Для нее существует множество аксессуаров в которых можно утонуть. Но с ее помощью можно делать превосходные фотографии, если потратить много времени на обучение. Аналогично существует много людей, которые не могут использовать разнообразные фичи языка и библиотеки.
Массовый рынок
С другой стороны, у нас есть устройства, которыми можно пользоваться сразу. Такое устройство дешевое, простое, «милое». Прощает ошибки, не требует много усилий для освоения. Является «вещью в себе». Мало расширений и дополнений, если таковые вообще есть. Отсуствуют взаимозаменяемые части.
Как-то во время преподавания мне было нужно, чтобы у студентов была установлена библиотека GUI. Оказалось, что установить одну и ту же библиотеку на студенческие Mac, Linux, Windows, весьма болезненно.
Языку нужна «система»
Каждый произвожитель фототехнии предлагает «систему», которая предполагает, что вы можете постепенно обновлять оборудование и переходить на следующий уровень по мере обучения.
Не обязательно давать новичку профессиональную камеру со всеми наворотами. В этом случае у него будут трудности и результат вероятно будет хуже, чем если бы он использовал «мыльницу». Поэтому какое-то одно решение не будет подходящим для всех.
Какими должны быть основные дистрибутивы?
Язык должен быть представлен тремя дистрибутивами. Для новичков, любителей и профессионалов.
Модули помогут
База:
import bundle.entry_level; //Для новичков
import bundle.enthusiast_level; //Для продвинутых
import bundle.professional_level; //Для профессионалов
Расширения (которые не входят в базу):
import grahics.2d;
import grahics.3d;
import professional.graphics.3d;
import physlib.linear_algebra;
import boost.XML;
import 3rd_party.image_filtering;
Нужны хорошие пакетные менеджеры и системы сборки
Как ученик на вторую неделю после начала обучения может установить библиотеку графического интерфейса и работы с базами данных? Различные библиотеки и системы собираются по разному. Различные библиотеки могут быть плохо совместимыми. Десяток несовметимых пакетных менеджеров — это не решение. Нужно сделать простым выполнение простых задач
> download gui_xyz
> install gui_xyz
Или эквивалетным способом, например в IDE:
import gui_xyz; //в коде
Современный C++
Мое видение современного C++ (как обычно):
- Статическая типобезопасность, хорошо определенные интерфейсы.
- Безопасность ресурсов (конструкторы/деструкторы, RAII).
- Абстракции без накладных расходов.
- Инкапсуляция, инварианты.
- Обобщенное программирование, шаблоны.
- Простота для большинства разработчиков, сложность скрыта в библиотеках.
Меняться трудно
Современный C++ это не C, Java, C++98 и не тот язык, на которым вы программировали 10 лет назад. Инерция — враг хорошего кода. Преподаватели, оправдывая неиспользование современных стандартов, говорят, что «мы так не делаем», «это не вставить в мою учебную программу», «может быть через 5 лет». У студентов появляется большее доверие к интернету, чем к преподавателям. Некоторые считают, что они умнее преподавателей, и иногда они правы. У меня стабильно каждый год на курсе были студенты, абсолютно убежденные, что они умнее меня в программировании. В этих частных случаях, я обоснованно уверен, что оне не правы [смех в зале].
Что такое современный С++?
- Лучшие практики, использующие текущий стандарт ISO С++
- Стремление к типо- и ресурснобезопасному коду
Для реализации этого 2 года назад был открыт проект C++ Core Guidelines. Он дает конкретные ответы на вопросы. У него много много участников, включая Microsoft и Red Hat.
Примеры кода
Не отделяйте примеры от объяснения. 5 страниц голой теории это лишняя трата. Давайте примеры и объяснения к ним. Без объяснения люди не обобщают. Они просто копипастят и сами изобретают трактовку, причем иногда очень странную.
Улучшение кода
Всегда объясняйте причины. Например:
//1
int max = v.size();
for(int i = 0; i < max; ++i)
//2
for (auto x : v)
Почему 2 лучше чем 1? Пример 2 явно показывает намерение, v может быть изменен без переписывания цикла, и менее подвержен ошибкам. Следует заметить, что 1 предоставляет более гибкие возможности. Но ведь goto еще более универсален, и поэтому мы избегаем его.
- I.4: Делайте интерфейсы точными и строготипизированными
[I.4 означает пункт из Core Guidelines]
void blink_led1(int time_to_blink) //Плохо - неясный тип
void blink_led2(milliseconds time_to_blink) //Хорошо
void use()
{
blink_led2(1500); //Ошибка: какая единица измерения?
blink_led2(1500ms);
blink_led2(1500s); //Ошибка: неверная единица измерения
}
[Здесь milliseconds какой-то простой тип не из библиотеки Chrono, поэтому последняя строчка приводит к ошибке. Ниже по тексту описано обобщение типа для единицы измерения, взятого из Chrono. Если интересно, можете почитать мое описание этой библиотеки]
template
void blink_led(duration time_to_blink)
{
auto ms_to_blink = duration_cast(time_to_blink);
}
void use()
{
blink_led(2s);
blink_led(1500ms);
}
- ES.20: Всегда инициализируйте объект
- F.21: Для возврата нескольких значений из функций предпочитайте использовать tuple, структуру (или structured binding).
Error_code err; //неинициализировано: потенциальная проблема
//...
Channel ch = Channel::open(s, &err); //out-параметр: потенциальная проблема
if(err) { ... }
Лучше:
auto [ch, err] = Channel::open(s) //structured binding
if(err) ...
А должен ли этот код использовать возврат двух параметров?
- E.1: Прорабатывайте стратегию отлова ошибок в начале разработки
- E.2: Бросайте исключение для уведомления того, что функция не может выполнить задачу
- E.3: Используйте исключения только для уведомления об ошибках
auto ch = Channel::open(s);
Лучше? Да, если неуспешное открытие было предусмотрено в программе.
Улучшение кода: не будьте слишком умными
Слово «умный» в контексте использования C++ — ругательное. Найдите баг:
istream& init_io()
{
if(argc > 1)
return *new istream { argv[1], "r" };
else
return cin;
}
- P.8: Не допускайте утечки
- R.11: Избегайте прямого вызова new и delete
- R.4: Raw-ссылка (T&) должны быть невладеющей
Комментирование
- P.1: Выражайте идеи прямо в коде
- NL.1: Не говорите в комментариях, что и так ясно видно в коде
- NL.2: Выражайте намерения в комментариях
- NL.3: Поддерживайте комментарии в актуальном состоянии
//Плохо
auto x = m * v1 + vv //Перемножение m с v1 и прибавление vv
//Хорошо
void stable_sort(Sortable& c)
//cортирует "c" согласно порядку, задаваемым "<"
//сохраняет исходный порядок равных элементов (определяемыми "==")
{
//...несколько строк нетривиального кода
}
Philosophy rules
Я рекомендую вам отправиться на github и почитать раздел Philosophy rules, содержащий основные концепции.
Core guidelines
Моя цель очень проста. Мы можем писать типо- и ресурсобезопасный код без утечек, повреждения памяти, сборщика мусора, ограничений в выразительности, ухудшения производительности.
Сейчас разрабатываются 2 открытых проекта: анализатор, для проверки провил Core Guidelines, и библиотека GSL — guidelines support library (реализация от Microsoft).
Вопросы? Комментарии?
Нет, я расказал далеко не все про обучение. Лишь едва царапнул поверхность.
Вопросы из зала
[У Страуструпа есть сверхспособность отвечать по 5 минут на простые вопросы, поэтому я очень сильно сократил его ответы да и сами вопросы тоже]
Core Guidelines слишком всеобъемлющие. как учить?
Не нужно читать всё. Прочитать введение, затем раздел с философией. Не нужно искать правило, правило само найдет вас.
Нужны ли стандартной библиотеке нужны простые функции? Например random [я полагаю, что имеется в виду функция без необходимости установки начального значения и возможностью задания закона распределения]?
Да, нужны.
Вы говорили про 3 дистрибутива C++. Кто должен этим заниматься?
Вряд этим будет заниматься комитет, поэтому, я думаю, это нужно делать силами сообщества. Это будет проще с развитием единого пакетного менеджера и модулей
Моя дочь учится в колледже и мы вместе делали проект термостата. Так вот, для того, чтобы получить температуру и отобразить на экране, потребовался целый семестр изучения C++. Что вы думаете по этому поводу?
Да, есть такая проблема. С модулями будет лучше.
Нужно ли преподавать программирование как общий предмет, так же как математику
Я не компетентен в этом вопросе.
mmatrosov: вы говорили о том, что в обучении нужно пользоваться билиотеками. Не будет ли такого, что новое поколение программистов не будет знать основ?
Зависит от цели. Я учу студентов как реализовать вектор, они должны знать об указателях, но не каждому нужно реализовывать lock-free код.