Comments 95
C++ наше многое. Пошел отмечать.
Однако, 4.4 миллиона — это примерно треть населения Москвы. Много же людей пишут на C++!
Было бы интересно узнать, сколько приблизительно вообще профессиональных программистов в мире.
Было бы интересно узнать, сколько приблизительно вообще профессиональных программистов в мире.
Легендарный язык, на котором можно написать свою карманную вселенную и тут же ее уничтожить, параллельно уничтожив несколько параллельных реальностей.
Наверное самый холиварный язык в мире. И конечно же явления суть описана его автором:
П.С. Модули уже дайте а? Такой классный подарок будет.
Наверное самый холиварный язык в мире. И конечно же явления суть описана его автором:
«Есть всего два типа языков программирования: те, на которые люди всё время ругаются, и те, которые никто не использует.»
Bjarne Stroustrup.
П.С. Модули уже дайте а? Такой классный подарок будет.
Поздравляю коллег! Лично я ни разу не пожалел что выбрал C\C++ как основные языки разработки
Всегда воспринимал С++ как нечто что было всегда, неизменным. Просто никогда не задумывался об этой стороне… А он, оказывается, развивался, не всё сразу построилось так как сейчас воспринимается привычным.
Интересно, почему ООП считалось медленным? В случае С++ весь overhead — только виртуальные функции (+1 операция на lookup), да и никто ведь в здравом уме не будет делать 100% функций виртуальными. Или за-за оверхеда по памяти так считалось?
Интересно, почему ООП считалось медленным? В случае С++ весь overhead — только виртуальные функции (+1 операция на lookup), да и никто ведь в здравом уме не будет делать 100% функций виртуальными. Или за-за оверхеда по памяти так считалось?
Как я понимаю, это идёт из времён, «когда машины были большими, а программы маленькими», до эры «купите побольше оперативки и процессор поновее».
Когда бизнес систематически не платил за нефункциональные требования типа скорости софта и другие оптимизации, но платит за обновление вычислительного парка, сформировался эффект оптового производства железа, цена транзакции удешевилась за счёт быстрого железа, что нынче можно спрашивать «нужно ли волноваться из-за оверхеда».
Когда бизнес систематически не платил за нефункциональные требования типа скорости софта и другие оптимизации, но платит за обновление вычислительного парка, сформировался эффект оптового производства железа, цена транзакции удешевилась за счёт быстрого железа, что нынче можно спрашивать «нужно ли волноваться из-за оверхеда».
Да нет, оверхед здесь побольше. Таблицы VMT, само создание/уничтожение классов. Сейчас этот overhead относительно мал, но давайте посмотрим на ресурсы персоналок в начале 90х… Начиная от набора регистров и до объема памяти.
Были времена, когда доступ к памяти выполнялся за десятки тактов, да и сейчас не всегда за один.
Ну вот в числодробилках всяких типа моделирования гидродинамики ООП до сих пор считается медленным. Я видел проект, в котором все было сделано через шаблоны, не было ни одной виртуальной функции. Легко прикинуть. К примеру, у вас система из дискретной равномерной сетки, N*M*L узлов, на каждой итерации надо обработать каждый узел, всего I итераций. В простейшем случае состояние узла на следующей итерации зависит от состояния его ближайших соседей (и его самого) на предыдущей итерации, положим 27 штук соседей ( включая сам узел). Итого получается вложенный цикл из 27*N*M*L*I итераций. Каждое из этих чисел N,M,L,I легко может быть порядка 1000 или 10000 (и это еще очень скромно). Итого 10^13-10^17 итераций. Если у вас в цикле будут вызовы виртуальных функций, то, скорее всего, вы это заметите. Конечно, зависит от того, что еще считается в цикле. В алгоритме, который я пробовал и имею в виду, добавление модного слоя абстракции в этом вложенном цикле на некоторые сущности через динамический полиморфизм сразу стоило 20% времени выполнения.
Разве числодробилки не под GPU пишут?
А зачем ООП вообще нужно для моделирования узлов регулярной сетки? Тем более зачем нужны виртуальные функции?
О, это просто. Вот посмотрите, к примеру, на диаграмму классов уже упомянутого проекта с lattice-boltzmann method. Дело в возможности включать разную физику и численные методы. Можно обходить 15 ближайших узлов, можно 19, можно 27. Можно использовать разные разностные схемы. Можно моделировать однофазный поток, можно многофазный. Если многофазный, то с есть разные методы его моделирования (даже в рамках этого численного метода). Можно включать турбулентность, можно выключать. Можно использовать разные геометрии и граничные условия. В параллельном случае можно использовать разные топологии связей. И т.п. И так у вас набегает 2986 классов, хотя программа все еще, по сути, это вложенные четыре цикла с парой сложений и умножений внутри. К счастью, все комбинации классов обычно можно разрешать статически, поэтому можно обойтись статическим полиморфизмом.
И так у вас набегает 2986 классов, хотя программа все еще, по сути, это вложенные четыре цикла с парой сложений и умножений внутри
Похоже на оверинжениринг. Шаблонные функции с массивом флагов для настройки разных параметров алгоритма, как по мне, должно быть достаточно. Зачем ООП?
Ну как минимум у вас есть возможность подставлять в шаблонные параметры (класса или пусть и функции) не функцию за функцией, а группы функций (классы), что уменьшает число шаблонных параметров на порядок. Ну и в целом это метод борьбы со сложностью. Можно сгруппировать функции семантически; данные, интересные нескольким функциям, можно добавить в класс. Какие-то поля сделать приватными, чтоб никто их больше не видел и вы точно знали, что за пределами класса о них думать не надо. Делегировать что-то другому классу (конечно, можно делегировать просто разным функциям, но так у вас близкие функции уже сгруппированы, и понять делегирование намного проще). Или, к примеру, если у вас даже с классами шаблонных параметров функции или класса много, вы можете группировать какие-то из них в классы дальше. Приватные поля для кеширования сразу легко вводить и т.п. Короче, все, что пишут в книжках по ООП.
Бывает и такое!.. Подумал лучше — и понял, что порол какую-то полную чушь. Из-за глубоко укоренившейся привычки что ООП это иногда медленно, не смог принять для себя гипотезу об оптимальной виртуальности и сразу отбросил возможную объектную реализацию (именно из-за того, что помнил об оверхедах, а не из-за удобства проектирования… а ведь мне показалось что наоборот… беда).
Конечно, если бы виртуальные функции работали быстрее, объектная реализация была бы предпочтительнее. Не прав, приношу свои извинения.
P.S.: Попробовал прикинуть в коде как бы выглядела реализации без ООП — бр-р-р. Как вообще имитация полиморфизма может быть сделана без виртуальных функций? На свич-кейсах как-то не очень хорошо (вроде, по быстродействию это не будет давать весомого преимущества). Делать по гриду под каждый тип и выбирать на основе какой-то индексной таблицы — много памяти, да и тоже не совсем ясно как… Как такое делают на практике?
Конечно, если бы виртуальные функции работали быстрее, объектная реализация была бы предпочтительнее. Не прав, приношу свои извинения.
P.S.: Попробовал прикинуть в коде как бы выглядела реализации без ООП — бр-р-р. Как вообще имитация полиморфизма может быть сделана без виртуальных функций? На свич-кейсах как-то не очень хорошо (вроде, по быстродействию это не будет давать весомого преимущества). Делать по гриду под каждый тип и выбирать на основе какой-то индексной таблицы — много памяти, да и тоже не совсем ясно как… Как такое делают на практике?
Ну, в общем случае — никак. Ибо виртуальные функции — это динамический полиморфизм, а шаблоны — это статический полиморфизм. Однако, применительно к данной задаче нам динамический полиморфизм вроде как и не нужен. Есть один большой алгоритм и его нужно посчитать для заданной параметризации, которая не будет меняться в процессе работы.
Можно привести такой пример. В динамическом полиморфизме есть интерфейс, а в статическом — концепт. Любой класс, реализующий заданный набор методов удовлетворяет этому концепту. Класс, удовлетворяющий концепту, передаётся на этапе компиляции, в то время как объект, удовлетворяющий интерфейсу, передавался бы на этапе выполнения.
В динамическом полиморфизме можно сделать условие на конкретный тип предоставленного объекта через RTTI и построить логику в зависимости от этого. В статическом можно сделать характеристический (traits) класс, у которого будут свои специализации под разные классы, которые он будет характеризовать.
Входной параметр функции — тоже динамический полиморфизм, по сути. Ведь функция работает по-разному в зависимости от значения параметра, пусть это и не имеет отношения к ООП. Вместо параметра можно использовать скалярный аргумент шаблона. В целом, любое поведение, доступное динамически, можно смоделировать статически, только при условии, что вся необходимая информация есть на этапе компиляции. И хотя выглядеть в С++ это может тяжеловато, компилятору с этим будет работать намного проще, нежели чем с виртуальными функциями, ибо ему сразу доступна вся возможная информация.
Можно привести такой пример. В динамическом полиморфизме есть интерфейс, а в статическом — концепт. Любой класс, реализующий заданный набор методов удовлетворяет этому концепту. Класс, удовлетворяющий концепту, передаётся на этапе компиляции, в то время как объект, удовлетворяющий интерфейсу, передавался бы на этапе выполнения.
В динамическом полиморфизме можно сделать условие на конкретный тип предоставленного объекта через RTTI и построить логику в зависимости от этого. В статическом можно сделать характеристический (traits) класс, у которого будут свои специализации под разные классы, которые он будет характеризовать.
Входной параметр функции — тоже динамический полиморфизм, по сути. Ведь функция работает по-разному в зависимости от значения параметра, пусть это и не имеет отношения к ООП. Вместо параметра можно использовать скалярный аргумент шаблона. В целом, любое поведение, доступное динамически, можно смоделировать статически, только при условии, что вся необходимая информация есть на этапе компиляции. И хотя выглядеть в С++ это может тяжеловато, компилятору с этим будет работать намного проще, нежели чем с виртуальными функциями, ибо ему сразу доступна вся возможная информация.
Типобезопасность и исключения дают ощутимый overhead.
В случае С++ весь overhead — только виртуальные функции (+1 операция на lookup)* На этапе компиляции — это барьер для оптимизаций. Вы не можете встраивать через виртуальные функции
* На этапе выолпнения — проблемы с фетчингом кода. Я даже не знаю, сможет ли в таком случае отработать branch prediction, или будет полная останвка конвеера
Ну и да обычно это не важно
* На этапе компиляции — это барьер для оптимизаций. Вы не можете встраивать через виртуальные функции
Clang спокойно их девиртуализирует и инлайнит (там, где применимо).
Там, где применимо, виртуальные функции и не нужны
А минусовать-то зачем? Вы написали про барьер для оптимизаций, я написал, что этот барьер обходится.
Можете пример привести? А то вот что-то у меня не девиртулизуется даже в примитивном случае, казалось бы
http://postimg.org/image/rl0t1cayr/
С++ лучший язык для программирования компьютеров.
Не оч. удачная инфографика: сразу неочевидно, к какому из событий относится каждый из относительно больших фрагментов блеклого текста — расположенному над ним или сбоку от него, или это вообще «заметки на полях», к конкретным событиям не привязанные. И, да, было бы здорово видеть SVG- или HiDPI-вариант.
С праздником, любимый язык! Я тебя никогда не брошу и, если понадобиться, буду бороться за тебя до конца!
А что они из boost возьмут?
А мы, кстати, в честь 30-летия Cfront проверили. Публикация статьи запланирована на четверг.
А вот и сама статья: К тридцатилетию первого C++ компилятора: ищем ошибки в Cfront.
Всего в мире сейчас более 19 млн разработчиков,
источник: www.3dnews.ru/912876
Т.е. если верить оценке JetBrains в 4,4 миллиона С++-программистов, каждый четвертый программист в мире знает С++?
Как думаете, насколько адекватна эта оценка? Я думаю, все же тех, для кого этот язык основной, на порядок меньше.
Сам С++ изучаю с 14 лет (т.е. уже 12 лет), из них 4 года — работаю С++-разработчиком. И все еще думаю, что не знаю его =)
С нетерпением из новых стандартов жду модулей.
Насчет фразы про «большой шаг после С++98», подумалось
-С++98 — «мажорная» версия, первый стандарт, вроде как 1.0
-С++03 — апдейт, фиксы, вроде 1.1
-С++11 — долго-долго пилили, много-много фич, похоже на версию 2.0
-С++14 — быстро приняли, залатали недостатки 11-го, можно назвать 2.1
-С++17 — модули, сеть, filesystem, аспекты, что там еще получится — попахивает 3.0?
-С++98 — «мажорная» версия, первый стандарт, вроде как 1.0
-С++03 — апдейт, фиксы, вроде 1.1
-С++11 — долго-долго пилили, много-много фич, похоже на версию 2.0
-С++14 — быстро приняли, залатали недостатки 11-го, можно назвать 2.1
-С++17 — модули, сеть, filesystem, аспекты, что там еще получится — попахивает 3.0?
Нет,
-С++98 — «мажорная» версия, первый стандарт, вроде как 1.0
-С++03 — апдейт, фиксы, вроде 1.1
— а с выходом С++11 перешли на модель релизов Google Chrome
-С++98 — «мажорная» версия, первый стандарт, вроде как 1.0
-С++03 — апдейт, фиксы, вроде 1.1
— а с выходом С++11 перешли на модель релизов Google Chrome
Увы, тоже не совсем точное решение. Про автообновление стандарта везде и всюду каждые 6 недель пока можно только мечтать :D
Ну а если серьезно, Вы не считаете, что набор запланированных изменений в 14 был куда меньше? Просто мне кажется, обрадовались, что 14 релиз удалось «быстро выпустить», в срок, и теперь опять раскатали губу на много всего. Да даже 1 или две фичи из планируемого в 17 — будет в 10 раз круче 14 апдейта, как мне кажется (одни модули чего стоят!).
p.s. я знаю про декларируемую схему работы, на хабре даже картинку постили.
Ну а если серьезно, Вы не считаете, что набор запланированных изменений в 14 был куда меньше? Просто мне кажется, обрадовались, что 14 релиз удалось «быстро выпустить», в срок, и теперь опять раскатали губу на много всего. Да даже 1 или две фичи из планируемого в 17 — будет в 10 раз круче 14 апдейта, как мне кажется (одни модули чего стоят!).
p.s. я знаю про декларируемую схему работы, на хабре даже картинку постили.
По такому поводу хочется включить, всем известную, песню группы «Сектор Газа» :)
Вы посвятили С++ большую часть жизни...
Вопрос вы выделен.
Вопрос вы выделен.
Очень символично, что сегодня вышла первая версия компилятора D написанная на самом D.
Страуструп — легенда.
Книжку его прочитал от корки до корки.
На экзамене преподавателю объяснял разницу типов связей между классами и объектами.
И хотя сейчас на нем не пишу, но желаю долгих лет языку и его создателю.
Книжку его прочитал от корки до корки.
На экзамене преподавателю объяснял разницу типов связей между классами и объектами.
И хотя сейчас на нем не пишу, но желаю долгих лет языку и его создателю.
Жалко, что не было вопросов о его отношении к «убийцам», типа раста. Считает ли он, что они его вскоре заменят, или что плюсам нет конкуренкции, и все эти расты — игры в песочнице… Было бы интересно узнать мнение такого человека по подобному вопросу.
А на расте можно писать библиотеки для всех остальных языков?
Не увидел в обещаниях нормальных utf8- строк. До сих пор считается, что строки не нужны?
Такие UTF-8-строки — на самом деле набор байтов:
The type of a u8"..." string literal is const char[].А нужен бы стандартный способ работы с массивами нормализованных символов как неделимых логических сущностей — в частности со встроенной нормализацией (простым использованием UTF-32 это автоматически не решается, т. к. один символ может быть образован более чем одним code point).
Любые строки — набор байтов. Как их интрпретировать — дело библиотек и пользовательского кода
> А нужен бы стандартный способ работы с массивами нормализованных символов как неделимых логических сущностей
А зачем?
А зачем?
Ну вот зря минусуете. На rsdn, помнится, целый тред был, вопрос-то не праздный. Что делать-то с этим символом? Замена подстроки работает и на utf-8, считать кол-во букв смысла не имеет, замерять длину строки тоже надо не так. Заменять отдельно букву — зачем?
Мне до сих пор интересно, зачем всё-таки надо получать какой-то там энный символ.
А когда это действительно надо (пройтись по символам), тогда можно использовать отдельный алгоритм, но нет смысла хранить так строку по умолчанию.
Мне до сих пор интересно, зачем всё-таки надо получать какой-то там энный символ.
А когда это действительно надо (пройтись по символам), тогда можно использовать отдельный алгоритм, но нет смысла хранить так строку по умолчанию.
Ну именно __массив__ может и не нужен, но __нормализованных символов__ нужно для любой мало-мальской обработки текста (см. NLP)
Можно поподробнее, мне реально интересно. Нормализовать и бегать специальным итератором прямо по utf8 строке не решит проблему?
Если вы действительно не понимаете, то попробуйте представить, что произойдёт при попытке извлечения символьной подстроки, расположенной далеко от начала строки, из UTF-8-строки, представленной не как набор символов, а как набор байтов, и как это связано с быстродействием.
Я не понял задачу. Что значит «попытка извлечения символьной подстроки»?
Хм, что такое подстрока? Например, «мир» в строке «Здравствуй, мир».
А что значит «извлечь» её? Чтобы её найти, не надо делать ничего особенного, обычный поиск подстроки, в чём сложность?
А откуда берётся номер, с которого надо брать подстроку, и длина подстроки?
Просто если был какой-то поиск для этого, то проблемы опять не будет. А какие ещё могут быть ситуации?
Просто если был какой-то поиск для этого, то проблемы опять не будет. А какие ещё могут быть ситуации?
Работая с текстом, удобнее и проще работать с ним как с набором символов, а не байтов, и переход на соответствующий уровень абстракции (преобразовать байты в массив символов) в общем случае имеет смысл осуществить единожды, а не делать это (разбирать всю UTF-8-строку с самого начала) снова и снова при каждой операции.
Это актуально в частности при разборе кода, синтаксические единицы (например, имена переменных или даже функций) которого могут содержать символы вне ANSI-диапазона, а также при анализе текстов на естественных языках — например, для вычисления соотношения количества слов на разных языках, чтобы определить основной язык документа. Не говоря уж о задачах поиска типа «все слова длиной от 1 до 3 букв», «все слова в верхнем регистре» или «определить общее количество символов в тексте».
Безусловно, многие задачи можно относительно успешно решить на уровне байтов, просто это не всегда удобно и целесообразно в том числе с точки зрения быстродействия.
Это актуально в частности при разборе кода, синтаксические единицы (например, имена переменных или даже функций) которого могут содержать символы вне ANSI-диапазона, а также при анализе текстов на естественных языках — например, для вычисления соотношения количества слов на разных языках, чтобы определить основной язык документа. Не говоря уж о задачах поиска типа «все слова длиной от 1 до 3 букв», «все слова в верхнем регистре» или «определить общее количество символов в тексте».
Безусловно, многие задачи можно относительно успешно решить на уровне байтов, просто это не всегда удобно и целесообразно в том числе с точки зрения быстродействия.
ICU недостаточно для вас?
Если вы о codepoint'ах, то для этого можно взять u32string (basic_string<char32_t>). А если речь о буквах, то там всё сложнее, насколько я понимаю. Например, æ — один codepoint, но одна или две буквы в зависимости от языка.
См. мой первый комментарий, а также увлекательную хабраисторию о букве «й».
Я это знаю, как это соотносится с моим комментарием?
Разбор кода, по-моему, везде орудует именно codepoint'ами без нормализаций (ну где надо, чтоб переменная «май» с разными «й» определялась одинаковой?). Даже там, где надо сравнивать «май» с разными «й» можно часто обойтись нормализацией, и работа всё равно идёт на уровне codepoint'ов.
Места, где надо уметь понимать, что «mæ» — это 2 или 3 буквы с зависимости от языка, по-моему, всё же на так часты.
Кстати, есть ли буквы, которые в принципе не умещаются в один codepoint (после нормализации)?
Разбор кода, по-моему, везде орудует именно codepoint'ами без нормализаций (ну где надо, чтоб переменная «май» с разными «й» определялась одинаковой?). Даже там, где надо сравнивать «май» с разными «й» можно часто обойтись нормализацией, и работа всё равно идёт на уровне codepoint'ов.
Места, где надо уметь понимать, что «mæ» — это 2 или 3 буквы с зависимости от языка, по-моему, всё же на так часты.
Кстати, есть ли буквы, которые в принципе не умещаются в один codepoint (после нормализации)?
Возможно, вы путаете самостоятельный символ со шрифтовой лигатурой (например, для последовательности букв «fi» в шрифте PT Serif Caption предусмотрен отдельный графический знак, но буквы всё равно две, и каждую можно выделить по отдельности). У строки нет такого атрибута, как язык. Например, в JavaScript свойство
В C++ хотелось бы такой же абстрагированной работы со строками, как в JavaScript, на уровне стандартной библиотеки языка. Не критично, не обязательно, не проблема, но было бы неплохо. ;-)
C++ вообще несколько удивляет отсутствием некоторых общеупотребительных функций, привычных по другим, более высокоуровневым, языкам — например,
length
строки æ
будет равно 1
, а "æ".substr(0, 1)
вернёт символ æ
, а не его половину. ;-)В C++ хотелось бы такой же абстрагированной работы со строками, как в JavaScript, на уровне стандартной библиотеки языка. Не критично, не обязательно, не проблема, но было бы неплохо. ;-)
C++ вообще несколько удивляет отсутствием некоторых общеупотребительных функций, привычных по другим, более высокоуровневым, языкам — например,
trim()
, split()
, join()
, в результате чего, чтобы просто начать писать программы, требуется сначала реализовать собственную библиотеку таких функций или использовать стороннюю библиотеку. Опять же, не критично, но странновато.Например, в JavaScript свойство length строки æ будет равно 1, а "æ".substr(0, 1) вернёт символ æ, а не его половину. ;-)
Правильно, потому что он орудует код-пойнтами, а не буквами. В Си++ будет так же (если взять u32string)
Не путаю, æ — лигатура в английском языке, но буква в датском. Поэтому когда мы говорим о «получить все слова длиной 3 буквы» встаёт во весь рост вопрос о том, о каком языке мы говорим.
C++ вообще несколько удивляет отсутствием некоторых общеупотребительных функций
Это да, есть такое.
Похоже, вы склонны игнорировать некоторые части текста, на который отвечаете. %)
Например?
У строки нет такого атрибута, как язык.
Вот именно, но это же вы просили
И тут непонятно, почему эта задача как-то связана с обсуждением стандартных строк, если даже идеальная на ваш взгляд строка такую задачу решить не позволит, так как язык туда положить нельзя, а без него задачу не решить.
Т.е. когда мы начинаем считать не кодпойнты, а логические буквы, нам надо заранее что-то куда-то преобразовать, зная при этом язык, а потом работать уже с кодпойнтами, но с кодпойнтами-то работать в C++ можно. Вот то, что стандартная библиотека бедна на всякого рода нормализации и trim/join — это да.
Не говоря уж о задачах поиска типа «все слова длиной от 1 до 3 букв», «все слова в верхнем регистре» или «определить общее количество символов в тексте».
И тут непонятно, почему эта задача как-то связана с обсуждением стандартных строк, если даже идеальная на ваш взгляд строка такую задачу решить не позволит, так как язык туда положить нельзя, а без него задачу не решить.
Т.е. когда мы начинаем считать не кодпойнты, а логические буквы, нам надо заранее что-то куда-то преобразовать, зная при этом язык, а потом работать уже с кодпойнтами, но с кодпойнтами-то работать в C++ можно. Вот то, что стандартная библиотека бедна на всякого рода нормализации и trim/join — это да.
Предлагаю остановиться на том, что было бы неплохо иметь в C++ способ работы хотя бы с codepoint’ами как самостоятельными сущностями без необходимости раздувания байтового объёма строк в несколько раз путём использования кодировки UTF-32 и работы с последовательностями по 4 байта вместо реальных codepoint’ов. Или, возможно, мне что-то неизвестно о работе с UTF-32 в C++.
В том, что придется пройти от начала строки. Никакого Боера-Мура, например. Но то же самое и в utf-16, только там переменной длины цепочки из двух-, а не однобайтных элементов.
Sign up to leave a comment.
30 лет С++