Ты всё перепутал или не дочитал/не осмыслил текст. Тут речь не о мощи, как о проблеме, а о хрупкости, как о проблеме. В современном языке программирования её не должно быть. Вот, ассемблерная программа не сломается вдруг так часто, как сломается сишная/плюсовая от простой перекомпиляции другим компилятором или новой версией того же. В больших проектах это очень важно, т.к. в них выше вероятность, что код окажется в каких-то местах достаточно хрупким для «поломки» компилятором. Хуже того, вещественная арифметика слабо специфицирована, и в определённых задачах ты будешь то и дело упираться в то, что компилятор решил иначе оптимизировать арифметику, что значит, что у тебя результаты будут плавать вместе с плавающей точкой, если ты не примешь экстренных мер для упразднения компиляторных вольностей. Язык таким образом становится всё более нишевым, т.к. вхождение в него становится всё более сложным и долгим, по сравнению с другими языками, плюс в нём продолжают накручивать абстракции, делая сам язык ещё большим, ещё более сложным, и ещё менее логичным (он и в виде голого C не отличался логичностью, а тут и того хуже). Я считаю, что это всё большие - минусы для современного языка программирования.
Ну раз ты писал, что *абстракциями* де, закрывают дыры, то единственной абстракцией, которая для закрывания дыр, может быть RAII, и то, это, скорее pattern. Что ты скажешь про RAII? Мне интересно услышать.
Остальные абстракции не для закрывания дыр. Они для программирования сложных вещей проще.
Скота Майерса обожаю. Это он меня посветил в RAII паттерн. И не только.
Остальные абстракции дыры действительно не очень закрывают. Они по большей части просто сосуществуют с дырами, но иногда их и расширяют (C++ имеет свои собственные UB, дополняющие пришлые из C). И хоть они и помогают местами что-то проще написать, они не помогают это потом проще прочитать. Здесь кроется второе большое зло языка.
У тебя всё видение C++ в контексте стабильности и "помехоустойчивости". Конечно, что угодно можно испортить неуёмным рвением. Но таки on average абстракции и читать код помогают, надо употреблять ёмкие имена для них, и хорошо продумывать/разделять интерфейсную часть с имплементацией. Последнее есть бОльшая проблема, так как индивидуальное понимание что есть хорошо ну очень разное. Потому очень к месту иметь стандарт для кодирования как у гуглей, скажем.
У меня на работе периодически выстреливают всякие глюки, связанные с UB, с несовместимостью с другими компиляторами (=чрезмерной или необоснованной привязкой к конкретном компилятору). И это не мой код, но мне с ним работать.
Продумывать абстракции и т.п. надо, но опять же, когда я их получаю в чужом коде в непродуманном или недокументированном виде, я оказываюсь в попе, т.к. сам по себе не очень логичный и не очень продуманный язык (ролик смотрел?) усложняет разгребание чужих говн, им дозволенных. Потом вдруг выясняется, что отладчик не переваривает прогу и сам падает (я об этом писал), и разбирать код становится ещё сложнее, т.к. нужно его или вычитывать в мельчайших подробностях, или инструментировать вдоль и поперёк.
Я не уверен, что гугловская бумажка помогает в достаточной степени. Скомпилируй Android, сливая весь вывод make'а в лог. Потом посчитай сколько там всяких компиляторных warning'ов в гуглокоде. Посмотри на то, какие штуки опциями компилятору запретили делать или предполагать в том же Chromium'е. Часть этих штук перечислена в доке, по ссылке в записи. От хорошей ли жизни это сделали?
Если ты живёшь в своём маленьком мире и не вынужден ковыряться в горах чужого кода, то можно говорить что угодно, петь любые дифирамбы языку и оправдывать любые дефекты как незначительные. Но как только проект становится большим, сложным и обзаводится немалым числом программистов весьма себе разнообразного уровня программирования в целом и владения конкретным языком в частном, все эти дефекты и проблемы становится трудно игнорировать.
В среднем по индустрии, как я понимаю, ООП скорее де факто мертво, чем живо. Мало кто пишет ООП так, как ты тут говоришь: с идеальным разделением, с хорошим API, так чтобы можно было только использовать foo.h и foo.lib и не лезть менять foo.cpp, из которого foo.lib сделан (а только читать хорошую доку с годными прмерами, делать #include "foo.h", наследовать и переопределять виртуальные методы, прилинковывать foo.lib и радоваться тому, что всё работает), с предвосхищением будущего, на года, да ещё чтоб лишних сущностей и кода не было. Огромная часть кода изначально была написана для здесь и сейчас и потом была многократно модифицирована для других здесь и сейчас без особых мыслей о структуре, архитектуре и т.п.: приляпали сбоку, фичу/проект сдали, побежали дальше.
В среднем по индустрии, как я понимаю, ООП скорее де факто мертво, чем живо.
Глядя в код некоторого Офиса, у меня такого впечатления не было. Там очень жёсткая дисциплина код-ревью. Проект гниёт оттуда, откуда мы думаем, вот потому у них "лепить с боку" не очень приветствуется. В Индустрии, если говорить о крупных компаниях с многолетними проектами ещё есть проблема платформенности/исторического наследия, когда меняется собственно видение продукта и его платформы, но надо делать так, чтобы новое сосуществовало со старым. *** Ах да, вот там ворнинги забивают! ***
А warnings в этом контексте вообще слегка за уши притянуты. Компилировал Android года три назад и Chromium сейчас. Но всегда интересовал какой-то модуль, а не статистика предупреждений, которые важны в контексте.
Зная, как пользователь, что некоторые баги в этом некотором офисе живут десятилетиями, а некоторые фичи портят документ случайным образом, зная, что там продублировали уже существующие элементы UI, и зная из первых уст, как некоторые баги были «исправлены», у меня не возникает иллюзий на счёт стройности того кода. Ещё я насмотрелся на ужосы в некоторой ОС, где важные куски были написаны абы как, и заведующие ими не могли ответить определённо на бинарный вопрос после недельного изучения собственного кода и собственной же документации, где в иных важных компонентах warning'и были или выключены вовсе или выставлены на средний уровень, т.е. многое находимое просто не находилось, и народ боялся их включать на полную и чистить код. И ведь у них там тоже есть guidelines! В warning'ах скрываются ошибки. Если код не компилируется чисто, то среди безобидных warning'ов пропускаются всамделишные ошибки, т.к. в эту простыню warning'ов просто никто не всматривается.
Ну, в общем, кроме прикладухи, но и движущие части прикладухи, почти всё сделано на C и C++, ужасных-ужасных языках. А тут proposal, как нам сделать из Си НеСи, а интертрепатор. Очень вовремя!
Первый пункт меня огорошил достаточно, я же спросил. Ты отнёс такое к интерпертаторам. Ешё продолжаю читать и вникать. Что не значит, что вовсе не читал. Просто не проникся.
Я там сказал, что мне трудно представить, что такое может сделать компилятор. Но таки может в некоторых ситуациях.
Весь текст по ссылке был явно не про интерпретатор, а про укрощение вредных вольностей компилятора. Как это можно было не понять - не знаю. Если только не читать.
A data race results in unspecified behavior. Informally, we expect that the result of a data race is the same as in C99: threads are compiled independently and then data races have a result that is dictated by the details of the underlying scheduler and memory system. Sequentially consistent behavior may not be assumed when data races occur
Это как дружественным компилятором отлавливать? Тут глубина мысли понятна, а как компилятор статически сделает заключение? Он будет анализировать все вовлечённые модули? Добавить в язык синхронизационный контекст какой-то, так выйдет, чего это они пишут, что просто сделать дружественным. Или специальный рантайм как-то помогает, или новые конструкции языка.
Или я ничего не понял тут, и они просто постулируют UB здесь?
Как я понимаю, они хотят ввести определение. В C99 ничего нет про races. Т.е. без определения даже и не понятно какое тут U: undefined или unspecified. Ну, т.е. пипец полный или в соотвествии с конкретной низлежащей реализацией. Хотят не полный, а в явном виде unspecified.
Да, я что-то стормозил. Простой пример по первому пункту:
bool DidSomething(void) { char* p = malloc(1); if (p) { // do something with *p free(p); } return p != NULL; }
Ты типа думаешь, что если память выделилась, всё было сделано и потом она освободилась, то можно использовать значение указателя послее free() как индикатор успеха.
Компилятор же может подумать, что если условие и тело if не выполняются, то нужно вернуть false ((NULL != NULL) == false). А если выполняются, то поскольку имеет место UB (использование указателя p после free(p)), можно не генерировать код для проверки p != NULL, а вместо этого так же всегда возвращать false. Упс.
Компилятор же может подумать...А если выполняются... то поскольку имеет место UB (использование указателя p после free(p))
Это какой компилатор такое может подумать? Гипотетический, принимая решение, как генерить код на основании вызова некоторой функции, которая, к тому же, одинарный указатель принимает, а не адрес от той указательной переменной? Ты как компиляторо-писатель хорошо понимаешь, что в переменной указательного типа p не так много дополнительных смыслов.
Да тот же gcc запросто может так подумать. Он знает, что это не какая-то там левая ф-ция, а ф-ция стандартной библиотеки с известным поведением. Т.е. ему не нужно даже пытаться смотреть на значение p. Если был вызов free(p), то согласно стандарту с p больше ничего делать нельзя, кроме как присваивать ему новое значение или брать евоный адрес, что даёт компилятору полное право не генерировать код согласно твоему исходнику.
На досуге скомпилируй в ассемблер hello-o-world программу с выражением printf("hw!\n") (опции -c -S у gcc). Загляни в асм. Там на самом деле будет вызов puts("hw!").
The Last Thing D Needs (Scott Meyers):
Reply
Остальные абстракции не для закрывания дыр. Они для программирования сложных вещей проще.
Скота Майерса обожаю. Это он меня посветил в RAII паттерн. И не только.
Reply
Остальные абстракции дыры действительно не очень закрывают. Они по большей части просто сосуществуют с дырами, но иногда их и расширяют (C++ имеет свои собственные UB, дополняющие пришлые из C). И хоть они и помогают местами что-то проще написать, они не помогают это потом проще прочитать. Здесь кроется второе большое зло языка.
Reply
Reply
У меня на работе периодически выстреливают всякие глюки, связанные с UB, с несовместимостью с другими компиляторами (=чрезмерной или необоснованной привязкой к конкретном компилятору). И это не мой код, но мне с ним работать.
Продумывать абстракции и т.п. надо, но опять же, когда я их получаю в чужом коде в непродуманном или недокументированном виде, я оказываюсь в попе, т.к. сам по себе не очень логичный и не очень продуманный язык (ролик смотрел?) усложняет разгребание чужих говн, им дозволенных. Потом вдруг выясняется, что отладчик не переваривает прогу и сам падает (я об этом писал), и разбирать код становится ещё сложнее, т.к. нужно его или вычитывать в мельчайших подробностях, или инструментировать вдоль и поперёк.
Я не уверен, что гугловская бумажка помогает в достаточной степени. Скомпилируй Android, сливая весь вывод make'а в лог. Потом посчитай сколько там всяких компиляторных warning'ов в гуглокоде. Посмотри на то, какие штуки опциями компилятору запретили делать или предполагать в том же Chromium'е. Часть этих штук перечислена в доке, по ссылке в записи. От хорошей ли жизни это сделали?
Если ты живёшь в своём маленьком мире и не вынужден ковыряться в горах чужого кода, то можно говорить что угодно, петь любые дифирамбы языку и оправдывать любые дефекты как незначительные. Но как только проект становится большим, сложным и обзаводится немалым числом программистов весьма себе разнообразного уровня программирования в целом и владения конкретным языком в частном, все эти дефекты и проблемы становится трудно игнорировать.
В среднем по индустрии, как я понимаю, ООП скорее де факто мертво, чем живо. Мало кто пишет ООП так, как ты тут говоришь: с идеальным разделением, с хорошим API, так чтобы можно было только использовать foo.h и foo.lib и не лезть менять foo.cpp, из которого foo.lib сделан (а только читать хорошую доку с годными прмерами, делать #include "foo.h", наследовать и переопределять виртуальные методы, прилинковывать foo.lib и радоваться тому, что всё работает), с предвосхищением будущего, на года, да ещё чтоб лишних сущностей и кода не было. Огромная часть кода изначально была написана для здесь и сейчас и потом была многократно модифицирована для других здесь и сейчас без особых мыслей о структуре, архитектуре и т.п.: приляпали сбоку, фичу/проект сдали, побежали дальше.
Reply
Глядя в код некоторого Офиса, у меня такого впечатления не было. Там очень жёсткая дисциплина код-ревью. Проект гниёт оттуда, откуда мы думаем, вот потому у них "лепить с боку" не очень приветствуется. В Индустрии, если говорить о крупных компаниях с многолетними проектами ещё есть проблема платформенности/исторического наследия, когда меняется собственно видение продукта и его платформы, но надо делать так, чтобы новое сосуществовало со старым. *** Ах да, вот там ворнинги забивают! ***
А warnings в этом контексте вообще слегка за уши притянуты. Компилировал Android года три назад и Chromium сейчас. Но всегда интересовал какой-то модуль, а не статистика предупреждений, которые важны в контексте.
Reply
Reply
Reply
Reply
Ешё продолжаю читать и вникать. Что не значит, что вовсе не читал. Просто не проникся.
Reply
Весь текст по ссылке был явно не про интерпретатор, а про укрощение вредных вольностей компилятора. Как это можно было не понять - не знаю. Если только не читать.
Reply
Это как дружественным компилятором отлавливать? Тут глубина мысли понятна, а как компилятор статически сделает заключение? Он будет анализировать все вовлечённые модули? Добавить в язык синхронизационный контекст какой-то, так выйдет, чего это они пишут, что просто сделать дружественным. Или специальный рантайм как-то помогает, или новые конструкции языка.
Или я ничего не понял тут, и они просто постулируют UB здесь?
Reply
Reply
bool DidSomething(void)
{
char* p = malloc(1);
if (p)
{
// do something with *p
free(p);
}
return p != NULL;
}
Ты типа думаешь, что если память выделилась, всё было сделано и потом она освободилась, то можно использовать значение указателя послее free() как индикатор успеха.
Компилятор же может подумать, что если условие и тело if не выполняются, то нужно вернуть false ((NULL != NULL) == false). А если выполняются, то поскольку имеет место UB (использование указателя p после free(p)), можно не генерировать код для проверки p != NULL, а вместо этого так же всегда возвращать false. Упс.
Reply
Это какой компилатор такое может подумать? Гипотетический, принимая решение, как генерить код на основании вызова некоторой функции, которая, к тому же, одинарный указатель принимает, а не адрес от той указательной переменной? Ты как компиляторо-писатель хорошо понимаешь, что в переменной указательного типа p не так много дополнительных смыслов.
Reply
На досуге скомпилируй в ассемблер hello-o-world программу с выражением printf("hw!\n") (опции -c -S у gcc). Загляни в асм. Там на самом деле будет вызов puts("hw!").
Reply
Leave a comment