Как стать автором
Обновить
86
0.5
Евгений Охотников @eao197

Велосипедостроитель, программист-камикадзе

Отправить сообщение

В микроконтроллерах конечно.

Прекрасно. Только к контексту разговора имеет опосредованное отношение.

Чтобы было лучше понятно, такая аналогия:

В конце 1990-х JavaScript был мейнстримом в нише Web-страничек. Но на всю разработку софта (вне зависимости от прикладных ниш) влияния не оказывал. Прошло двадцать лет и JavaScript мейнстрим из мейнстримов вне зависимости от ниши.

Может быть Rust стал мейнстримом в разработке для микроконтролеров. Прекрасно, если так.

Но вот вообще в разработке софта вообще разговоров о Rust гораздо больше, чем самого Rust-а. Не смотря на то, что релиз версии 1.0 состоялся девять лет назад.

Путь к настоящему мейнстриму у тех же C++, Java, C# и Go был гораздо короче.

Единственное исключение, которое приходит на ум

Вроде бы std::launder требовал поддержки на уровне компилятора.
И, возможно, std::start_lifetime_as, т.к. это именно что подсказка компилятору.

По вашим критериям так ни C#, ни JavaScript до мейнстрима так и не добрались.

Первая стабильная версия Golang вышла в 2009, но никто на нём всерьёз не писал до примерно 2015г.

В 2009-ом Гугл просто предъявил Golang миру, релиз версии 1.0 у Golang-а состоялся в марте 2012-го.
Собственно, про Rust так же было известно задолго до релиза версии 1.0 в мае 2015-го.

Если верить Wikipedia, то Docker появился в 2013-ом, а в 2014-ом там заменили LXC на написанный на Go libcontainer:

Docker debuted to the public in Santa Clara at PyCon in 2013.[48] It was released as open-source in March 2013.[21] At the time, it used LXC as its default execution environment. One year later, with the release of version 0.9, Docker replaced LXC with its own component, libcontainer, which was written in the Go programming language.

Так что с "никто" и "всерьёз не" получается как с совой на глобус.

Ну и добавьте к дате релиза Go 1.0 девять лет и оцените насколько сильно он проник в мейнстрим к тому времени. И сравните с проникновением в этот же мейнстрим Rust-а на данный момент. Как раз 9 лет с релиза версии 1.0.

Раз пошла такая пьянка про подавляющее большинство программистов, то это самое подавляющее большинство не в состоянии писать на C++ безбажный код.

Исходя из моего (пусть и небольшого) опыта программирования на VisualBasic, Java и Ruby, это утверждение истинно не только для C++.

Я сетую на высокие накладные расходы не "C++ного кода вообще", а кода, написанного на C++ без достаточного понимания работы вычислительной системы.

Я просто приведу здесь вашу же цитату:

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

Если "либо отказаться от многих новых возможностей" не относится к "C++ному коду вообще", то мне остается только попросить вас более четко излагать ваши мысли, чтобы кривотолков не возникало.

А что они читают, на каких материалах учатся?

А это не важно. Поинт в том, что нельзя ориентироваться по тому, что пишут в Интернете. Во-первых, потому, что пишут в Интернете далеко не все, а только небольшой процент от причастных к профессии. Во-вторых, потому, что круг нашего чтения складывается под наши собственные предпочтения. Так, если отталкиваться от моего круга чтения, то чуть ли не каждый второй может написать Boost.Hana или Boost.Spirit.

Дались Вам эти платформы.

Так это же вы сетуете на высокие накладные расходы C++ного кода. Значит вам этого не хватает и вам приходится опускаться сильно ниже, что подразумевает хорошее знание конкретной платформы. Отсюда и сомнение в том, что некий C++ный разработчик кроме своей прикладной области еще и будет сильным экспертом в нескольких аппаратных платформах. Т.е. такие люди определенно есть, но их совсем немного.

И ради бога, если эта разница не ощущается пользователем непосредственно.

Боюсь без конкретики все разговор опять зайдет про сферических коней в вакууме.

Я вам пример привел: работа с JSON. В которой шаблонов очень много. Но мало кого парят накладные расходы, связанные с использованием шаблонов в этой области.

В том-то и дело, что примеры искусственные. И, что важно, нужно сравнивать объем и качество кода с шаблонами и объем и качество кода без оных на решении одних и тех же задач.

Вот уже упомянутый выше RapidJSON. Весь на шаблонах. Можно попробовать сделать его конкурента без шаблонов и посмотреть во что все это выльется.

Да, работаю в одиночку, но разные сообщества читаю регулярно.

Огромное количество разработчиков вообще не читает ресурсов типа Habr или reddit. А из тех, кто читает, очень небольшой процент ничего не пишет (ни статей, ни комментариев).

Не вижу связи.

Связь в том, что C++ позволяет писать переносимый высокоуровневый код, который будет работать настолько эффективно, насколько смогли сделать разработчики компилятора под эту платформу. И, если брать в рассмотрение сразу несколько платформ, то не так уж много шансов, что конкретный прикладной программист будет настолько же хорошо знать ассемблер x86 и условного ARM-а, как это знают компиляторостроители. Огромному количеству C++ников это просто не нужно.

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

Или о том, что был выбран неэффективный алгоритм.

Я вполне понимаю и даже уважаю позицию "мы знаем, что такой стиль приводит к значительным накладным расходам, но считаем их оправданными потому, что ...". Но чаще люди просто не понимают, насколько их код избыточен - ни по его виду, ни по функциональности.

Боюсь, здесь разговор пошел о каких-то сферических конях в вакууме.

Например, может быть приложение, у которого ресурсоемкая часть составляет 20% кода. А остальное -- это интерфейс с пользователем, чтение входных данных, запись результатов, контроль форматов и т.д., и т.п.

Та же самая работа с JSON может выглядеть очень по разному. Можно взять шустрый RapidJSON (который весь на шаблонах) и написать 100500 строк кода вокруг него, т.к. RapidJSON дает ну очень низкоуровневый интерфейс. Можно взять более тормознутый nlohmann::json, с которым будет написано всего 1500 строк кода. Как раз за счет шаблонов. И разница по производительности в несколько раз не будет играть никакой роли, если работа с JSON-ом не лежит на критическом пути.

Но тут встает вопрос - не слишком ли много на себе берет компилятор?

На этот вопрос есть разные точки зрения. В адрес компиляторостроителей уже грубо высказываются в том плане, что просто выбрасывать куски кода не есть хорошо. Но пока имеем то, что имеем

Это C++, таков путь.

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

Начиная с C++20 компилятор сам уже достаточно умен, чтобы понимать в некоторых случаях, что пользователь неявно начинает время жизни для объекта. Ссылка на пропозал была выше. Начиная с C++23 для этих целей завезли start_lifetime_as.

На самом деле (жирное ИМХО) это был косяк комитета: эту практику, сколь уж она считалась UB, давным-давно нужно было узаконить. Но руки дошли только в рамках работ над C++20.

До этого многие (включая меня самого) даже не знали, что такое поведение является UB.

В чем причина UB?

Насколько я понимаю, причина в том, что компилятор C++ должен уметь отслеживать начало и завершение времени жизни объекта. Например:

some_struct s1; // Здесь очевидное начало lifetime для объекта.
some_struct * p1 = new some_struct{}; // Здесь еще одно очевидное начало lifetime.
some_struct * p2 = new(&s1) some_struct{}; // Здесь очевидное начало жизни для p2.

В случае, когда мы тупо делаем reinterpret_cast или C-style case, время жизни ни для какого объекта не начинается.

Ну, блин. Это не UB. Это просто явный косяк.

Вы не за то зацепились. Проблема не в значении 53. Проблема в приведении типа. В чистом Си вот это не UB:

some_struct * p = (some_struct *)(some_ptr + some_offset);

тогда как в C++ этот же код:

some_struct * p = (some_struct *)(some_ptr + some_offset);

будет иметь UB. Этот UB не эксплуатировался компиляторами до C++20 (скорее даже до С++23), тогда как начиная с C++23 никаких гарантий по этому поводу уже нет. Не поставил std::start_lifetime_as, ну значит ССЗБ.

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

Оптимизатор не может стать причиной UB. Оптимизатор может воспользоваться оставленным пользователем UB. Например, в C++ (если я не запутался в нововведениях), вы не можете просто так написать код вроде:

char data[1024];
read_data(data);
some_struct * payload = reinterpret_cast<some_struct *>(data + 53 /*пропустили заголовок*/);

В чистом Си можете, а в C++ начиная с C++23 для этих целей следует применять std::start_lifetime_as.

Потому что порядок следования байт и выравнивание мы устанавливаем руками одинаковое с обеих сторон.

У "выравнивания" есть две составляющие в данном вопросе:

Первое: упаковка данных внутри буфера. Так, если у вас структура вида:

struct header {
  uint32_t _magic_number;
  uint8_t _version;
  uint16_t _fields;
  uint64_t _flags;
};

то без #pragma pack для вашей структуры в зависимости от настроек компилятора между _version и _fields может быть "дыра".

Второе: выравнивание адресов для вашей структуры на принимающей стороне. Тот самый alignas из современного C++. Без оного фокус вида:

char data[sizeof(header)];
read_data(data);
header * h = (header *)data;
if(0xDEADBEAF == h->_magic_number) // OOPS!

может аварийно завершится на некоторых платформах в точке "OOPS".

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

В компиляторах, конечно же, встречаются ошибки. Но, как правило, если программа в релизе с оптимизациями не работает, то с 99% вероятностью это проблема кода пользователя, а не качества компилятора.

Вот поэтому критичные по скорости куски кода писали на С.

Если вы думаете, что в чистом Си нет UB, то вам нужно прочитать хотя бы вот эту серию статей: https://habr.com/ru/articles/341048/ (это первая, в ней ссылки на остальные).

Если очередная версия компилятора превращает работающий код в ХЗ что, то нужен ли такой компилятор?

Это перпендикулярный вопрос. Пока же мы живем в реальности, в которой в стандарте C++ определен ряд UB и компиляторам разрешено эти UB эксплуатировать. Что и происходит и, временами, ведет с потере работоспособности кода и последующим бурлением говн в этих наших Интернетиках.

ЕМНИП, одним из UB, в частности, было то, что до C++20 вот такой вот код:

alignas(DataHeader) char buffer[sizeof(DataHeader)];
read_data(buffer);
DataHeader * header = reinterpret_cast<DataHeader *>(buffer);

содержал в себе UB.

Поскольку эта практика была распространенной, такой UB не эксплуатировался компиляторами. Но в будущем запросто может. Поэтому в C++20 этот UB устранили для нескольких случаев (включая показанный в примере выше), но для других случаев подобные касты все равно остаются UB.

Эт да, но это одна из самых очевидных проблем, которую легко обойти, если делать преобразование представления прямо "по месту". Веселее с неочевидными, когда люди, например, не знают про выравнивание данных и директивы вроде #pragma pack.

И ради чего все это? Вот чтобы что?

Чтобы в коде не было UB и чтобы очередная версия компилятора не превратило результат трансляции вашего cast-а в ХЗ что.

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

Если у вас свежий C++, то в C++20 уже были сделаны некоторые послабления.
А в C++23 завезли std::start_lifetime_as.
Проблем с выравниванием данных это не решает (тут вы правы на счет переносимости), но хотя бы избавляет код от UB.

на приктике такие конструкции не то что никому не нужны , они будут мешать. Поэтому так писать никто не будет, поэтому вы и стесняетесь объяснять в чем смысл вашего кода, потому что объяснения вам самому покажут какая там ерунда написана

Жаль, что здесь есть только две оценки для комментария: +1 и -1.
Очень, очень не хватает оценки facepalm. Хотя здесь бы более уместной была бы double-facepalm.

1
23 ...

Информация

В рейтинге
1 459-й
Откуда
Гомель, Гомельская обл., Беларусь
Зарегистрирован
Активность