10 переваг мови С++, яка і досі залишається актуальною

Усім привіт, мене звати Іван. Маю 6-річний досвід роботи в IT, працював на початку своєї кар’єри в стартапі, потім Luxoft, зараз в EPAM. Обіймаю посаду старшого розробника.

Це стаття про переваги С++ і чому ця мова ще є актуальною. Ця стаття буде цікава тим, хто вже володіє якоюсь мовою програмування і хотів би зрозуміти, чи варто вчити С++ і які її переваги над іншими мовами, а також тим, хто розуміє С++ і хотів би дізнатися більше.

1. Багатопарадигмова мова

Глобально парадигми програмування можна розділити на дві категорії: імперативна та декларативна. Всі з Вас, хто програмує, певною мірою вже знайомі з імперативною, вона включає ООП.

C++ — мова з декількома парадигмами, які можуть вирішити проблему багатьма різними способами. С++ підтримує наступні парадигми:

  1. процедурна;
  2. функціональна;
  3. об’єктноорієнтована.

Багатопарадигмова мова програмування дозволяє програмістам вибирати конкретний підхід або змішувати частини різних парадигм програмування, надаючи гнучкість для розв’язання різних проблем.

Переваги використання багатомовності включають скорочення часу, витраченого на вивчення нових носіїв для різних мов, і полегшення переходу до різних парадигм.

2. Управління пам’яттю

Тривалість зберігання — це властивість ідентифікатора, що визначає правила створення і знищення об’єкта.

Існують 4 види тривалості зберігання:

  1. автоматичний;
  2. статичний;
  3. поточний;
  4. динамічний.

Тривалість зберігання тісно пов’язана з тривалістю життя об’єкта. Наприклад, два глобальні об’єкти з властивістю статичної тривалості зберігання мають однаковий час життя — весь час виконання програми. Водночас два об’єкти з динамічною тривалістю зберігання даних будуть мати різні терміни життя. Тривалість їхнього життя залежить від того, коли викликаються відповідні функції динамічного управління пам’яттю.

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

3. Нульова абстракція

Принцип нульових накладних витрат — принцип конструкції C++, який стверджує:

Ви не платите за те, що не використовуєте.
Те, що ви використовуєте, настільки ж ефективне, як і те, що ви могли б писати вручну.

В загальному випадку це означає, що C++ не буде додавати функції, які додають будь-які накладні витрати, чи то в часі, чи у просторі, більше ніж програміст міг би ввести без використання функції. Єдиними двома особливостями мови, які не відповідають принципу нульового накладу, є ідентифікація типу виконання та виключення, і тому більшість компіляторів запускають переключення, щоб вимкнути їх.

Приклад нульової абстракції: масиви C-стилю пропонують безперервне зберігання при дуже низькій вартості, якщо задекларувати масив з C-стилем в області файлів, то можна замінити його на масив STL (C++) без додаткової вартості. Це можна подивитися за асемблерним кодом.

4. C++ сумісна з попередніми версіями протягом десятиліть

С++ сумісний з попередніми С++ версіями протягом кількох десятиліть, а реалізація підтримувала бінарну сумісність так довго, або майже так само довго. Жодна інша мова програмування не має цього.

Що робить будь-яку іншу мову так само підхожою для взаємодії з безліччю бібліотек С++, які були написані за всі ці десятиліття.

5. Детерміноване руйнування

Детерміноване руйнування не є синонімом до RAII. Мало мов наблизилися до того, щоб мати такий спосіб життя і безпеку ресурсів. RAII є загальнішим методом управління всіма ресурсами, в той час як garbage collection використовується тільки при роботі з пам’яттю.

Виділення ресурсу (або отримання) здійснюється під час створення об’єкта (зокрема ініціалізації) конструктором, поки ресурси вивільняються на момент знищення деструктором.

Детерміноване знищення об’єктів, можна знайти лише в декількох інших мовах.

Коли виконання залишає функцію або будь-яку іншу область, чи то регулярну, чи то за винятком, всі локальні об’єкти знищуються і виконуються їх деструктори. Не тоді, коли збирач сміття відчуває, що щось потрібно прибрати, а відразу. Детерміноване руйнування гарантує, що ресурс звільняється, як тільки він нікому не потрібен.

6. Перевантаження оператора

Це не є унікальним для C++, але це все ще є особливістю. C++ має дещо, що може бути справді жахливим (не в тих руках!). Такі особливості, як перевантаження операторів: ними можна зловживати, але вони дозволяють містичні/ магічні бібліотеки, такі як boost spirit.

Існують правила для операторів перевантаження і їх потрібно дотримуватися для запобігання таким ситуаціям у своєму коді. Але в правильних руках вони набагато покращують читання коду і ховають якісь речі під капотом для простішого використання.

7. Невизначена поведінка

С++ — це мова програмування, задана через стандарт, який є «абстрактним» у різний ситуаціях.

У стандарті написано:

«Невизначена поведінка виникає, коли програма виконує щось, результат чого не визначено стандартом.»

Щоб імплементація могла робити якісь речі, що виходять за рамки відповідальності за реалізацію, то стандарт ніяк це не забороняє робити, що зазначено у цих «невизначених ситуаціях поведінки». Перевагою є те, що програма буде уникати непотрібної перевірки (наприклад, перевірки масиву за межами), яка буде коштувати часу для виконання програми, як в більшості інших сучасних мов програмування.

Ще, наприклад, вона не вимагає, щоб реалізація забезпечувала доступ до об’єктів у межах цих об’єктів.

Ці «ситуації» є тим, що ми називаємо «невизначеною поведінкою»: їх здійснення не має конкретної відповідальності, і тому стандарт дозволяє «все» і виходить, що мова не обмежена і лімітована в поведінці стандартом. Це частково пояснює, чому С++ все ще дуже успішна у застосунках, які потребують ефективного використання апаратних ресурсів.

8. Можливість використання функції друга

Friend — ключове слово в C++, яке використовується для обміну інформацією про раніше прихований клас. Наприклад, приватні члени класу приховані від всіх інших класів і можуть бути змінені тільки через розсилки або роздачі. Це ключове слово розширює можливості мови, і змінює поняття приховування в ООП для С++, а також додає варіацію способів вирішення проблем.

9. Узагальнене програмування

Загальне програмування є узагальненням програмних компонентів таким чином, щоб їх можна було легко використовувати в найрізноманітніших ситуаціях. У C++, шаблони класів і функцій є особливо ефективними механізмами для спільного програмування, тому що вони роблять узагальнення можливим без втрати ефективності. Генерація всіх типів відбувається на етапі компіляції, і це дає можливість реалізувати TMP.

Template Metaprogramming (TMP) — метод програмування, при якому компілятор використовує шаблони для генерації тимчасового вихідного коду, який він поєднує з рештою вихідного коду і компіляторів. Вихід цих шаблонів може охоплювати константи часу компіляції, структури даних і повні функції, які не займають часу у часі виконання.

10. Мова середнього рівня

Мова середнього рівня, що означає підтримку високого рівня програмування та низького рівня програмування. Вона підтримує використання ядра та драйверів у низькорівневому програмуванні, а також підтримує системні програми мовою програмування високого рівня. С++ сприймається як інструмент кодування середнього рівня, на відміну від інших мов низького і високого рівня. Тому він досить збалансований, щоб дозволити програмістам контролювати ситуацію у процесі розробки та маніпулювати апаратними системами.

Висновок

С++ дає гнучкість у використанні та максимальне управління тим, що використовується. C++ дозволяє познайомитися з низькорівневими концепціями програмування і допомагає зрозуміти, як комп’ютери думають і працюють.

👍ПодобаєтьсяСподобалось10
До обраногоВ обраному5
LinkedIn

Найкращі коментарі пропустити

«Плюси» потребують iнтелект вище середнього. Тому свого часу C++ почав поступатися високорiвневим мовам програмування. В 2000-х роках я входив в айтi, i поруч було десятки сiшникiв (з плюсами й без) а також, почали заходити джавiсти. Контраст був досить помiтний: сiшники-з-плюсами — то була справжня iнтелiгенцiя, вiд слова «iнтелект». Сiшники-без-плюсiв — хлопцi попостiше. Джавiсти вiдрiзнялись вiд «плюсовикiв» своєю поведiнкою, рiвнем гумору (бо порог входження нижче, як не крути).
Зараз, коли в айтi не набiг тiльки лiнивий, цього контрасту вже не стало.
В тiй конторi, що я починав, було ще достатньо совка: великi кiмнати з простими столами, що стояти рядами, чистенько-бiдненько, овертаймили, столовка була своя, роками всi варилися в одному соку й один одного знали як облуплених.
В столовцi посуд був звичайний, його мили, тому поїв-вiднiс на мийку, щоб тару не затримувати.
Сiшники були дисциплiнованi- з’їв борщ — вiднiс тарiлку, (allocate\deallocate memory, рефлекторно)
Джавiст зазвичай сидить, їсть, поруч на столi гора брудного посуду, мабуть чекає на Garbage Collector.

Я дотнещик, який зазаз свічається із .Net на C++. Можна подумати що я зїхав зглузду, але відповідь чому дуже проста, я не хочу розвязувати ті самі задачі що я розвязую на .Net на плюсах, я хочу займатись низькорівневою розробкою, працювати з хардварою і embedded пристроями. Без C/C++ в тому світі ніяк. Тому якщо:
— Ти хочеш працювати з залізом, кодити якісь дрони і тому подібні речі, ти мусиш знати C/C++
— Ти хочеш кодити операційну систему, розробляти драйвери, мусиш знати C/C++
— Тобі треба зробити якусь дуже оптимізовану прогу, наприклад, якийсь копмресор для відео, можна впринципі і без C++ обійтись, але використовувати джаву чи пайтон не найкращий варіант для таких задач.

Тому мені здається що мова доволі актуальна, просто використовується у певних специфічних областях, якщо вам цікаві ті області ви мусите її використовувати. А Світ IT у свою чергу не закінчується на тому щоб робити веб і мобайл аплікухи, і всякі бекенд системи.

Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
1. Багатопарадигмова мова

чиста правда, тільки біда, що той «сабсет парадигм» який обрали в наших «кузнях кадрів» затряг десь так років 20тому

2. Управління пам’яттю

теж перевага, тільки біде в тім, як це комбінують з «багатопарадигменістю» (зовсім не вдало)
варто було б згадати і те, що укрування динамічною пам’яттю також може бути автоматичним
shared_ptr... і таке інше
(а те, що тут назвали «автоматичний», очевидно варто було б уточнити «в стекові»)

3. Нульова абстракція

правильніше сказати «ми не платимо за те, що не використовуємо»

4. C++ сумісна з попередніми версіями протягом десятиліть

а ще вона може побачити всі «Сшні» хедери численних ембеддед платформ «з коробки»,
без необхідності описувати це вручну!

5. Детерміноване руйнування

правда, але примушувати людину писати класи-вропери довкола кожного ресурсу — знущання!
благо на пітхабі можна віднайти чимало самопсних «велосипедів» для «go-lang like defer» різної складності та зручості

6. Перевантаження оператора

Перевага тоді, воли оператори використовуються за принципом «найме5ншого здивування»,
«мова в мові» boost spirit, це для багатьох більше антиприклад, як не робити «DSL»

але в загальному перевантаження операторів (вкупі з перевантаженням функцій та ADL) дуже спрощує роботу, якщо не намагатися щоразу винайти «свою DSL на перевантажених операторах»

7. Невизначена поведінка

а ось це не перевага, а прокляття...
так, воно дозволяє компілятору робити «класні оптимізації»,
але треба вмикати ворнінги по максимуму і вважати на strict aliasing

8. Можливість використання функції друга

ем... знову ж таки, прекрасна фіча, але біда, що в наших «кузнях кадрів» мудрому використанню не вчать

9. Узагальнене програмування

це «аб’юз» тієї фішки, що темплейти в С++ «раптом» виявилися turing complete функціональної мовою програмування з «паттерн матчінгом» за параметрами шаблона...

для «непосвяченого» в «фунціональщину» такі трюки — правдиве пекло...
а для посвяченого — «недомова»...

проте чудова штука, коли не хочеться використовувати сторонні тули кодогенерації, обійтися без макросів і не бавитися з пребілд степом...
(і навіть незамінна там, де, інакше б довелося «парсити С++»)

10. Мова середнього рівня

100% тільки тут біда в тому, що опонерти з обох «таборів» (високорівневого ти низькорівневого) обирають фічі «протилежного табору» для своєї атаки!

насправді ж можна юзати і для ембедда (як «С з класами»), так і для високорівневих штук слідуючи функціональному стилю, проте для щього, знову ж таки треба досвід та підготовка, з чим в наших «кузнях кадрів» — біда...

Є РАСТ з Вебассамблею,цешка таке,майже все!Може буде актуальна ще якийсь час с

є ТЕСЛА з портативним дизель генератором. двс все, може бути актуально ще деякий час.

з квори:

After a decade of working with C++ what annoys you the most?
After two-and-a-half decades working with C++, what annoys me the most is the rate of change in C++ over the last 9 years.

Don’t get me wrong. I love move semantics (C++11). I love the new concurrency features (C++14 and 17).

But there are a lot of less useful things being grafted onto C++. Range-based initializers are pretty, but often inefficient. Same with range-based for loops. std::any doesn’t offer much I need. lambdas are class instances wearing fancy clothes. I could go on and on. But the point is, I have to learn all this stuff to stay current, but it doesn’t fundamentally change my practice.

C++ stayed fundamentally the same for about 15 years after I learned it in 1995. This allowed developers like me to get really good at C++. Since 2011, C++ has become yet another topic on the learning treadmill that all developers must run on.

тому я i покинув плюсовий свiт

lambdas are class instances wearing fancy clothes.

Тю, а что, было непонятно, что иначе инкапсуляцию захваченных данных не реализовать? Не годится — ну не используй лямбды.
Ладно бы он пожаловался на то, что лямбды с bind существуют впараллель и новичку неочевидно, как в одном и другом что-то сделать с одинаковым результатом. Но просто наезжать на неизбежные свойства реализации — мнэээ... странно.
Одного этого понятно, что что-то у этого товарища нечисто в размышлениях.

Since 2011, C++ has become yet another topic on the learning treadmill that all developers must run on.

Только-только в C++11 наконец сделали логическое замыкание фич предыдущих поколений до приемлемого состояния и начали развивать... как товарищ недоволен. Ну, баба с возу...

На доу популярний топік по програмуванню? Дякую, як початківцю с++, цікаво було почитати коменти.

що цікаво, в темі про С++ доказують що С++ ліпше чим Rust

А чим теми про С++ гірші за теми про Раст?

Думаю, що тренд, однако, очевидний

Вибачте що втручаюсь в суперечки аксакалів. Шукаю інструмент code coverage, щоб працювало у vscode + msvc clang. Поки використовую gcc cov, але незручно — доводиться перезбирати під gcc, щоб побачити code coverage.

“Cobertura is a free Java code coverage reporting tool”
Трошки не підходить, мені для C++. Чи там є?

Не тільки, я С++ міряв цією тулою
Я відкопав де я це робив і я забув, вибач! Там треба ще opencppcoverage заюзати... Ось мої команди. Я трохи позамінював назви, але намагався повністю зберігти структуру важливих шляхів та файлів, аби ти міг це повторити на своєму проекті.

Як я білдив проект, він cmake-based:

mkdir project-build
cd project-build
cmake ..\project\
Це нагенерує солюшен студії у папочці project-build поруч з project. Потім у студії збираєш. Далі знову у консоль.
cd project
scripts\coverage.bat
Скрипт нагенерує coverage.xml який можна зкормити у pycobertura (тобі треба пітон мати). Він виглядає так: (папка source це підкаталог у project)
opencppcoverage ^
	--export_type cobertura:..\project-build\coverage.xml ^
	--sources source ^
	--working_dir ..\project-build\tests ^
	-- ..\project-build\tests\project-tests.exe
І потім генеруємо репорт у консоль:
pycobertura show ..\project-build\coverage.xml
Або html:
pycobertura show --format html --output ..\project-build\coverage.html ..\project-build\coverage.xml --source \
Незручно те що для —source можливо підійде і relative path, але у мене ні, я йому давав корінь, тоді він зназодить сорци.
OpenCPP coverage: github.com/...​/OpenCppCoverage/releases
pycobertura: github.com/aconrad/pycobertura
Можливо якісь депенденсі я забув. Зараз не можу протестувати актуальний стан справ, тільки завтра.
Не топ тула, MC/DC вона не виміряє, але при деяких послабленнях на code style можна майже усі бранчі покрити, та іноді і conditions =) я з нею багато помилок виловив у проекті на якому працював сам один.

LLVM-Cov llvm.org/...​ommandGuide/llvm-cov.html пробував якщо GCC та GCov не подобатися ? 2. Взагалі під вінду і GCC і CLANG рекомендую використовувати від MSYS2. Там пакетний менеджер pacman як у arch linux і усі новітні версії та збірки, і до того які злінковані з Universal CRT. Можна не займатись мануальними білдами Dll Hell-у. Інші пакетні менеджери, як то Conan як на мене — не те.

Спробую. Використовую msvc clang, бо він найшвидший. Ну а gnu clang швидший за gcc.

Що ви розумієте за «швидший» ? Швидше компілює похідний код, чи генерує бінарний код для конкретної цільової платформи, який працює відносно швидше ніж той самий похідний код скомпільований іншим компілятором, через кращі оптимізації ? Насправді відповіді нема, є низка тестів які синтетично підігнані під один компілятор, так само є контр версії інших синтетичних тестів підігнаних під інший. В чому перевага збіркі від MSYS2 — новітня версія, така сама як і під Linux чи Mac, відповідно з баг фіксами кращєю підтримкою стандартів тощо, та пакетним менеджером який дозволяє легко встановити більшість із залежностей без того щоб їх годинами скачувати і білдити. Щодо замірів швидко дії коду який отримується і GCC, і Clang і MS VC++, при умові лінкування з Universal CRT на Windows то мої синтетичні тести, для XML parser бібліотеки, що я написав, показували різницю в межах статистичної похибки. Там швідкість більше від обладнання залежить, скажімо як файл читається з HDD чи SSD.

Сиджу в vscode + msvc clang. Швидше збирає мій код. Дебагер значно швидше запускається за gdb. В мене embed (STM32, IAR), але працюю 95% часу в тестах на googletest.

Повільні збірки фіксяться або unity build або precompiled headers або ccache.

Повний білд: msvc clang — 52sec, gnu clang 1m 30sec. Але повний білд доволі рідко, типовий сценарій — правка тесту/дебаг.

Як бачите, проект відносно невеликий, чи варто для такого пробувати ccaсhe і ко?

У msvc clang відчутно бистріший лінкер, тобто тут ccahe та інше я так розуміє не допоможе.

Для порівняння, в IAR — 1m 43sec

Та 2 хвилини — то не година. Думаю, тут взагалі не варто перейматись, якщо не налаштовуєте CI/CD (де час то є гроші).

Юніті білд прибере стадію лінковки взагалі.

Як багато разів на день треба перебирати, може задовбати. Та дійсно не Mozilla, три строки коду поміняв — на ніч блід залишив.

52sec, gnu clang 1m 30sec

подивіться на сетінги, напевно PCH справді не задіяний.

Я дотнещик, який зазаз свічається із .Net на C++. Можна подумати що я зїхав зглузду, але відповідь чому дуже проста, я не хочу розвязувати ті самі задачі що я розвязую на .Net на плюсах, я хочу займатись низькорівневою розробкою, працювати з хардварою і embedded пристроями. Без C/C++ в тому світі ніяк. Тому якщо:
— Ти хочеш працювати з залізом, кодити якісь дрони і тому подібні речі, ти мусиш знати C/C++
— Ти хочеш кодити операційну систему, розробляти драйвери, мусиш знати C/C++
— Тобі треба зробити якусь дуже оптимізовану прогу, наприклад, якийсь копмресор для відео, можна впринципі і без C++ обійтись, але використовувати джаву чи пайтон не найкращий варіант для таких задач.

Тому мені здається що мова доволі актуальна, просто використовується у певних специфічних областях, якщо вам цікаві ті області ви мусите її використовувати. А Світ IT у свою чергу не закінчується на тому щоб робити веб і мобайл аплікухи, і всякі бекенд системи.

достатньо С, С++ зайве, бритва Оками
чомусь С++ примазують до плюсів,

ну не пишуть же Пайтон/Перл

але пишуть С поділене на С++

але пишуть С поділене на С++

Так пишуть коли мають на увазі їх спільни риси (яких дофіга і які складають основу цеї дискусії). Все вірно.

ну не пишуть же Пайтон/Перл

Пишуть.

але пишуть С поділене на С++
Так пишуть коли мають на увазі їх спільни риси (яких дофіга і які складають основу цеї дискусії). Все вірно.

так пишуть бо сі++ передбачає наявність сі і пряму інтеграцію в обидва боки без додаткових прошарків на зразок java jni .net python invoke bindings

а ну і да доречі об’єктні файли сі++ фактично є тімо самімо об’єктними файлами сі тіко зі своіма додатковими naming convention щоб покрити «статичний поліморфізм» якого у чистому сі такі нєма і відповідно такі нєма у об’єктних файлах і далі вже старий добрий синтаксичний цукор який дозволяє прокидати плюсові класи через бар’єр all shared object не помітно для «програміста» який сьогодні навіть не здогадується що там десь є щось вродє dumpbin elf objdump

ну не пишуть же Пайтон/Перл
Пишуть.

йошкінЪ котЪ куди я попав оп’ять ((

так пишуть бо сі++ передбачає наявність сі і пряму інтеграцію в обидва боки

Так пишуть, бо «/» в такому випадку означає «або».

Все інше що ви кажете — має сенс, але окремо.

(Можу сам перейти на стиль де «//» означає «або». Може, так і краще)

а ну і да доречі об’єктні файли сі++ фактично є тімо самімо об’єктними файлами сі

Або Паскаля. Або Ади. Або ще 500 мов «високого» рівня. Або асемблеру, бо він компілюється в такі ж *.o.

Тільки треба сумісний ABI зберігати, де його вимагають.

йошкінЪ котЪ куди я попав оп’ять ((

В реальний світ :))

Спитай фірму STMicroelectronics, чому їхнє SDK на С++

Ніби дотнет теж щось пропонував для IoT? І можна на рівні IL-коду працювати, або асемблерні вставки, чи то притягнуто за вуха? Я б спочатку всі можливості дотнету вичерпав чим занурятись в С++.

Там дуже обмежена версія дотнету, здається, навіть без рефлексії, і там, якщо я не помиляюсь, інтерпретатор, тож ніяких асемблерних вставок не буде.

Такі мови як дотнет чи джава наврядчи будуть по серйозному використовуватись для тих задач. Вони йдуть разом із віртуальним середовищем (a.k.a. CLR) яке створює великий оверхед, програмування під хардвару вимагає щоб код компілився в зразу в готові бінарники, а не IL, і щоб вони були максимально малі по розміру. Чесно кажучи я не дуже дивився що там дотнет пропонує в IoT зараз, але достатньо подивитись вакансії у тій області і побачити що там майже всюди треба знати C/C++. Тому на мою думку підстроювати дотнет під область хардварного програмування це більше ставити себе в ящик, краще вивчити інструмент який використовує ринок і відкрити собі ряд можливостей.

Насправді в IoT та промисловості багацько задач які цього не потребують. НІчого не буде якщо я в якійсь гідропоніці вологість землі поміпяю на кілька мілісекунд повільніше, або якесь стендове обладнання де не маю вимог для швидкості, але є вимоги до гнучності. Якийсь MicroPython, або щось інше, тут підходить я б сказав на відмінно.

Є, але воно нежиттєздатне. Так, погратись. Та й наголовніше — це маргінальний напрямок, мало користувачів. У випадку проблем ви залишаєтесь один на один. А з часом Microsoft його прикриє через непопулярність і взагалі все.

Багато часу довелось потратити на те щоб розібратись в базі лов левела, наприклад архітектура копютера, асемблер. Якщо базу цю базу знати то поверх неї вивчити C, а потім С++ не так і важко. Ну і в моєму випадку так як я ще з embedded девайсами хотів працювати, то найскладніше це електроніка, того шо як складаєш якийсь девайс не достатньо суто програмування, треба розуміти як правильно скласти різні модулі докупи щоб була правильна напруга, струм і т.д.

схоже що стаття писалась щоб отримати чергову личку на епамі

насправді переваг нуль, тупикова гілка

маємо те що маємо проти більшості не попреш селяві нічо лічного просто бізнес

Вот у тебя совсем не бомбит от С++ тем :DD

так, на проекті те що на С++ ті всі мікросервіси що пишуться/тестуються, на го/руст разів би 100 швидше йшла розробка, але ж ми не шукаєм легких шляхів

При переході на Раст більшість би звільнилась

Мені здається, що раст пішов трохи не туди, коли поміняли fun на fn

Да, без фану тяжко. Пишемо зовсім через силу...

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

Скажіть, а як зараз у плюсах, наприклад, розпарсити JSON у обʼєкт?
Edit: розпарсити у плюсову структуру без написання кода руками

Маю на увазі, як розпарсити джсон у структуру без написання кода руками.
Але все одно дякую, мабуть, ніяк без обмазування макросами.

Що не так з макросами? Ну, не те, що на асемблері, але якось робе

Та все так, може, я, звісно, відстав, але воно ж напевне щось таке:

__BEGIN_JSON_CLASS(Product)__

__JSON_PROPERTY(Id)__

...
__END_JSON_CLASS(Product)___

Ще й погано/зовсім не комбінується у випадку складної структури.

Нижче я дав лінк на те, що в нас

Виглядає жахливо, як висловився Саттер — код схожий на каракулі. Нажаль цілі фреймверки так побудовані, як наприклад wxWidgets. Це одна з проблем C++, як то кажуть мова дуже складна і одночасно — вона не достаньо складна в порівнянні з конкурентами.

як то кажуть мова дуже складна і одночасно — вона не достаньо складна в порівнянні з конкурентами.

проблемою мови стало те що вона перестала бути мовою прикладного програмування що власне передбачалося спочатку ідеєю «сі з класами» потім «шаблонами» але як виявилося цей шлях не надто однозначний точніше на впаки але тут окрема проблема як на мене бо сьогодні сі++ просто не є «практичною мовою» як пряме вираження тези «сьогодні комюніті не є практичними» )) селяві

Сучасний С++ без купи ***** виглядає так само, як пітон.

Або я просто звик, хз.

Проблема як раз що багато програмістів йдуть в С++ саме заради *****

? Як я знаю йдуть бо в бізнес домені в який їм довелось потрапити ця мова є провідною чи принаймні додатковою. А це ціла низка доменів : Embedded, GameDev, Desktop. Додаткова як мінімум в high performance (enterprise) та AI/ML. І відповідно системне програмування теж часто використовує і C++. Такого широкого застосування зазнає може лише підмножина — С. А так, якщо ви займаєтесь скажімо Frontend, то в незалежності від того подобається вам TypeScript чи ні — його доведеться досконально вивчити, навіть якщо більшість коду написано на підмножині — JavaScript.

Щоб потрапити до домена, треба знати якусь мову. От хтось вчить Пітон, бо він простий, а хтось вчить С++, бо воно складне й швидко працює. Людина хоче викликів, і потім усюди пише «елегантні рішення», котрі неелегантні програмісти, що працюють за гроші, не розуміють.

Але аспектний фреймверк типу Newton/Jakson було винайдено habr.com/ru/articles/655645

Ну немає в плюсах рефлексії. Не можна в таке метапрограмування на плюсах. Так само як в дотнет не можна закрити рефлексію (to native то вже не то). By design так.

Так, я розумію, але ж є (могла бути) кодогенерація.

ніяк, не для цього ця квіточка береглася

С++ сумісний з попередніми С++ версіями протягом кількох десятиліть, а реалізація підтримувала бінарну сумісність так довго, або майже так само довго. Жодна інша мова програмування не має цього.

Цікаво, це ваш особистий погляд? Для того щоб так впевнено говорити, потрібно дослідити всі існуючи мови програмування (а їх понад 1000).
А з приводу того, що якась мова протягом 30-40 років підтримує сумісність (у тому числі й бінарну) — чи це добре? Java підтримувала сумісність із попередніми версіями більше 20 років, а потім почала видаляти застарілі речі, які більше ніхто не використовував.

минулого року один мій колєга зробив проект на java8. тепер він не може обновити її, тому що проект використовує бібіотеку, всередині якої корба. для взаємодії з іншим компонентом, який ми не контролюємо.
я не джавіст, але часто чув, що всякі маленькі зміни апі в джаві ускладнюють перехід з однієї версії на іншу.

Ви знаєте, я дуже багато різних бібліотек використовував у своїй роботі, але не пам’ятаю жодної, яка використовувала б Corba.
Якщо ваш знайомий вирішив вставити у свій проект такий шмат legacy, значить він розумів усі ризики та відповідальності. Він, до речі, може оновитися на Java 9 або 10, там підтримка Corba ще є.

так, цілком справедливо, легасі ще той. але ж мені все таки здається, що отой самий принцип, написано один раз працює усюди, не сильно правда.

я не джавіст, але часто чув, що всякі маленькі зміни апі в джаві ускладнюють перехід з однієї версії на іншу.

У Java API ніколи не було змін, які порушували б зворотну сумісність.
Єдиний виняток — видалення з JDK 11 методів thread/stop класу Thread, які були оголошені як deprecated ще в Java 2.

Серіалізацію прибрали. URLClassloader замінили на LocalClassloader в ціла низка інших змін, що не слабо так зламала старий код.

Що таке

LocalClassloader

?

В мене був один комерційний проєкт на корбі, здається в 2000-му

Вона й пізніше використовувалась. В 2008-му здається, я робив інтеграцію з однією росіянською системою відеоспостереження, з розподіленою архітектурою. Там для комунікації між модулями вони використовували оцю корбу. Для дотнету була імплементація IIOPNet, якщо не помиляюсь, через Remoting воно працювало.

То може, та розподілена система закладалась десь до 2000?

Можливо, на той час система була доволі розвинена, то ж мабуть в розробці була вже давно.

Там не маленькі зміни, там зміни які ламають багато речей які історично фіксились воркераундами. Тобто можна було би без тих трюків, але існує багато але — наприклад був би жахливий перформанс і т.п. Таким чином то якась аспектна бібліотека зламається то якась JNI обертка зламається і т.д. і т.п. А потім не працює цілий фреймверк, як то Spring — бо він у себе цю бібліотеку з трюками використовує. Відповідно сам Spring має робити переробки, до того ж доволі суттєві — а потім ви в себе а проекті маєте їх робити — і там теж зворотня сумісність вже самого спрінга виявится зламана і треба буде переписувати існуючий код. Таким чином банальний абгрейд на нову версію, може вилитись в суттєвий об’єм роботи, при цьому без реалізації жодної бізнес фітчі. А це гарантований конфлікт із бізнесом, якому треба за це платити. Щоправда можна аргументувати секюріті, що щоб вас не зламали треба абгрейди робити і цьому це буде правда. Надаль такі речі відвертають технічних керівників від платформи, вони починають шукати альтернативи — Node, Go lang, Python чи .NET

Там підхід був м’яко кажучи який мало усе не загубив. Тільки от зараз поступово усе нормалізується вже із версією 21.
Підход — Java 9 і вище, а також судові тяжби з Oracle та Google, викинув Java з багаторічної першої позиції в TIOBE Index.

Все залежить від того, що писати і хто замовник. Безперечно с++ дуже гарна мова для деяких задач. Але тривалість розробки велика. Для деяких бібліотек підійде.

А також для UI на десктопі, особливо коли треба підтримка одночасно macos і вінди. Ви ще не забули, що є звичайні компи, окрім смартфонів? І щодо тривалості розробки, це міф. Робити UI що на WPF, що на QML, потребує порівнянних зусіль. Щодо бізнес-логіки, різниці з дотнетом мабуть зовсім немає.

Зануда мод он:

с++ це буквально єдина справжня мова програмування.

Всі інші або написані не професійними програмістами, або використовуються геть не так як було задумано :)

Ви забули зануду вимкнути.
Чи у вас в деструкторі?))

Єдина суттєва перевага С++ це компілятори що вже під нього написані. Там поховані десятиріччя кропіткої роботи команд, які билися свого часу з слабким залізом. Зараз вже стільки грошей і часу не проінвестують в новітні мови програмування.

Все інше, що ви вказали, більше схоже на тролінг. Тому що з точки зору звичайного інвестора, що інвестує в прикладне ПО, трьох С++ програмістів можна сміливо міняти на одного джавіста, рубіста, шарпіста і тд — строки релізу проекту будуть навіть швидші.

Зараз вже стільки грошей і часу не проінвестують в новітні мови програмування.

Вже зробили
clang
Треба лише frontend написати який згенерує AST :)

та ну. ast ще треба лінеаризувати і перетворити на llvm код. ця задача не така проста, як вам здається.

ця задача не така проста, як вам здається.

LLVM currently[as of?] supports compiling of Ada, C, C++, D, Delphi, Fortran, Haskell, Julia, Objective-C, Rust, and Swift using various front ends.

Many other components are in various stages of development, including, but not limited to, the Rust compiler, a Java bytecode front end, a Common Intermediate Language (CIL) front end, the MacRuby implementation of Ruby 1.9, various front ends for Standard ML, and a new graph coloring register allocator.[citation needed]

І взагалі С++ вже вмирає (ні), ніхто не хоче на ньому програмувати (хочуть, але високий поріг входження), вакансій нема (є), зарплати маленькі (ні) і т.д.

Гурт «Пирятин» на цю тему пісню написав же ж

Прийшов до висновку, що в сучасному світі розробки, де код потрібно писати якнайвидше і на вчора, цей пердолінг з С++ і сторонніми бібліотеками (які часто несумісні одна з одним), помилки лінкера/компілятора десь в шаблонному коді, налаштуваннями проекту (щоб все збілдилось з першого разу незалежно від середовища), смейк скріптами на сотні рядків того не вартує. Вийняток хіба що коли йде боротьба за наносекунди швидкодії і кілобайти пам’яті. Навіть для системного програмування вже є купа врапперів і біндінгів для більш високорівневих мов.

Ну от в системному часто треба швидку реакцію. А тут GC прийшов.

ну прийшов то й прийшов перервали його та поставили на паузу на самом інтєрєсном мєсті зробили швидку реакцію і далі пішли

а є вапчє вже мова яка дозволяє писати interrupts кросівоє?

Дряйвери під лінукс, наприклад. Там є low half і high half.

Умовно таймстамп запам’ятовуєш в обробнику переривання, а вичитка даних наприклад з SPI щедулиться окремо.

Реализация 1: в памяти дохрена структур, которые сложно ссылаются друг на друга всякими shared_ptr и weak_ptr, сложенными в сложные кольца. Всё это держится на одном корневом объекте описания, из которого куча ссылок — массивами, деревьями, отдельными объектами и т.п.
Последняя ссылка на корневой объект разрушается, срабатывает деструктор, и нитка блокируется на несколько секунд, потому что деструкция ссылок тянет за собой обнуление счётчиков других ссылок, от тех следующих по цепочке, и так далее. При этом ещё стек опасно близок к переполнению, потому что проход по цепочкам фактически реализует depth-first, а не breadth-first. В тестовых испытаниях стек постоянно переполнялся, поэтому его сделали неприлично большим.

Всё это было в автомобильном навигаторе на действие «забыть уже ненужный лист карты, с которого давно уехали», «поверните сейчас направо» не было сказано, водитель проскочил нужный поворот и теперь должен хреначить десять километров до точки разворота (а потом столько же обратно).

Реализация 2: то же самое, но в языке применён простой инкрементальный GC в стиле Lua. Если идёт круг GC, то на каждую аллокацию выполняется 2-3 прохода по списку старых объектов, и заметная часть тут же освобождается. Параллельно работает, на минимальном приоритете, фоновая нитка, которая, проработав сотню таких объектов из списка, вызывает для себя yield(), чтобы совсем уж никому не мешать даже при самом тупом шедулере. Случившийся delete (на самом деле просто выход ссылки из корневого набора) листа карты сопровождается вызовом типа gc.meek_hint(), который говорит «ну ты чуть ускорься, хоть и не настаиваю». Проблемы depth-first нет. Оперативка на задержанное освобождение не проблема — как ресурс его достаточно, в отличие от времени или стека.

Мораль? А её не будет, ну кроме припева (NSFW) и того, что «случай — он того, разный бывает».

(Кстати, найти stop-world GC сейчас вообще сложно. Emacs не в счёт, и то я мог отстать от жизни.)

Последняя ссылка на корневой объект разрушается, срабатывает деструктор, и нитка блокируется на несколько секунд, потому что деструкция ссылок тянет за собой обнуление счётчиков других ссылок, от тех следующих по цепочке, и так далее. При этом ещё стек опасно близок к переполнению, потому что проход по цепочкам фактически реализует depth-first, а не breadth-first. В тестовых испытаниях стек постоянно переполнялся, поэтому его сделали неприлично большим.

Всё это было в автомобильном навигаторе на действие «забыть уже ненужный лист карты, с которого давно уехали»

тю кота сосиской )) была у меня скромная оптимизация на 800,000% или ещё на 100500 порядков больше где я тупо пририсовал инкрементарный хип чисто под

уже ненужный лист карты

в котором и создавались все эти 800 млн объектов общим весом десятки гигабайт а «освобождалось» всё тупо тупым выбрасыванием (забыванием что там всё это было) потому что всё 800 млн представляли собой одно

уже ненужный лист карты

местные программисты долго жевали губами и противно морщились но поделать ни чего не могли схема реально работала на 800,000% и подкопаться было не к чему потому что в реальности это и правда тупо один объект

уже ненужный лист карты

самое обидное для них к.м.к. уже было что фича кастомный алокатор _уже_ был встроен в ихнюю архитектуру )) и да «правильное» удаление этого всего занимало так долго что его нельзя было даже сравнить с тем чтобы тупо закрыть программу и запустить заново я потом из интереса проверил но помнится я догадался уже позже (сделать как демо для насяльника) уже после того как взял деньги и всё

Реализация 2: то же самое, но в языке применён простой инкрементальный GC в стиле Lua.

told ya

в котором и создавались все эти 800 млн объектов общим весом десятки гигабайт а «освобождалось» всё тупо тупым выбрасыванием (забыванием что там всё это было) потому что всё 800 млн представляли собой одно

Метод арены — да, классика.
Хотя чаще его применяют не для этого, а ради гарантированного освобождения всего после обработки чего-то.

Ну вот ты и показал, что умный подход рулит :)

В трьох моїх великим проектах на С++ писати треба було так, щоб вони прожили років 20. Так і сталось. І це не дивно.

на передовій технології С++98?

Спочатку так, потім доапгрейдилося до С++14

А от, до речі, навіщо апгрейдились?

Та по-різному.

1) Зі свіжою версією QNX свіже GCC приїхало.
2) Вийшов новий офіційний тулчейн під Raspbian

Ну це — можливість. А де потреба?

Себто, як взагалі апгрейд проводили? Щось переписували, чи сказали хлопцям "завтра можете починати юзати decltype(auto)?

В першому випадку керівник мій кум, треба буде якось при зустрічі спитатись. Скорше за все, вийшов новий ріліз QNX з виправленими багами і якимись цікавими фічами.

В другому щось подібне. Просто в опціях прописали -std=c++14 і сказали, що можна користуватись. В нас гуя на Qt, якісь куски коду з автопілота (пошук шляху серед перешкод ітп) копілюється і в гую, для валідації і симуляції. Взагалі рішення ці приймаю не я, мені важливо, щоб перед тим, як щось міняти, нічого не розвалили, а коли розваили, то швидко відкотили і ще раз подумали.

Зайшов до сусідів розпитатися.

QNX 7.0 і GCC 5.4

Перейшли з 6.5, бо в ній не підтримувалося нове залізо.
Залишилися на С++11, бо це стабільга версія, яка йде з ОС.

Це не safety critical, а safety related. Якась частина (здається emergency stop) сертифікована за IEC 61508.

Теж працюю зараз на проекті, якому більше 20 років. Перейшли на 17 стандарт пару років назад, політ нормальний.
Переходили, бо нові фішечки роблять деякі етапи розробки зручнішими.
Плюс складність — в деякому сенсі так, але не такий страшний аваков, як його малюють. Плюс я завжди на зв’язку і з задоволенням поясню, що незрозуміло (я в команді щось типу «фаната C++» і «експерта», періодично консультую колег, у кого виникають питання).

Так воно так і є що всі пишуть на своїй мові, а потім або вставляють ++ або використовують бібліотеки які на ++ бо потрібна швидкість.

в сучасному світі розробки, де код потрібно писати якнайвидше

А шо це за такий сучасний світ? Раніше не потрібно було найшвидше?

Раніше не потрібно було найшвидше?

Ну ось уяві собі: здав ти стопку перфокарт на компіляцію і отримуєш роздруковку через день. І це якщо дівчата з зарплатнею 70 рублів не зронили її, змішавши всі карти. Тоді ще день затримки.

Тю, та таких аналогій і зараз купа. Уяви, шо виклав реліз недотестований системи і отримуєш великі проблеми через тиждень.

Чого уявляти, у мене таке було ;)
Але зараз, по більшости, для своєчасного тестування системи витрати в тисячі разів менші ніж тоді. (І не тільки для тестування, зрозуміло)
А тоді той обʼєм фактичної роботи, що робить середня галера для одного замовника, не могла зробити вся планета.

Але зараз, по більшости, для своєчасного тестування системи витрати в тисячі разів менші ніж тоді. (І не тільки для тестування, зрозуміло)

що породило парадігму «нє планірованія а толька ізмінєнія»

А тоді той обʼєм фактичної роботи, що робить середня галера для одного замовника, не могла зробити вся планета.

що породило систему у який на справді мало хто знає що саме «робить одна середня галєра для замовника як уся планета»

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

А тоді той обʼєм фактичної роботи ... не могла зробити вся планета.

це просто масштабувалося на порядки тож софт тепер тіпа теж вже передбачувано «нє на всігда by design»

youtu.be/BnKpNVHw-TQ

Раніше не потрібно було найшвидше?

раніше нє біло агілє )) а «програмістів» можна біло дажє біть

Був суцільний СММ 100500-го рівня

раніше було менше фрейморків/біблотек/високорівневих мов тому зліпити конкурента з гівна і палок було важче і відповідно погонщики не так сильно змушували грести веслами. Як приклад можна навести період пандемії (це було не так давно), коли всі масово ринулись захоплювати ринок користувачів, які сидять вдома та поточний ажіотаж з АІ. І байдуже, що воно все нетестоване і забаговане, пофіксити можна пізніше. Ну і використовувати щось більш високорівневе за С++ там де можна не використовувати С++ означає менше шансів вистрілити собі в ногу + поріг входу нижчий + швидкість розробки вища

1. Багатопарадигмова мова

Ну... писати можна, але якщо використовуються багато різних парадигм, то компілятор не може отримати гарантії, які надає слідування однієї лінії. Це ускладняє. Знову ж таки, ADT немає, без цього функціональність не дуже, ...

2. Управління пам’яттю

Але більшість розробників голосують за GC, якого немає.

3. Нульова абстракція

Скоріше за усе це zero overhead. Тут це скоріше помилка проектування, бо жодна мова цієї особливості не взяла. Взагалі, ООП не дуже поєднується з High Performance, тому економити на ініціалізації нулями майже нічого не дає. А от складнощі...

4. C++ сумісна з попередніми версіями протягом десятиліть

Знову ж таки, останні роки досить бурний розвиток с точки зору нових стандартів, що призводить до того, що приходиться стикатися майже у усіма етапами розвитку

5. Детерміноване руйнування

defer з Golang мені подобається більше.

6. Перевантаження оператора

Окрім базових бібліотек, я не дуже багато бачив, де б це використалося на практиці. Не кажучи про те, що базові бібліотеки можна було б додати у саму мову.

7. Невизначена поведінка

Мені незрозуміло, коли це буде перевагою. На практиці це призводить до того, що баги можуть жити роками, та вистрілювати у час Ч. Та ускладняється відтворення помилок. Економія... Це ж не ядро Linux, і то... там не С++ не пишуть.
Знову ж мультипарадигма. Особисто мені краще було там де треба надійність писати на одній мові, там де треба швидкодія — на іншій. А не пробувати запхнути усе в одну мову.

8. Можливість використання функції друга

Ну... Можливо і можливо, це не є чимось унікальним... Є internal, ...

9. Узагальнене програмування

Ну... шаблони С++ так і лишилися часткою С++, ніхто не ризикнув їх брати у такому вигляді. Як на мене generic з Ada набагато кращі у використанні.

10. Мова середнього рівня

На практиці це означає, що тобі треба думати про низький рівень коли працюєш на високому, та виконувати роботу компілятора, розкручуючи усі конструкції мови на низькому.

А де private наслідування, віртуальне наслідування? Теж унікальні фічі С++. Взагалі, якщо брати саме унікальні можливості С++, то вони так і лишилися у С++, ніхто їх не ризикнув брати до інших мов. Як на мене це натяк.

Буквально вчера для двух объектов перегружал операторы. Оно как отвертка типа torx, нужна редко, но без нее бывает плохо.

Тут це скоріше помилка проектування, бо жодна мова цієї особливості не взяла.

Та взяла, наприклад, Rust

Rust це все-таки не ООП. ADT без views, як на мене, вже тягне за собою трохи overhead. Знову, наприклад, як у Rust не ініціалізувати поле? Так, є assume_initialized, але unsafe. А це може буде overhead, тому що поле буле ініціалізовано пізніше.

Так, є assume_initialized, але unsafe.

Ну ансейф, і що. С++ взагалі весь ансейф, і нічого.

В принципі так усе можна реалізувати. Просто якщо брати zero overhead та UB, то в С/С++ якщо заради zero overhead нам треба припустити UB, то ми обираємо zero overhead. У Rust ми виключаємо UB. Звісно що засоби будь якої мови дозволяють обійти її обмеження, особливо якщо є interop з C :-)

У тебя какая-то каша в голове.

нам треба припустити UB

Никогда нельзя допускать UB. Если твоя программа допускает UB, она некорректна с точки зрения компилятора, это по сути как ошибка компиляции. Ты ж не скажешь «ради zero overhead я допускаю ошибки компиляции в программае»?

Дуже примитивний погляд.
Насправді найнадійніші програми не ті що написані без помилок, а ті що вміють себе «лікувати».
І якщо це так, то ub це майже штатна ситуація. Наприклад ub в космічних апаратах може визвати звичайна радіація, алеж там купа дублюючих та діагностичних систем що контролюють одна одну і тому там все працює дуже стабільно в порівнянні з програмістами що пишуть свої програми в світі рожевих поні.

Насправді найнадійніші програми не ті що написані без плмилок, а ті що вміють себе «лікувати».

Опять же буду немного скептично настроен и заявлю, что если инженер не смог написать С++ программу, которая не вызывает неопределенного поведения, у него нет квалификации для того, чтоб реализовать систему для космического аппарата, которая «может сама себя лечить» при облучении космической радиацией.

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

З цього я лише можу зробити висновок, що ти ніколи не бачив програм більше 30к рядків. Томущо там 100% будуть баги, в тому числі UB. Як не в твому коді, то в бібліотеках що ти використовуєш. В ідеальному світі багів нема. В реальному вони завжди є, як бактерії в організмі. І ти маєш з цим щось робити, а не мріяти про стерильний кишковий тракт

ніколи не бачив програм більше 30к рядків

Всего лишь 30К строчек? Видел и даже собственноручно писал.

Вот есть древняя бухгалтерия, которая пользуется калькуляторами.

Ты выпускаешь калькуляторы, которые показывают ошибку при переполнении (если в результате больше 8 цифр перед запятой), но каждая операция на них занимает 2 секунды.

Соседняя контора выпускает калькуляторы, которые при переполнении показывают мусор (UB), но каждая операция на них занимает 1 секунду.

Какие калькуляторы будут удобнее для бухгалтеров, и будут лучше продаваться?

Какие калькуляторы будут удобнее для бухгалтеров, и будут лучше продаваться?

лєбєдь красівий, як лєбєдь, а обізяна, как обізяна

Будет спрос и на калькулятор, который считает как попало, и который считает точно (следуя правилам базовой арифметики, их давно уже формализовали и никакой однозначности не осталось).

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

У большинства бухгалтеров не бывает восьмизначных чисел.

WAT?
В мене стипендія була семизначним числами

640K ought to be enough for anybody

Чому це не можна? Сішечка так і робить, для перформансу, плюси, мабуть, теж.
UB це ж, наприклад, «ми не контролюємо переповнення при додаванні двох чисел, якщо процесор не контролює». Тобто behavior є undefined тільки до того моменту, поки ми не знаємо, на якому залізі ми його запускаємо.

Чому це не можна?

Потому что обычно программы пишут чтоб у них было _определенное_ поведение, а не _неопределенное_ поведение.

UB це ж, наприклад, «ми не контролюємо переповнення при додаванні двох чисел, якщо процесор не контролює».

Нет. UB это:

stackoverflow.com/...​vior-erase-the-hard-drive

To emphasize that the results of undefined behavior are not predictable and may be very unpleasant, experienced C++ programmers often say that programs with undefined behavior can erase your hard drive.
Тобто behavior є undefined тільки до того моменту, поки ми не знаємо, на якому залізі ми його запускаємо.

Ты все еще не догоняешь в чем СУТЬ неопределенного поведения и что-то додумываешь чтоб заполнить пустоту в ментальной модели. Вместо этого нужно не додумывать, а просто принять за истину то, что ты не можешь знать что происходит при неопределенном поведении программы.

Вот программа:

#include <stdio.h>
#include <stdlib.h>

void __attribute__ ((noinline)) add(int a, int b) {
    if (a < 0 || b < 0) {
        exit (1);
    }

    int result = a + b;
    if (result >= 0) {
        printf("RESULT IS POSITIVE: %d\n", result);
    } else {
        printf("RESULT IS NEGATIVE: %d\n", result);
    }
}

int main() {
    add(0x7FFFFFFF, 0x1);
}

Ее будем запускать на x86-64 процессоре (конкретная модель не важна; если лично тебе важна, можешь выбрать любую модель процессора). Ты знаешь на каком железе программа будет запускаться, видидшь код программы, скажи что именно она выведет на экран (только без читерства, программу не запускать).

Ты все еще не догоняешь в чем СУТЬ неопределенного поведения и что-то додумываешь чтоб заполнить пустоту в ментальной модели. Вместо этого нужно не додумывать, а просто принять за истину то, что ты не можешь знать что происходит при неопределенном поведении программы.

О да, великий гуру, я не доганяю, принеси мені світло знання.

To emphasize that the results of undefined behavior are not predictable and may be very unpleasant, experienced C++ programmers often say that programs with undefined behavior can erase your hard drive.

І що? Це якісь загальні слова, які абсолютно не торкаються суті невизначенної поведінки. Те, що ти їх наводиш, свідчить про те, що ти не теж суті не розумієш.

Ее будем запускать на x86-64 процессоре (конкретная модель не важна; если лично тебе важна, можешь выбрать любую модель процессора). Ты знаешь на каком железе программа будет запускаться, видидшь код программы, скажи что именно она выведет на экран (только без читерства, программу не запускать).

Як тільки ми знаємо модель (або тип) процесора, поведінка перестає бути невизначеною. Тобто, undefined behaviour це не «програма працює рандомно, і ми не можемо сказати, як», а «программа працює по-різному на різних процесорах або системах».

Як тільки ми знаємо модель (або тип) процесора, поведінка перестає бути невизначеною.

Вот модель процессора:
Processor: Intel® Core™ i7-10610U CPU @ 1.80GHz 2.30 GHz

Код я выше привел: dou.ua/...​rums/topic/42888/#2607395

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

Від того, що я не можу сказати, що воно видасть, поведінка не стає undefined. Суть у тому, що ти можеш запустити цей код (або глянути у доку процесора), побачити результат, і такий самий результат буде на усіх сучасних x86 (x64) Intel та AMD процесорах.

программа працює по-різному на різних процесорах або системах«

Ага, но так работало где-то в 80-х годах.

«програма працює рандомно, і ми не можемо сказати, як»

А вот так работает в 2020-х годах.

Від того, що я не можу сказати, що воно видасть, поведінка не стає undefined.

От того, что вам кажется, что поведение определено, оно не становится определенным.

Если запустить мой пример, то логично было бы предположить, что вне зависимости от того как работает ALU на процессоре (даже игнорируя тот факт, что ALU на всех современных процессорах сложение работает одинаково), мы получим один из вариантов:

1) какую-то ошибку (CPU trap, hardware exception, software panic)
2) «RESULT IS NEGATIVE: some_negative_value»
2.1) бонусные очки если это будет «RESULT IS NEGATIVE: −2147483648», ибо это именно то, что в теории должен посчитать любой ALU при сложении 32-х битных знаковых 0×7FFFFFFF и 0×1.
3) «RESULT IS POSITIVE: 0»
4) «RESULT IS POSITIVE: some_positive_value»

Теперь на самом деле запускаем код и видим: godbolt.org/z/aPnKo6zKf

RESULT IS POSITIVE: -2147483648

Смотря на вывод, можно предположить, что программа считает, что:
1) result = −2147483648 , что в принципе верно, если CPU сделал wrapping overflow по правилам twos complement.
2) (result >= 0) == true, что в принципе неверно, так как result = −2147483648, и на любом CPU отрицательное число должно быть меньше нуля.

На всякий случай, в этом коде нет никаких «хаков», используются стандартные настройки компилятора (последний gcc в release), в самом компиляторе нет багов, которые я бы тут использовал (вернее, баги скорее всего есть, но они не относятся к тому что я демонстрирую). Для того, чтоб иметь подобную хрень в коде не нужно извращаться и пытаться написать что-то стремное, тут даже нет ничего реально «опасного» типа манипуляции указателями, обычная математика и ничего больше.
В логике программы нет никаких ошибок (не считая, разумеется, того, что программа вызывает неопределенное переполнение при переполнении знаковых чисел из-за чего она в принципе является некорректной с точки зрения компилятора).

Происходит (скорее всего, ибо чтоб сказать точно, нужно разбираться во всех тонкостях работы самого GCC, в чем реально разбираются, наверное, дестяки людей):

void __attribute__ ((noinline)) add(int a, int b) {
    if (a < 0 || b < 0) {
        exit (1);
    }

^^^
Эта проверка говорит компилятору, что числа a и b положительные (или равны 0), иначе мы просто выходим из программы.
    int result = a + b;
^^^
Это говорит компилятору о том, что числа нужно сложить.
    if (result >= 0) {
        printf("RESULT IS POSITIVE: %d\n", result);
    } else {
        printf("RESULT IS NEGATIVE: %d\n", result);
    }
^^^
Эта проверка на то, является ли результат сложения положительным или нет. И тут компилятор «вспоминает», что перед этим мы проверили a и b, и они положительные. А при сложении положительных чисел всегда получается положительное, верно? Значит, этот if блок эквивалентен
    if (true) {
        printf("RESULT IS POSITIVE: %d\n", result);
    } else {
        printf("RESULT IS NEGATIVE: %d\n", result);
    }
, т.е. в итоге метод сводится к:
void __attribute__ ((noinline)) add(int a, int b) {
    if (a < 0 || b < 0) {
        exit (1);
    }

    int result = a + b;
    printf("RESULT IS POSITIVE: %d\n", result);
}

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

Значит программа всегда будет писать «RESULT IS POSITIVE: ...»

И тут мы скажем «погоди, но при переполнении может получиться отрицательнео число!»

И компилятор скажет «Что? При переполнении? Нет, не может, переполнение знаковых чисел это неопределенное поведение, ты что стандарт не читал, корректная программа не может вызвать неопределенное поведение, значит, переполнение никогда не произойдет и отрицательное число никогда не получится.

«Подожди, но отрицательное число ПОЛУЧАЕТСЯ, посмотри на вывод: RESULT IS POSITIVE: −2147483648». В результате логика программы некорректна, ты неправильно скомпилировал. Почему нельзя дать процессору делать то что он делает и не ломать логику метода?

«Оно как бы да... но по стандарту переполнение знаковых чисел — это неопределенное поведение, значит, если произошло переполнение, мы можем делать что хотим, о корректности и логике можешь забыть. Я бы мог оставить эту проверку, но это добавит дополнительный бранч в процедуру и похерит мои бенчмарки. Все конкуренты делают ту же самую оптимизацию, я не хочу проигрывать на бенчмарках, так что сори, как я уже сказал, если программа вызвала переполнение знаковых чисел, она вызвала неопределенное поведение, она некорректна, иди читай стандарт и фикси код».

Никогда нельзя допускать UB.
Чому це не можна?

ответ понятен?

Ох уж ці бурі в стакані.
1. 99.99999% чисел що використовує программа ніколи не будуть вище за мільярд. Тому що це доволі велике число, а зазвичай integer то якись Count або счотчик в масиві.
2. Єдиний поширений випадок коли нам потрібно додавати чи перемножати дуже великі цілі числа між собою — це підрахунок хеш коду в хеш функції.
І UB переповнення там навіть корисне.
Воно працює швидко та навіть є частиною алгоритму.

Тож з чим ти боришся, Артьом?

Тож з чим ти боришся, Артьом?

С ветряными мельницами 🤡

99.99999% чисел що використовує программа ніколи не будуть вище за мільярд.

Ну... оцінка трохи завищена, у мене був випадок, коли я використовував uint256_t саме через переповнення. Але так, ця проблема зазвичай перебільшена.

99.99999% чисел що використовує программа ніколи не будуть вище за мільярд.

В ембеддеді в кожному першому проекті буде лічильник для таймстампів в мілі- чи мікросекундах, який в 32х-бітному варіанті буде переповнюватись через 49 днів і 71 хвилину, відповідно.
На вже двох проектах, для профілювання нерівномірності приходу мережевих пакетів доводилось використовувати uint128_t, бо алгоритми для середньоквадратичного значення або дають катастрофічні помилки округлення, або потребують запису таймстампів у пам’ять замість обробки на місці, або наївного «сума квадратів мінус квадрат суми», яке потребує великих цілих чисел для проміжних операцій, бо навіть 64хбітні за годину можуть переповнитись.

І баги з переповненням зустрічаються часто і б’ють користувачів боляче: www.netways.de/...​le-to-uptime-counter-bug

На вже двох проектах, для профілювання нерівномірності приходу мережевих пакетів доводилось використовувати uint128_t, бо алгоритми для середньоквадратичного значення або дають катастрофічні помилки округлення, або потребують запису таймстампів у пам’ять замість обробки на місці, або наївного «сума квадратів мінус квадрат суми», яке потребує великих цілих чисел для проміжних операцій, бо навіть 64хбітні за годину можуть переповнитись.

І баги з переповненням зустрічаються часто і б’ють користувачів боляче:

це дуже цікава проблема цілком реальності і якраз прихід чатжпт може знаменувати ще більші проблеми у цьому

справа тут у тому що «програмісти» вимушено беруться «робити математику» яку вони не розуміють але це нормально але так само існує такий самий розрив з боку самих математиків які чудово розуміють математику але суто абстрактну без врахування конкретної чи узагалі імплементації

цікава тут річ що спеціально навчені люди займатися саме цим на стику вже давно існують вони називаються інженери )) але чомусь саме у програмуванні у нас на сьогодні існує розрив який за усіма ознаками буде погіршуватися привіт боінг 737 мах

В ембеддеді в кожному першому проекті буде лічильник для таймстампів в мілі- чи мікросекундах, який в 32х-бітному варіанті буде переповнюватись через 49 днів і 71 хвилину, відповідно.

І що поганого що воно переповнилося і вийшло в краш.
Ти вважаєш що краще перевіряти діапазон чисел процессору на кожній ітерації ? Це буде більше 4.2 млрд пустих перевірок числа на діапазон. Такий лаг тобі дійсно потрібен на ембедед залізі?

І що поганого що воно переповнилося і вийшло в краш.
Ти вважаєш що краще перевіряти діапазон чисел процессору на кожній ітерації ? Це буде більше 4.2 млрд пустих перевірок числа на кожному інкременті. Такий лаг тобі дійсно потрібен на ембедед залізі?

см. примечание «раньше были инженеры теперь их нет остались одни программисты» ))

т.е. не кому подумать "так наша железяка планирует работать ... хм ок вечно давай предусмотрим и построим ей специальный расчётный цикл maintenance который будет просто сбрасывать все эти потенциально наполненные переполнения вместо

більше 4.2 млрд пустих перевірок числа на кожному інкременті. Такий лаг тобі дійсно потрібен на ембедед залізі?

но инженеров больше нет селяви просто бизнес ))

На Б777 здається була проблема з переповненням, і треба було живлення на літаку вимикати кожні кілька місяців.

Я навів приклад з SSD — у когось, наприклад, буде стояти RAID, який розвалиться одномоментно через те що «крашнуться» всі диски одночасно.
До речі, канонічна історія з Therac-25 en.wikipedia.org/wiki/Therac-25 — те теж про те, що true + 1 через переповнення стає false

Я навів приклад з SSD — у когось, наприклад, буде стояти RAID, який розвалиться одномоментно через те що «крашнуться» всі диски одночасно.

о богі хтось строїть raid на ssd ))

raid на ssd ))

Що не так? Чи ви про ZFS/etc.?

Навіть на RAM строять RAID (але тоді звуть RAIM).

І що поганого що воно переповнилося і вийшло в краш.
Ти вважаєш що краще перевіряти діапазон чисел процессору на кожній ітерації ? Це буде більше 4.2 млрд пустих перевірок числа на діапазон. Такий лаг тобі дійсно потрібен на ембедед залізі?

Для начала: там где ожидается переполнение целого числа, не использовать типы, которые при переполнении вызывают неопределенное поведение. В сишечке такие типы есть (например, беззнаковые, для которых поведение при переполнении строго прописано). Тем более для таймстампа, где сам бог велел использовать unsigned.

Для начала: там где ожидается переполнение целого числа, не использовать типы, которые при переполнении вызывают неопределенное поведение.

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

В ріалтаймі потрібен детермінізм. Швидкість після того.

І скільки такого коду на проекті у %?

Приблизно весь, що покладається на роботу з часовими інтервалами, які несподівано можуть мати TIME PARADOX?
Навіть якщо брати готові системні ліби, неодноразово бачив (і сам робив) баги на конвертації результату ftime() або clock_gettime() в часовий інтервал.

Ну от в телефонії щось того коду з інтервалами була лише імплементація таймерів, один раз написана на старті. Себто, менше 1% коду.

В ембеддеді в кожному першому проекті буде лічильник для таймстампів в мілі- чи мікросекундах, який в 32х-бітному варіанті буде переповнюватись через 49 днів і 71 хвилину, відповідно.

Так там воно і використовується з урахуванням переповнення лічильника. Тобто треба відлік якихось таймаутів, вони точно не будуть в мікросекундах аж 49 днів. Ну а uptime рахувати треба вже в секундах, вистачить на 136 років )

1. 99.99999% чисел що використовує программа ніколи не будуть вище за мільярд. Тому що це доволі велике число, а зазвичай integer то якись Count або счотчик в масиві.

i.imgflip.com/7i69u5.jpg

2. Єдиний поширений випадок коли нам потрібно додавати чи перемножати дуже великі цілі числа між собою — це підрахунок хеш коду в хеш функції.
І UB переповнення там навіть корисне.

Переполнение — да. UB — нет. Собственно, не случайно криптография почти эксклюзивно построена на беззнаковых целочисленных, поскольку для них переполнение — допустимая операция с определенным поведением*

* как обычно в С/С++, за исключением случаев, когда происходит integer promotion и умножения якобы беззнаковых приводит к неопределенному поведению.

Воно працює швидко та навіть є частиною алгоритму.

Для того, чтоб работало быстро, не нужно вызывать UB

Тож з чим ти боришся, Артьом?

С безграмотностью среди молодежи.

криптография почти эксклюзивно построена на беззнаковых целочисленных, поскольку для них переполнение — допустимая операция с определенным поведением*

Ну поскольку mpz построен как лёгкий враппер вокруг mpn (и точно так же в аналогах), это в криптографии не имело бы существенного недостатка. А вот то, что преимущества дополнительного кода теряются на длинной арифметике — более коренная причина.

С безграмотностью среди молодежи.

щасти ))

І що, на іншому компі з таким самим процесором (або з іншим Інтел процесором) воно по-іншому себе буде вести? Або може там рандом, типу сьогодні одне, завтра інше?

І що, на іншому компі з таким самим процесором (або з іншим Інтел процесором) воно по-іншому себе буде вести?

Во-первых, тебе показывают пример, где простейшая программа на полном серьезе начинает думать, что negative > positive, фундаментальная дырка в элементарной логике, начинает исполняться НЕ ТОТ КОД, который ты написал. Это элементарный пример, который легко протестировать и продемонстрировать, что выполненая логика отличается от той, которая была закодирована. В реальных прогарммах эти вещи протестировать сложно или нельзя (юнит тесты могут весело проходить, а в продакшене в коде будет происходить что-то абсолтно другое).

Тебя волнует только то, воспроизводимо ли это всегда на одном и том же процессоре, и если до, то збс, можно жить?

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

Когда ты пишешь на С/С++, ты не пишешь для какого-то конкретного процессора (как можно было бы ожидать от языка низкого уровня), даже если ты знаешь на каком процессоре будет выполняться код, а для некой абстрактной виртуальной машины, описанной в стандарте С/С++. Компилятор будет следить за тем, чтоб поведение прогарммы соответствовало тому как если бы она выполнялась на той абстрактной машине.

Або може там рандом, типу сьогодні одне, завтра інше?

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

Та спокійніше, ти мені вже пʼяте повідомлення доводиш щось своє, я лише хотів зрозуміти, чи існує невизначена поведінка на рівні асемблера та скомпільованої програми. Логіка підказує, що ні, але ти так завзято заперечуєш, що я засумнівався.

Во-первых, тебе показывают пример, где простейшая программа на полном серьезе начинает думать, что negative > positive

За приклад дякую

Тебя волнует только то, воспроизводимо ли это всегда на одном и том же процессоре, и если до, то збс, можно жить?

Мене хвилює суть терміна.

Невизначена поведінка коду — поведінка, яка може змінюватися залежно від таргет платформи, версії та флагів компілятора.

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

Розумію

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

Це ж може бути лише, наприклад, при доступі за границями буфера, або під час use after free? Переповнення ж має бути детерміноване на одній і тій же системі?
(Я запитую, а не сперечаюсь, якщо що)

Мене хвилює суть терміна.

Невизначена поведінка коду — поведінка, яка може змінюватися залежно від таргет платформи, версії та флагів компілятора.

Понятие неопределенного поведения более широкое, чем то, которое ты описал. Но формальный ответ, если он тебе нужен — да, оно может меняться в зависимости от вещей, которые ты указал. Также оно может меняться в зависимости от других вещей. Например, оно может меняться если в .c файле в конце добавить лишний перенос строки.

Це ж може бути лише, наприклад, при доступі за границями буфера, або під час use after free?

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

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

Переповнення ж має бути детерміноване на одній і тій же системі?

Я думаю ошибка в том, что ты думаешь в контексте «как мой код будет выполняться на процесоре».

Ты не пишешь код для процессора. Ты пишешь код для абстрактной машины, которая описана в стандарте С/С++. У тебя на процессоре переполнение знаковых чисел работает определенным образом, в стандарте С/С++ переполнения знаковых чисел не существует.

Выше/ниже мой пример на Rust, который демонстрирует, что в конкретном примере при переполнении чисел процессор _вообще ничего не делает_, инструкция сложения чисел (которая могла могла бы переполнить число в рантайме) ответствует программе.

я лише хотів зрозуміти, чи існує невизначена поведінка на рівні асемблера та скомпільованої програми.

Ще раз:

1. Не впадаючи в НЛП, але: «Це тебе не цікавить» або «це не те запитання, що ти маєш ставити».

Чому так — а тому, що ти можеш, зрозуміло, розібрати результат перекладу транслятором (компілятором) джерельного коду в асемблер... один раз. два. три. Але це означає, що ти контролюєш результат роботи транслятора і тим самим фактично виконуєш за нього работу. І ти так зможеш зробити тільки на малій програмі, бо на великій у тебе принципово не вистачить ресурсів це робити, тим більше після кожної зміни джерельного коду. І чим більше програма, тим більше імовірність, що твоє порушення правил (може, про які ти просто не знаєш) призвело до того, що в асемблер странслювалось зовсім не те, що ти хотів.

Це зрозуміло, сподіваюсь? Вимоги, що транслятор перероблює твій код в щось, що виконує те ж саме, що ти написав — вони зʼявились не на пустому місці.

2. Якщо розглядати саме так, як ти питаєш тут: да, є випадки, коли навіть маючи асемблерний/машинний код ти не можеш вважати, що виконується саме те, що ти хотів написати.

Але більшість реальних випадків — це коли ти щось недостатньо зробив в міжнитковій/міжпроцесній синхронізації. Ти думаєш, що тобі не змінили значення змінної, а її змінили — чи навпаки, не змінили те, що мали (або невчасно).

Решта таких випадків — некоректне поводення з кешом (особливо з памʼятю пристроїв), некоректна взаємодія з пристроями через їх порти, або навіть помилки в процесорі — це вже дуже спеціалізовані випадки і їх треба розглядать окремо.

Логіка підказує, що ні, але ти так завзято заперечуєш, що я засумнівався.

Твоя логіка не враховує, вже сказав, мультітредінгу і мультіпроцесінгу. А вони зараз на кожному розі.

Невизначена поведінка коду — поведінка, яка може змінюватися залежно від таргет платформи, версії та флагів компілятора.

Можна і так сказати. Але тут пропущене критичне: що помилка в слідуванні умовам мови в одному місці може шляхом, який ти не передбачиш в силу обмеженості людських ресурсів, вплинути на інше місце коду, причому радикальним чином.

Це ж може бути лише, наприклад, при доступі за границями буфера, або під час use after free? Переповнення ж має бути детерміноване на одній і тій же системі?
(Я запитую, а не сперечаюсь, якщо що)

Ні, не детерміноване. Наприклад, ASLR, яке увімкнуте за замовчуванням на майже всіх сучасних системах, принципово дає різні результати у різних випадках. Для памʼяти даних можуть бути застосовані такі ж підходи: на старті процесу чи компоненту деякі параметри ініціалізуються з генератора випадкових чисел.
Або тут дивіться про заповнення памʼяти — можна форсувати нулі навіть при malloc(), можна заповняти байтами 0xa5, можна не повертати звільнені шматки.
Це все зневаджувальні міри, спеціально призначені для пошуку помилок, але для повноти їх треба згадати.

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

volatile ?

volatile ?

Чтобы ещё ухудшить?

«Подожди, но отрицательное число ПОЛУЧАЕТСЯ, посмотри на вывод: RESULT IS POSITIVE: —2147483648». В результате логика программы некорректна, ты неправильно скомпилировал. Почему нельзя дать процессору делать то что он делает и не ломать логику метода?
это неопределенное поведение, значит, если произошло переполнение, мы можем делать что хотим, о корректности и логике можешь забыть. Я бы мог оставить эту проверку, но это добавит дополнительный бранч в процедуру и похерит мои бенчмарки.

=>

volatile ?

=>

В результате логика программы некорректна, ты неправильно скомпилировал. Почему нельзя дать процессору делать то что он делает и не ломать логику метода?

fixed !

ЗЫ: нах ты отвечаешь не читая контекст ты что член комиссии по стандартам прямо дежавю ))

Я сейчас ссылки не найду, но читал, что clang и через volatile находит пути вычислить и оптимизировать. Поэтому

ты что член комиссии по стандартам прямо дежавю

вполне читаю :)
Но что делать, когда почвы под ногами нет?

volatile ?

Если хочется чтоб было медленно, можно и volatile. Только тогда зачем на C++ писать если с таким же успехом можно было и на питоне?

Если хочется чтоб было медленно, можно и volatile. Только тогда зачем на C++ писать если с таким же успехом можно было и на питоне?

на питоне есть volatile ?

ЗЫ: под питоном внутре сишечка даже интерфейсики есть всё чики пики ))

Если хочется чтоб было медленно, можно и volatile. Только тогда зачем на C++ писать если с таким же успехом можно было и на питоне?

Ну, в проблемных местах на C/C++ с volatile будет всё равно быстрее, чем на питоне :)

Volatile — вроде бы надёжный хак, да (не уверен за последние потуги авторов компиляторов, но им должны были, по идее, постучать по голове, чтобы засунули всё обратно).
Но если его слишком часто применять... да и вообще неудобно всё время видеть такое.

Хотя то, что я давно талдычу — что в типовой программе 95% кода вообще не требует оптимизаций ни на не-переполнение, ни на алиасинг, ни на непустой указатель, ни на ещё десяток формальных UB — как раз вполне может превратиться в политику «volatile автоматом на всё что не локальная переменная».

Ну мучать шину памяти из-за volatile в 95% кода на одних ядрах сильно тормознет реал-тайм / перформанс сенситив код, который бежит на других ядрах, ИМО.

Это если доступ к памяти совсем не кэшируется?

Так volatile разве не запрещает кеширование? Или он только запрещает использование регистра?

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

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

формально такого требования нет к.м.к. это требование относилось к историческим временам когда volatile обозначали «переменную в адресном пространстве куда может быть случаться что-то не управляемое самим этим процессором конкретно ядром»

ЗЫ: кстати хороший вопрос как это «работает для программиста» сейчас в процессорах с 3-уровневыми кешами

Так volatile разве не запрещает кеширование? Или он только запрещает использование регистра?

volatile не «запрещает» ни чего )) т.е. «запрещает» )) он буквально «запрещает» _компилятору_ делать что-либо с этой переменной из евонной компилятора хитрой оптимизации варианты которых описаны здесь в треде среди прочего

типа «а ну эта же ж переменная не может быть 0 ну и х. с ней нах. не надо значит проверку делать (написанную программистов в тексте кода) и сразу вырасли 100500 в рейтинге компиляторов оптимизаторов»

Ну мучать шину памяти из-за volatile

ты имеешь в виду на volatile делается cache write-through ?

Не знаю. Всегда думал, что volatile идет мимо всех кешей.

Наскількі я знаю, volatile для того щоб ми завжди читали з пам’яті а не просто замінили на константе значення. Але программа робить просте звертання до адреси, і нічого не знає про кеші, відповідно використання кешів зберігається якщо змінна в кешуємій області пам’яті.

Всегда думал, что volatile идет мимо всех кешей.

Нет такого правила и не может быть.

«Мимо кэша» может быть только в смысле кэша значения переменной _для компилятора_(!)
То есть если у тебя есть последовательность типа

x1 = v;
x2 = v;

и v была volatile — без дополнительных действий компилятор даже с -O0 может за счёт SSA превратить это в чтение одного и того же значения в регистр и уже оттуда использовать как значение в этом месте для x1 и x2.
С volatile это запрещается и превращается в гарантированно раздельные чтения.
То же и для записи.

В C|C++ дополнительно есть требование строго порядка всех volatile операций — они _компилятором_ не переставляются друг с другом (но могут переставляться с другими операциями, в которых не участвует volatile), что однако ничего не говорит про возможность перестановки процессором.

В общем, странная штука:)
но от супероптимизаций таки защищает.

Як тільки ми знаємо модель (або тип) процесора, поведінка перестає бути невизначеною.

Ні, не перестає.
Класичний приклад:

#include <iostream>
int main()
{
    int x = 27;
    for(int i=0; i < 10; ++i)
    {
        std::cout << i << " : " << i*1000000000 << " : " << x << std::endl;
        if(x==1) break;
        x = x%2 ? x*3+1 : x/2;
    }
}

Один і той же x86-64.

$ g++ -o r r.cxx ; ./r | tail -1
9 : 410065408 : 71
$ g++ -o r r.cxx -O; ./r | tail -1
111 : -669149696 : 1
(GCC 9.4.0)
Тобто, undefined behaviour це не «програма працює рандомно, і ми не можемо сказати, як»

Саме — рандомно, що компілятору прийде в його «голову» залежно від 100500 факторів, які неможливо передбачити.

а «программа працює по-різному на різних процесорах або системах».

Це зветься implementation-defined.

Вчіть матчастину ©.

Ні, не перестає.

Тобто, скомпільована програма працює по-різному на одному типу процесора? Чи після компіляції «undefined» зникає?

Тобто, скомпільована програма працює по-різному на одному типу процесора?

Саме так.

Чи після компіляції «undefined» зникає?

Цього не зрозумів.

Дякую, я зрозумів. Я думав, що якщо ти програму скомпілював, то вона працює однаково на однакових типах процесора (ну якщо брати більш-менш детерміновані речі типу переповнення). Зрозуміло, що, наприклад, доступ до неініціалізованої памʼяті то чистий рандом.

Тобто, скомпільована програма працює по-різному на одному типу процесора?

Це виходить один і той же асемблер працює по-різному?

що якщо ти програму скомпілював, то вона працює однаково на однакових типах процесора

Саме так.
А ось компіляція може перетворити програму на щось таке, про що ти і не здогадувався.

Це виходить один і той же асемблер працює по-різному?

Асемблер — одинаково.
Компілятор — ні.

Це виходить один і той же асемблер працює по-різному?

загалом так можна виходити ще й з того що один і той же асемблер може спрацювати по різному

у тебе процесор 2-го покоління а вони запустили код на процесорі 7-го покоління асемблер той самий? а ну да там же ж не асемблер навіть а машинний код )) і архітектура заліза

у тебе процесор 1 одне ядро і ти перевіряв також на 2 і не бачиш нічо такого щоби

один і той же асемблер працює по-різному

а вони запустили код на процесорі де 39 ядер частина з яких «спеціальні»

у тебе процесор 1 та 4 ядра а вони запустили код на системі де 4 процесори по 1 ядра на кожен і між ними оперативка шариться спеціальним чином або numa або не numa

у тебе процесор 1 та 4 ядра а вони запустили код на системы де 4 процесори по 39 ядер і твій код саме запускався на тому сокеті який оце згорів і саме міняється на гарячу

Це виходить один і той же асемблер працює по-різному?

тож питання такі цікаве а нашо вапчє так писать який саме поінт саме у тому щоб так вапчє писать? що саме має відповісти це питання?

Тобто, скомпільована програма працює по-різному на одному типу процесора? Чи після компіляції «undefined» зникає?

послухай а сірьозно а який сенс чи мораль чи саме технічний зміст у цьому питанні? що саме ти хочеш від цього питання отримати?

мені справді цікаво бо там тобто тут згори на щаблях «кар’єрних драбин» на яких маю здебільшого вимушено то тут дмуть огидні протяги «людського фактору» який вже виходить слід не тільки враховувати а ще й підлаштовуватися бо виявляється прото бити людей не можна я перевіряв )) тож мені цікаві усі аспекти «як люди собі думають» ні чого особистого просто бізнес

Я лише хочу для себе зрозуміти. Бо я завжди думав, що андефайнед — він лише про код, який генерується компілятором. Але читаючи тред мені здалося, що цей андефайнед може зʼявлятися і в уже скомпільованій програмі (ні).

Але читаючи тред мені здалося, що цей андефайнед може зʼявлятися і в уже скомпільованій програмі (ні).

може

читання та писання змінної з різних потоків ub

Ну, як на мене, це трохи інша річ, так і на генератор випадкових чисел можна сказати, що це ub.

Читання змінної з різних потоків, це, скоріш, випадковий вибір з комбінації інструкцій з обох потоків.

так і на генератор випадкових чисел можна сказати, що це ub.

з якого це дива?

Читання змінної з різних потоків, це, скоріш, випадковий вибір з комбінації інструкцій з обох потоків.

а ну так тепер же ж у нас ще й модель пам’яті є )) ну так доречі за моделлю пам’яті все що між memory barriers то є офіційно ub since c++11

згідно моделі його навіть може не бути

Читання змінної з різних потоків

а може бути )) чистий ub як частина стандарту

Класичний приклад:
    for(int i=0; i < 10; ++i)
    {
        std::cout << i << " : " << i*1000000000 << " : " << x << std::endl;

=>

справа тут у тому що «програмісти» вимушено беруться «робити математику» яку вони не розуміють але це нормально але так само існує такий самий розрив з боку самих математиків які чудово розуміють математику але суто абстрактну без врахування конкретної чи узагалі імплементації

цікава тут річ що спеціально навчені люди займатися саме цим на стику вже давно існують вони називаються інженери )) але чомусь саме у програмуванні у нас на сьогодні існує розрив який за усіма ознаками буде погіршуватися привіт боінг 737 мах

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

Цель программиста — не допускать таких условий, или знать, какое нестандартное поведение в данном случае проявит его программа.

При зміні платформи треба брати бабло і вмикати мосх. Ібо тру

А ще — розпихати усюди купу асертів, щоб воно не глючило, а гучно падало.

Асерти теж змінюють поведінку компілятора, часто маскуючи проблеми:(

Ну я люблю ship with asserts enabled. Тоді іншої поведінки просто не буває)

Асерти теж змінюють поведінку компілятора, часто маскуючи проблеми:(

проблеми коду чи проблеми компілятора ))

ЗЫ: ет були старі часи і був якийсь 8-бітний процесор з якимось фірмовим сішним компялятором ну і свої обмеження платформи як без них то там в усій схемі була known issue іноді і то досить часто з огляду на поступове накопичення бази та розміру коду там траплялася «не передбачувана поведінка ...» радше за все компілятора )) но єто нє точно ... тож ця known issue вирішувалася турботливо розставленими по всьому коду nop() яка буквально nop

ЗЫ: а ну і да вже 16-бітна платформа яка була сумісно програмно (на рівні сішного коду звісно) такої known issue вже не мала

ЗЫ: а ну і да повертаючись до «код працює однаково на усіх процесорах» а скіко було цікавого у переписуванні коду який «32 біт достатньо для всіх» коли почали виходити 64-бітні платформи і так само те саме було ще у сиву давнину коли код переходив з 16 біт на 32 )) якщо хто не знає то була 16-бітна вінда хє хє хє

Були до CPUID техніки визначення типа процесора за багами в різних реалізаціях 8088, 8086, 80286, 80386SX і DX...

В мене асерти в продакшині

І як з виробністю кода?

Якщо це питання до мене, то це з звичайний IF на перевірку вхідних параметрів і проміжних результатів обчислень.

Звісно, що їх нема в критичних обчислень, де важливе лейтенсі. Але таких кусків не так вже й багато. Ну і юніт-тести на фільтри і всяке інше.

Ну... для мене дуже неприємне UB це інвалідація ітераторів при модифікації контейнера в STL. Інколи capacty контейнера достатньо, там програма поводить себе добре. А інколи трапляється realloc та ... Звісно, що намагаєшся такого уникати, але... життя бентежне...

для мене дуже неприємне UB це інвалідація ітераторів при модифікації контейнера в STL.

use the force of std::list luke ))

std::luke — контейнер, який не інвалідує свої ітератори

Ну... мені приємніше юзати справжні сішні двозв’язні кільцеві списки, бо при видаленні елемент списку не має знати, з якого контейнеру він видається.

Я джунів колись за таке кохав

ви плутаєте undefined behaviour and unspecified behaviour. ото друге і є залежність від платформи/компілятора. якщо по нормальному, то розробник компілятора у доках має писати як усі оті unspecified реалізуються. а от undefined це така собі чорна діра, її в коді має не бути (але вона часто є, на жаль).

Unspecified behaviour — поведінка не прописана у специфікації, але однакова на усіх платформах
Undefined behaviour — поведінка залежить від платформи, але однакова на однакових платформах

Чи я щось плутаю?

Чи я щось плутаю?

Да, плутаєте.

Undefined behaviour — поведінка залежить від платформи, але однакова на однакових платформах

Це зветься implementation-defined.

Unspecified behaviour — поведінка не прописана у специфікації, але однакова на усіх платформах

А такого взагалі нема.

“unspecified behavior

behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance”
(C99)
“in any instance”, зауважте.

І до купи:

“undefined behavior

behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements”
(там же)

use of a nonportable or erroneous program construct

Ну так все правильно, конструкція, яка працює по-різному в залежності від того, під яку платформу зібрана і з якими флагами компілятора.

Ну ок, я для себе зрозумів, дякую

Ну так все правильно, конструкція, яка працює по-різному в залежності від того, під яку платформу зібрана і з якими флагами компілятора.

А також від сусіднього коду, включаючи той, що підключений через ланцюг 100500 #include з бібліотек, про які ви не чули.

І найголовніше те, що вона може і не сама працювати по-різному, а і впливати на інші частини коду, в тому числі ті, про які ви і не здогадуєтесь!

Ну ок, я для себе зрозумів, дякую

ok:)

Ну... ось простий приклад UB:

int * ptr = malloc(100);
free(ptr);
some_call();
*ptr = 0xFACE0000;

при якому у пам’ять запишеться випадкове значення. Тут як пощастить, може ця пам’ять не буде аллокована, тому нічого не зміниться. А можливо ми змінимо якесь бойове значення. Можливо пошкодимо сам алокатор. Цей код може працювати по різному навіть у двох послідовних запусках програми.

а може тої сторінки пам’яті давно нема і викине глобальне виключення на bad access to the page ))

Ну... зазвичай brk рухає пам’ять лише уперед. Але і під Windows за рахунок фрагментації досить мало шансів, що певна сторінка буде звільнена. Тому шанси на segfault незначні.

Ну... зазвичай brk рухає пам’ять лише уперед.

Залишилось знайти хто його ще використовує :)
Постійно бачу як на ньому тільки rtld щось аллокує, а потім libc «так, у нас 8 ядер? мапимо 8 шматків по 64MB malloc’ом, щоб виділяти з них, кожному ядру своє».

Тому шанси на segfault незначні.

Але не 0.

Offtopic: браузер крашився на слабких смартфонах, коли закривав сторінку з великою картинкою. При цьому креші в випадкових місцях з різними стеками. При цьому дебагер не міг показати різні локальні змінні у багатьох стек фреймах.

Тиждень дебагу знайшов, що там була структурка, котра завідувала замапленою пам’яттю. Коли пам’ять мапилась, заповнювалися поля data_ та size_. Коли пам’ять анмапилась, після анмапу обнулялося поле data_ (вказівник на замаплену пам’ять). В деструкторі робився анмап.

Ітого: при закритті сторінки робився munmap(data_, size_), а при знищенні вкладки — ще раз munmap(NULL, size_). Коли в системі мало оперативи (старий смартфон), то оцим хвостом рейнджа [0; size_] зачіпляло сторінки стека і анмапило їх.

Нажаль, той код на той час переписали, і я не апстрімнув прикольний фікс.

Коли в системі мало оперативи (старий смартфон), то оцим хвостом рейнджа [0; size_] зачіпляло сторінки стека і анмапило їх.

оу повертаючись до наших сі++ програмістів якщо валити на єкзаменах то я ще страшенно люблю вапрос «де стек?» ))

ЗЫ: а ну да ще за багатопоточність було «можна оцінити межу кількості потоків які можемо запустити ну ось скажімо конкретна система 8 ядер 80 мегабайтів озу (озп?) то скіко можна?»

ЗЫ: доречі да останнє питання ще й з зірочкою на розуміння шо таке пам’ять вапчє і зокрема такі віртуальна пам’ять взагалі ))

У Rust для цього є, наприклад, unchecked_add doc.rust-lang.org/...​html#method.unchecked_add

Тобто, хочеш непереносимий код, але zero overhead — будь ласка

unchecked_add
Тобто, хочеш непереносимий код

Где написано, что unchecked_add непереносимый?

але zero overhead

zero overhead чего? Можете привести код, который показывает преимущество unchecked_add над аналогичным безопасным кодом (который не может вызвать UB)

Наскільки я розумію, звичайний + має defined behaviour, тобто видає паніку при переповненні, на будь-якій платформі, і це забезпечується генерацією додаткового кода (extra overhead).

unchecked_add, наскільки я розумію, знову ж, не додає таких перевірок, тобто реакція на переповнення буде залежати від того, на якому процесорі все це запускається.

Где написано, что unchecked_add непереносимый?

Він переносимий, але його поведінка відрізняється в залежності від платформи.

unchecked_add, наскільки я розумію, знову ж, не додає таких перевірок, тобто реакція на переповнення буде залежати від того, на якому процесорі все це запускається.

wrapping_add точно так же не делает никаких проверок

rust.godbolt.org/z/o7nzqofWe

ассемблерный код идентичный. Можете потыкать разные target и убедиться, что на любом CPU, под который можно собрать Rust, под эти фукнции генерируется одинаковый ассемблерный код.

Тогда в чем тогда на самом деле СУТЬ unchecked_add?

А де там таргет можна обирати? Щось усе потикав і не знайшов, тікі версію компілятора та його флаги бачу.

У попапі з вибору компілятора з набору інструкцій тільки amd64.
Я думаю, що на х86 unchecked_add і wrapping_add генеруют однаковий асемблер, бо процесор по дефолту врапає при додаванні.

Тогда в чем тогда на самом деле СУТЬ unchecked_add?

Як я розумію, просто використати інструкцію додавання процесора без додаткового коду.

А де там таргет можна обирати? Щось усе потикав і не знайшов, тікі версію компілятора та його флаги бачу.

Там где прописываются аргументы компилятора, добавляешь:

—target XXXXXX

Список поддерживаемых архитектур: doc.rust-lang.org/...​stc/platform-support.html

например

-O —target powerpc-unknown-linux-gnu

Я думаю, що на х86 unchecked_add і wrapping_add генеруют однаковий асемблер, бо процесор по дефолту врапає при додаванні.

Найди пример платформы, где генерируется разный ассемблер.

Як я розумію, просто використати інструкцію додавання процесора без додаткового коду.

нет, СУТЬ unchecked_add в том, что если unchecked_add вызывает переполнение, то поведение программы не определено.

Вот пример, где ассемблерный код будет отличаться, который показывает в чем СУТЬ unchecked_add:

rust.godbolt.org/z/hj864Eb41

«безопасное» wrapping_add

pub fn add_wrapping(a: u16) -> i32 {
    let a: i32 = a as i32;

    let result = i32::MAX.wrapping_add(a);

    result
}

example::add_wrapping:
        movzx   eax, di
        add     eax, 2147483647
        ret

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

«небезопасное» unchecked_add, которое вызывает неопределенное поведение:

pub fn add_unchecked(a: u16) -> i32 {
    let a: i32 = a as i32;

    let result = unsafe {i32::MAX.unchecked_add(a)};

    result
}

example::add_unchecked:
        mov     eax, 2147483647
        ret

Из ассемблерного кода видно, что программа игнорирует входящий аргумент, не делает никакого сложения и всегда возвращает 2147483647

Спросишь WTF? Почему ассемблерный код не делает то, что мы прописали в rust коде? Ведь по логике он должен «просто використати інструкцію додавання процесора без додаткового коду». Это не происходит по следующей причине:

1. Компилятор видит, что мы принимаем беззнаковое 16-битное число. Тут все хорошо
2. Конвертируем беззнаковое 16-битное в знаковое 32-битное число (компилятор понимает, что после конвертации мы гарантированно имеем или 0 или число, которое больше 0). Тут все хорошо
3. Добавлем сконвертированное число к i32::MAX используя unchecked_add.

А тут компилятор начинает додумывать "так, мы добавяем что-то к i32::MAX. Это что-то находится в диапазоне 0..=u16::MAX. Стоп, тут используется unchecked_add, т.е. если произойдет переполнение, то поведение не определено... Если добавить 0 к i32::MAX, то результат будет i32::MAX. Если добавить любое другое положительное число к i32::MAX, то происходит переполнение, ХАХАХА, а как мы знаем, при переполнении поведение не определено, значит ANYTHING CAN HAPPEN, значит, единственное корректное значение, которое можно передать в этот метод это 0, я должен поддерживать только этот случай, а i32::MAX + 0 == i32::MAX. Т.е. я даже не должен делать операцию сложения. Получается, это эквивалентно коду:

pub fn add_unchecked(_a: u16) -> i32 {
    i32::MAX
}

збс, сэкономили одну инструкцию.

Ты разумно возразишь:

Як я розумію, просто використати інструкцію додавання процесора без додаткового коду.

Комплиятор ответит: нет, посмори документацию на unchecked_add, там явно написано:

This results in undefined behavior when self + rhs > i32::MAX or self + rhs < i32::MIN, i.e. when checked_add would return None.

Где ты там прочитал про инструкции сложения процессора?

===========

и ты скажешь "ну хорошо, получается что вот теперь-то поведение ОПРЕДЕЛЕНО! Функция всегда возвращает i32::MAX.

Ответ, нет, оно все еще неопределено. Тот факт, что ты наблюдаешь какое-то поведение не делает его определенным.

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

rust.godbolt.org/z/5fjvKffT1

#![feature(unchecked_math)]

use std::hint::black_box;

#[inline]
pub fn add_unchecked(a: u16) -> i32 {
    let a: i32 = a as i32;

    let result = unsafe {i32::MAX.unchecked_add(a)};

    result
}

fn add_hello(a: u16) -> i32 {
    add_unchecked(a)
}

fn main() {
    let a: u16 = 1;
    println!("add_unchecked({a}) {}", add_unchecked(1));
    
    let a: u16 = 1;
    println!("add_hello({a}) {}", add_hello(1));
    
    // try commenting and uncommenting this line and 
    // see how result changes
    let a: u16 = black_box(1);
    println!("add_hello({a}) {}", add_hello(black_box(1)));
}

вывод:

add_unchecked(1) -2147483648
add_hello(1) 2147483647
add_hello(1) 2147483647

но при этом если закомментировать последнюю строчку, то получаем

add_unchecked(1) -2147483648
add_hello(1) -2147483648

Как видишь, результат unchecked_add может:
1) быть некорректным с точки зрения любого вида математики
2) может быть разным для одних и тех же входных параметров _в рамках одного и того же процеса_
3) результат unchecked_add может меняться в зависимости от того, что делает код, который идет ПОСЛЕ выполнения самого unchecked_add.
4) может меняться в зависимости от настроек компилятора, которые _не должны_ изменять поведение программы (например, включения/отключение инлайна функции)

Наскільки я розумію, звичайний + має defined behaviour, тобто видає паніку при переповненні, на будь-якій платформі, і це забезпечується генерацією додаткового кода (extra overhead)

В debug режимі — да.
В release він працює як truncating.

Реліз режим — зло :)

І це каже той хто зберігає асерти в коді :)
Ваш M != вашому ж M. Значить ли це, що isnan(ваш M) == true?

Як на NAN впливає release vs debug?

(В мене через NAN дрон впав був, вле це інша історія...)

Як на NAN впливає release vs debug?

Ну як ну як...
асерти — це зневадження.
Debug — це зневадження.
Якщо тільки Release але асерти — це x!=x.
Якщо x!=x, то x is NaN.
Проста логіка.

(В мене через NAN дрон впав був, вле це інша історія...)

Розкажіть :)

В мене в цирку був свій макрос SUCCEEDED/FAILED, який щось типу if (умова) { залогувати все що треба }.

Зараз на дронах асерт просто валить потік (точніше, логує помилку і впадає у вічний цикл зі сліпом).

А з NAN було таке, за що мені стидно. По-перше, воно раз проскочило на тестуванні, і я дозволив польоти до того, як його виловили. Ну і роздовбали дрона об асфальт з висоти 25 метрів. Був дубльований автопілот, але *щоб не було скачка при перемиланні автопілотів через різні очікувані швидкості і позиції при перемиканні автопілотів*, всупереч вимогам ізоляції ми дозволили синхронізацію швидкостей і біасів від активного автопілота до пасивного. Ну і NAN просочився і вбив все. Добре, що впало, а не полетіло в випадковому напрямку з випадковою швидкістю, а то я б тут напевне зараз не писав. Виправили, звичайно. Отаке...

асерти — це зневадження.
Debug — це зневадження.

Не зовсім. Асерти — це мінне поле, щоб баги бачити в коді там, де вони трапились, а не через пів-години ловити незрозумілу поведінку. І в релізі асерти — це 95% що програма замість некоректної поведінки впаде, і система її запустить заново чистеньку та здоровеньку.

Дебаг — це щоб простіше було читати код у відладці, коли з самого асерта не зрозуміла проблема.

І в релізі асерти — це 95% що програма замість некоректної поведінки впаде, і система її запустить заново чистеньку та здоровеньку.

то нє азєрті то вотчдогі ))

Ну я завжди думав, що вочдог — це той, хто перевіряє, що система не висить.

а як ти взнаєш що твій азєрт на справді заазєртівься а не висить? ))

і система її запустить заново чистеньку та здоровеньку.

і хто це зробить перезапустить програму як не вотчдог?

а як ти взнаєш що твій азєрт на справді заазєртівься а не висить? ))

А там або abort() або *(int*)0=0

і хто це зробить перезапустить програму як не вотчдог?

Супервайзер. Вочдогом називають регістр в процесорі, в котрий треба кожну секунду щось писати, щоб проц не бутнувся. Або потік чи системний таймер з такою ж функціональністю. Наприклад, актор, котрий збирає keepalive меседжі від усіх інших потоків системи. Якщо хтось пару разів підряд не надіслав меседж — логуємо та асертимо. І нас запустить супервайзор, що живе в іншому процесі.

Згоден, я надто узагальнив.

Знову, наприклад, як у Rust не ініціалізувати поле?

doc.rust-lang.org/...​em/union.MaybeUninit.html

Так, є assume_initialized, але unsafe. А це може буде overhead, тому що поле буле ініціалізовано пізніше.

Зачем гадать когда можно проверить? rust.godbolt.org/z/K1WfoxhrT

Ця ж поведінка не за замовчуванням, тому це різні підходи по дизайну мови. Тому я не можу сказати, що Rust взяв zero overhead у тому вигляді, який він є у C++. Не кажучи про те, що у C++ zero overhead пов’язаний з ООП, якого в Rust просто немає.

Ця ж поведінка не за замовчуванням, тому це різні підходи по дизайну мови. Тому я не можу сказати, що Rust взяв zero overhead у тому вигляді, який він є у C++.

Таки да, Rust не взял из С++ подход к неинициализированным значениям, а именно:

* Если переменная имеет примитивный типа (полученный в наследство от С), она будет неинициализирована (читать из нее — UB, куда ж без него)
* Если переменная является объектом, то будет вызван неявно дефолтный конструктор для инициализации. Если дефолтного конструктора нет (Hello() = delete), ошибка компиляции
* Если переменная имеет тип ссылки (&), ошибка компиляции.

збс подход

Не кажучи про те, що у C++ zero overhead пов’язаний з ООП, якого в Rust просто немає.

В Rust есть zero cost абстракции ООП doc.rust-lang.org/book/ch17-00-oop.html

Давайте так, моя теза що С++ нічого не дав іншим мовам програмування. Тобто усі унікальні фічі, які з’явилися саме у С++, далі цієї мови не пішли. Одна з таких фіч це zero overhead у simula like OOP. Саме її я вважаю помилкою проектування, бо коли у нас з’являється Simula like OOP, то ми сильно про економію тактів процесору ми вже не думаємо. Тому навіть підхід Delphi, де пам’ять під об’єкт просто зануляється та автоматично викликається деструктор, якщо щось трапилося навіть у конструкторі, мені подобається більше практичністю, ніж ситуація, коли будуть викликані лише ті деструктори, для яких виконалися конструктори.

У Rust немає Simula Like OOP, там є ADT. Тому я не схильний розглядати усі означені фічі як запозичення з C++. Так, у Rust можна досягти zero overhead на рівні unsafe коду. Але це нова концепція, як на мене.

Не дуже зрозуміло, що ви мали на увазі, але ООП у плюсах зовсім не zero overhead, а таблиця віртуальних методів, вказівник на неї у кожному інстансі, і так далі.

У Раст це все зʼявляється лише коли ми явно це вказуємо dyn Trait. А до тих пір у нього оверхед на рівні звичайного виклику функцій.

zero overhead доки нема віртуальних методів. З віртуальними методами також оверхед дуже малий — ідентифікатор класу в С був би енумом, але ті 3 чи 7 байтів (різниця в розмірі вказівника та енума) зазвичай не роблять різниці.

Тобто таблиця віртуальних методов не використовується, доки немає віртуальних методів?

Вона навіть не створюється, якщо в класу чи його батьків нема віртуальних методів. Себто, простий клас, навіть з наслідуванням, ідентичний структурі в С. Якщо є віртуальний метод — тоді першим членом класу є вказівник на таблицю. Якщо множинне віртуальне наслідування — тоді там стає складніше.

You can safely access a C++ object’s data from a C function if the C++ class:

Has no virtual functions (including inherited virtual functions)
Has all its data in the same access-level section (private/protected/public)
Has no fully-contained subobjects with virtual functions

If the C++ class has any base classes at all (or if any fully contained subobjects have base classes), accessing the data will technically be non-portable, since class layout under inheritance isn’t imposed by the language. However in practice, all C++ compilers do it the same way: the base class object appears first (in left-to-right order in the event of multiple inheritance), and member objects follow.

Furthermore, if the class (or any base class) contains any virtual functions, almost all C++ compilers put a void* into the object either at the location of the first virtual function or at the very beginning of the object. Again, this is not required by the language, but it is the way “everyone” does it.

If the class has any virtual base classes, it is even more complicated and less portable. One common implementation technique is for objects to contain an object of the virtual base class (V) last (regardless of where V shows up as a virtual base class in the inheritance hierarchy). The rest of the object’s parts appear in the normal order. Every derived class that has V as a virtual base class actually has a pointer to the V part of the final object.

isocpp.org/...​t-cpp-data-members-from-c

Одна з таких фіч це zero overhead у simula like OOP.

Це не Simula-like OOP. Simula-like OOP це коли у кожного класу власний «двигун» життя і між ними бігають повідомлення — структури з примітивних обʼєктів (уяві собі JSON).

С++ і аналоги — OOP на зовнішньому потоку виконання.

Таке враження, що ти сплутав Smalltalk та Simula. Simula це мова програмування, де уперше з’явилися віртуальні методи та VMT:

! dogs.sim ;
Begin
    Class Dog;
        ! The cim compiler requires virtual procedures to be fully specified ;
        Virtual: Procedure bark Is Procedure bark;;
    Begin
        Procedure bark;
        Begin
            OutText("Woof!");
            OutImage;           ! Outputs a newline ;
        End;
    End;

    Dog Class Chihuahua;        ! Chihuahua is "prefixed" by Dog ;
    Begin
        Procedure bark;
        Begin
            OutText("Yap yap yap yap yap yap");
            OutImage;
        End;
    End;

    Ref (Dog) d;
    d :- new Chihuahua;         ! :- is the reference assignment operator ;
    d.bark;
End;
Таке враження, що ти сплутав Smalltalk та Simula.

Ні.
Див. наприклад hannemyr.com/cache/knojd_acm78.pdf (посилання прямо з вікіпедії)
«active components named stations».
«transferred to the queue part of another station» — обмін повідомленнями.
І т.д.
В коді всякі route, wait...

Simula це мова програмування, де уперше з’явилися віртуальні методи та VMT:

Це вже Simula-67 і її методи реалізації.

Тому звати стиль C++ «Simula-like» щонайменше неоднозначно.
Треба інший термін.

Тому звати стиль C++ «Simula-like» щонайменше неоднозначно.
Треба інший термін.

ось: ООП-не-той-що-мав-на-увазі-Алан-Кей
можна скоротити до не-Кейний-ООП
або англ Kayless-OOP

Я в старих записах знайшов: «процедурне» ООП (C++/JAva/etc.) і «процесне» (Smalltalk/Simula/Erlang/etc.)

Ok, Simula-67 like :-)
Взагалі, якщо брати більш зрозумілі описи, на кшталт
Intriduction to Simula, то там є Virtual, но немає нічого про route, wait, ... Тобто Wait використовується як звичайний метод.

Головна перевага будь-якої гарної мови це її обмеження. С++ не має обмежень, тому це імовірно не мова, а набір інструментів, які іноді складаються в щось схоже на фреймворк або бібліотеку. На жаль, зараз більшість мов рухаються в напрямку цих «переваг»

текст сгенерирован ChatGPT
— преимущества хорошего языка это его ограничения?
— в C++ нет ограничений?
— нет ограничений, поэтому это не язык?

Ну на самом ограничения дают плюшки. Например, если мы разрешаем только условия и структурную рекурсию (отказываемся от общей рекурсии и циклов), то мы получаем плюшку, что наш код будет всегда выполнен за конечное время (тотальность). Если мы отказываемся от мутабельных переменных, то в качестве плюшки у нас отсутствие сайд-эффектов и возможность параллелить выполнение из коробки. Если мы разрешаем чтобы в один момент времени у нас был либо один указатель, доступный на запись, либо много на чтение, мы получаем отсутствие race conditions, и т. д. и т. п. и др. и пр.

В C++ в принципе можно делать всё, и это делает очень затраднительным получения таких гарантий.

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

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

эта дискуссия скорее о терминах — называть ли стакан наполовину пустым или наполовину полным

то, что пессимист называет ограничением, оптимист называет свободой )

Свобода одних не повинна обмежувати свободу інших, але це не про плюси. Мало в якій мові одночасно співіснують проекти типу Chrome і GCC в стилі С з класами, і скажімо Qt з останніми стандартами, і це ще й якось доводиться подружувати між собою.

І на відміну від скажімо Python 2/3, тут розрив з часом тільки росте, великі, інфраструктурно важливі проекти зовсім не спішать переходити на нові підмножини, від чого під «С/С++ розробником» в описі вакансії чи резюме без детального допитування вже неможливо визначити, кого їм саме потрібно.

а вы в сырцы Chromium давно заглядывали?
там вполне себе нормальные плюсы, причем стандарта C++17 (!)

и для GCC вынуждены были смигрировать на плюсы как раз потому, что поддерживать «си с классами» на таком объеме кода несколько подзамахались

там вполне себе нормальные плюсы, причем стандарта C++17 (!)

си++17 но типа без исключений и вообще? а ну ок

Но неизбежное, полезное и зачастую няшное.

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

як возвращать ошібку через ексепшен а не перемінну возвріта

Хвастаетесь?

в спп обмежень ціла купа. наприклад кількість інстанційованих рекурсивно визначених шаблонних класів (хоча це і обмеження реалізації), неможливість сигналізувати про помилку в деструкторі, і т.д. якщо ви не натикались на обмеження спп ви його ще погано знаєте.

неможливість сигналізувати про помилку в деструкторі

Можна
Кидати ексепшен з деструктора не можна
Вигадай механізм який зберігає помилку non-throw і сигналізуй

отож. у спп існують десятки способів обходити обмеження мови, але самі обмеження від цього нікуди не діваються.

неможливість сигналізувати про помилку в деструкторі, і т.д.

просто не роби це у деструкторі «і т.д.» тоже не роби ))

ну тобто не програмуй на спп. я так і намагаюся робити)

«Плюси» потребують iнтелект вище середнього. Тому свого часу C++ почав поступатися високорiвневим мовам програмування. В 2000-х роках я входив в айтi, i поруч було десятки сiшникiв (з плюсами й без) а також, почали заходити джавiсти. Контраст був досить помiтний: сiшники-з-плюсами — то була справжня iнтелiгенцiя, вiд слова «iнтелект». Сiшники-без-плюсiв — хлопцi попостiше. Джавiсти вiдрiзнялись вiд «плюсовикiв» своєю поведiнкою, рiвнем гумору (бо порог входження нижче, як не крути).
Зараз, коли в айтi не набiг тiльки лiнивий, цього контрасту вже не стало.
В тiй конторi, що я починав, було ще достатньо совка: великi кiмнати з простими столами, що стояти рядами, чистенько-бiдненько, овертаймили, столовка була своя, роками всi варилися в одному соку й один одного знали як облуплених.
В столовцi посуд був звичайний, його мили, тому поїв-вiднiс на мийку, щоб тару не затримувати.
Сiшники були дисциплiнованi- з’їв борщ — вiднiс тарiлку, (allocate\deallocate memory, рефлекторно)
Джавiст зазвичай сидить, їсть, поруч на столi гора брудного посуду, мабуть чекає на Garbage Collector.

Посуд то функція віку. Ваші, від слова «інтелект» (дяка за дужки!), були скоріше за все дорослішими, з совковим вихованням, хліб всьому голова, чисто не там де прибирають, всяке таке. А джавісти то вже імовірно були міленіали, звісно ж Емінем не миє посуд.
Загалом, більш неохайних людей ніж люди які знаються на плюсах, я не зустрічав. Я б думав що це мені не пощастило, але це навіть у мемах ще з нульових.
Гарний плюсовик навряд здогадується про існування реального світу. Інакше він не зможе в плюси.

Плюси — це реально складно, але така сталася коньюнктура, що ниша СРР почала звужуватись.
Якось молодий плюсовик подивився в IDE наскiльки на Джавi лекше кодити, i сказав менi лагiдно: «я вас ненавиджу»
Вони якраз всiм колгоспом вже тиждень ловили багу в тюремному софтi, де при транспортуваннi ув’язнених тi час вiд часу зникали.
Воно, звiсно, NDA, але в столовцi все чути.
З’ясувалося, що масив з даними тимчасово зберiгався в кешi, а не в БД. А пам’ять «текла». В Джавi, якщо код покритий тестами, таке менш верогiдно, й фiкситься лекше.

Гарний плюсовик навряд здогадується про існування реального світу. Інакше він не зможе в плюси.

Ага, тiльки причинно-наслiдковий зв’язок зворотнiй: це робота робить зомбi з молодих чоловiкiв.

В Джавi, якщо код покритий тестами

Якщо у тюремному проекті на С++ не було тестів, до чого тут С++? :-)

Цікавий баг. Тестами він не фікситься, імовірно. Дивно, що пам’ять текла так акуратно. Якщо спробувати таке зробити в C# то впало б все разом з OutOfMemoryException. До речі, пошукати таки довелось би, не тиждень, але.

Visual Assist не знав де вкрасти і як хакнути?

То було надцять рокiв тому, до того-ж, х\з як вони багу душили, якщо вона «плаваюча». Може вони на багатопоточнicть думали, як на root cause.

Як я пригадаю баги в STL для GCC 2.95.2...

А з iншого боку, щоб ми їли, якби все добре працювало)

В Джавi, якщо код покритий тестами, таке менш верогiдно

А в плюсах тести робити некомільфо?

Плюси самi по собi «схильнi» до memory leak, бо така мова, плюс, складнiсть коду там висока, тому, виловити баги на рев’ю важко, а часу дають мало зазвичай. Ну i тестами в тi лохматi часи не зловживали. Я на тому проектi не працював, тому вiдповiдати «за всю одесу» не можу, до того-ж, в плюси я не пiйшов вiд слова взагалi, бо менi вистачило С-без-плюсiв, а Джава зайшла як рiдна.

В Джавi, якщо код покритий тестами, таке менш верогiдно
А в плюсах тести робити некомільфо?

в плюсах testable архітектуру робити некомільфо by design

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

Ви так кажете, наче це щось погане.

Серед знайомих джавстів було кілька дуже людей, які вчасно звалили в Штати. Це коли 512 МБ на машину ставили тільки джавістам.

Це коли 512 МБ на машину ставили тільки джавістам.

Автору першої версії 12309 поставили в ті ж роки 2GB, хоча він, здається, джавістом не був.
Хоча стійте...

«Плюси» потребують iнтелект вище середнього.

Проблема в том как заставить человека с интеллектом выше среднего писать на С++

вот заставляють — «другіх проєктов у нас для вас нєт»

Вариант 1:

cargo new ...

через два месяца тебя спрашивают «ты чего наделал, мы же говорили на С++ писать». Отвечаешь «ой, а мне показалось что вы сказали „на С++ НЕ писать“, какой же умный человек скажет на С++ писать? Ну что поделаешь, хотите чтоб я переписывал то что уже написано и работает? Не проблема, это еще месяцев 6 займет, но вы же никуда не торопитесь, правда?».

Вариант 2.

«Это отлично, я люблю С++ (нет), ой подождите, минутку, мне тут рекурутер с конторы напротив звонит, сейчас повисите немножко на линии, я вам перезвоню (нет)»

нажаль це не R&D а банко-легасі, там все настільки пропроітерно заворочено саме на себе, що навіть стекоферфло дрівен девелопмент методолоджі не канає, розробка через VDI де make проходить 3 години, якщо без тестів, код з локалки навіть через емейл ніззя пересилати, ІДЕ глячно-костильна, відладка через логи які скачувати з девайса через юсб сесію після ресета давайса в спеціальний режим скачки/закачки

Вариант 2.

не канає, бо тілом не в Польщі

спп вимагає розпиляння бюджетів, так як інтелекта вище середнього мало, а посереднії розробників не те щоби много

По своєму чудова і в одночас огидна мова ))
Але це tool
Як і інші мови програмування
Тому я періодично вивчаю інші мови, вони розширюють мій mindset
Це допомагає програмувати на С++

7. Невизначена поведінка

певно складна штука але то єсть пряме походження складності самої мови бо сама мова вже з самого початку стала така складна що деякі «ситуації на краях» вирішили просто не реалізовувати

... як то починаючи з класичних переповнень цілих чисел бо «переповнення» просто «не помістилося» в мову при тому що у самому процесорі і у його асемблері переповнення цільком нормально поміщається як біт переповнення у регістрі флагів бо біт переповнення то є прямий наслідок самого принципу роботу «фізичного» alu

Це частково пояснює, чому С++ все ще дуже успішна у застосунках, які потребують ефективного використання апаратних ресурсів.

ніт )) це пояснюється все ще дуже доступною «фізичною» interoperability перш за все до прямої оперативної пам’яті яка представляє ті самі апаратні ресурси та до прямих викликів операційної системи

певно складна штука але то єсть пряме походження складності самої мови

Тільки злодовбучости стандартизаторів, з яких ~90% це автори компіляторів, які змагаються, хто ще більше раз зекономить 1% ціною злама половини коду.

І почав це, як це ні гірко, Столлман.

Я ось з одним проектом працюю де принципово -O0. Бо не вірять в добрі наміри авторів компіляторів і вважають, що краще втратити кілька %% ефективности, аніж шукати, хто спаплюжив результат.

бо «переповнення» просто «не помістилося» в мову при тому що у самому процесорі і у його асемблері переповнення цільком нормально поміщається як біт переповнення у регістрі флагів бо біт переповнення то є прямий наслідок самого принципу роботу «фізичного» alu

Нічого такого немає в принципах роботи ALU.
MIPS, Alpha, RISC-V — нема «біта переповнення». SystemZ — є у непрямому вигляді.
Якщо ви нічого крім x86 (ну ладно, ARM) не бачили — співчуваю.

Але: принципові проблеми не в цьому, а в тому, як бути з типами даних. Чому int32_t + int32_t не дає int33_t? А якби він давав int64_t, тобто long на більшости сучасних великих машин? А домножити ще раз на int32_t, був би int96_t?
Десь треба зупинитись. І якщо не можна вивести розмір значення так щоб не було переповнення, треба якось показувати, що робити з такою можливістю.
А стандартизатори просто згвалтували і стандарт, і всіх користувачів.

бо «переповнення» просто «не помістилося» в мову при тому що у самому процесорі і у його асемблері переповнення цільком нормально поміщається як біт переповнення у регістрі флагів бо біт переповнення то є прямий наслідок самого принципу роботу «фізичного» alu
Нічого такого немає в принципах роботи ALU.
MIPS, Alpha, RISC-V — нема «біта переповнення».

цікаво як саме ти це собі уявляєш? ))

Але: принципові проблеми не в цьому, а в тому, як бути з типами даних. Чому int32_t + int32_t не дає int33_t? А якби він давав int64_t, тобто long на більшости сучасних великих машин? А домножити ще раз на int32_t, був би int96_t?
Десь треба зупинитись. І якщо не можна вивести розмір значення так щоб не було переповнення, треба якось показувати, що робити з такою можливістю.

а ну да про це я якось не подумав

цікаво як саме ти це собі уявляєш? ))

Еее а що складного?
Наприклад, add для двох 32-бітних значень просто залишає молодші 32 біта результату.

В документації RISC-V, наприклад, сказано про це:

“‘‘We did not include special instruction-set support for overflow checks on integer arithmetic operations in the base instruction set, as many overflow checks can be cheaply implemented using RISC-V branches. Overflow checking for unsigned addition requires only a single additional branch instruction after the addition: add t0, t1, t2; bltu t0, t1, overflow. For signed addition, if one operand’s sign is known, overflow checking requires only a single branch after the addition: addi t0, t1, +imm; blt t0, t1, overflow. This covers the
common case of addition with an immediate operand.
For general signed addition, three additional instructions after the addition are required, leveraging the observation that the sum should be less than one of the operands if and only if the other operand is negative.

add t0, t1, t2
slti t3, t2, 0
slt t4, t0, t1
bne t3, t4, overflow
’’”

Вот длинная арифметика тут, конечно, удорожается — пример для MIPS:

.Loop0: daddiu  $9,$9,-1
        ld      $12,8($5)
        daddu   $11,$11,$2
        ld      $13,8($6)
        sltu    $8,$11,$2
        daddu   $11,$10,$11
        sltu    $2,$11,$10
        sd      $11,0($4)
        or      $2,$2,$8

        daddiu  $5,$5,8
        daddiu  $6,$6,8
        move    $10,$12
        move    $11,$13
        bne     $9,$0,.Loop0
        daddiu $4,$4,8

Вся эта простыня, если перевести на C, делает следующее

using limb_t = uint64_t; // обязательно беззнаковый
limb_t *a, *b, *r;
...
int/*bool*/ carry = 0;
for (i = 0; i < n; ++i) {
  limb_t t1 = a[i] + b[i];
  limb_t t2 = t1 + carry;
  r[i] = t2;
  carry = (t2 < t1) || (t1 < a[i]);
}

детект переноса — на любом из сложении вероятно усечённая сумма меньше слагаемого (все операции и сравнения беззнаковые).
Да, существенно дороже, чем при наличии явного флага carry... но если кому-то это будет существенно (например, для криптографии) — да хоть спец. команды добавит.

а ну да про це я якось не подумав

Поки просто пишеш код — зазвичай це не видно. А ось якщо хоч щось близьке до транслятора — тоді ці питання як граблі одразу бьють по всьому куди дістануть ;\

PS: переключив мови по ходу:) облом виправляти.

Еее а що складного?
Наприклад, add для двох 32-бітних значень просто залишає молодші 32 біта результату.

=>

Нічого такого немає в принципах роботи ALU.
В документації RISC-V, наприклад, сказано про це:

=>

RISC-V has no condition code register or carry bit. The designers believed that condition codes make fast CPUs more complex by forcing interactions between instructions in different stages of execution. This choice makes multiple-precision arithmetic more complex. Also, a few numerical tasks need more energy. As a result, predication (the conditional execution of instructions) is not supported. The designers claim that very fast, out-of-order CPU designs do predication anyway, by doing the comparison branch and conditional code in parallel, then discarding the unused path’s effects. They also claim that even in simpler CPUs, predication is less valuable than branch prediction, which can prevent most stalls associated with conditional branches. Code without predication is larger, with more branches, but they also claim that a compressed instruction set (such as RISC-V’s set C) solves that problem in most cases.

давай спочатку

бо “переповнення” просто “не помістилося” в мову при тому що у самому процесорі і у його асемблері переповнення цільком нормально поміщається як біт переповнення у регістрі флагів бо біт переповнення то є прямий наслідок самого принципу роботу “фізичного” alu

=>

Нічого такого немає в принципах роботи ALU.

=>

MIPS, Alpha, RISC-V — нема “біта переповнення”.

=>

RISC-V has no condition code register or carry bit. The designers believed that

і по колу

бо біт переповнення то є прямий наслідок самого принципу роботу “фізичного” alu

=>

MIPS, Alpha, RISC-V — нема “біта переповнення”.

=>

RISC-V has no condition code register or carry bit. The designers believed that

один в один прямо дежавю “дискусія” “комісії по стандарту” )) а така в цілому прикольна була мова десь на початку 90-х ще навіть без stl і шаблонів ну а потім паніслась ну селяві

але ок давай ще раз по колу

... як то починаючи з класичних переповнень цілих чисел бо “переповнення” просто “не помістилося” в мову при тому що у самому процесорі і у його асемблері

тож чи є у тебе просте і зрозуміле пропозиція як записати мовою сі суму з переповненням?

а = б + ц

?

один в один прямо дежавю «дискусія» «комісії по стандарту» ))

Та хто його зна, що було першим — одне яйце відкладає інше :))
В мовах нема контролю переповнення через те, що в процесорах є один тип — число в регістр; а в процесорах не роблять тому, що в мові нема.

тож чи є у тебе просте і зрозуміле пропозиція як записати мовою сі суму з переповненням?

bool ovf = __builtin_add_overflow(b, c, &a);

приміть, працює на будь-яких розмірах змінних.

bool ovf = __builtin_add_overflow(b, c, &a);

всё правильно сделал!

В мовах нема контролю переповнення через те, що в процесорах є один тип — число в регістр;

learn.microsoft.com/...​nts/checked-and-unchecked
doc.rust-lang.org/...​iles.html#overflow-checks

C# - только исключения, а хотелось бы какого-нибудь try с bool признаком. Исключение — дороговато.
Ну тут у него общие подходы с Java, Swift, многими другими.
Rust — да, идёт верной дорогой. Хотя мне не нравятся варианты типа «паника в debug, усечение в release». Не должно быть такой зависимости вообще, паника/усечение/etc. должны задаваться контекстными опциями.
И всё равно таких, где усечение без вариантов, слишком много (Java если не использовать громоздкий Math; Go; куча прочих).
Но корень даже не в этом. Вот в каком языке ты увидишь встроенное «косое» умножение (типа i32*i32 -> i64) без предварительного расширения к выходному типу? В Rust такое уже завезли? А наличие такого — признак того, что начинают думать над проблемами размерности. Или где я могу управлять размером умолчательного типа для integral promotion? Ada не вспоминать.

Форт, навіть в крайньому стандарті

6.1.1810
M*
“m-star”
CORE
( n 1 n 2 — — d )
d is the signed product of n 1 times n 2 .

See: A.6.1.1810 M* .
A.6.1.1810 M*
This word is a useful early step in calculation, going to extra precision conveniently. It has been in
use since the Forth systems of the early 1970’s.

Форт, навіть в крайньому стандарті

Знаю:) Там ще UM* навскидку.
Але Форт це майже асемблер.
А ось як підіймаємось вище...

Я ж навмисне навів приклад з signed, а не unsigned.

У форті досить легко підніматися вище. Бачив дві досить цікаві гри, написані на форті. Одна відкрита, одна закрита. На пенсії планую повернутися поекспериментувати з деякими ідеями.

У форті досить легко підніматися вище.

Самому слідкувати за стеком... мене особисто то манало. Мій час дорожче.

Он FreeBSD з ~2000 тримала Forth в loaderʼі (як це сьогодні українською? завантажувач?)
Їх заманало, переходять на Lua.

При правильній декомпозиції стекових маніпуляцій майже нема.

Lua кльова, мені також подобається.

Доживу до пенсії, побавлюся, може щось більш осмислене напишу на цю тему. Є певні ідеї.

Саме тому тру є тільки асемблер і форт

Саме тому тру є тільки асемблер і форт

на какій асємблєр тру en.wikipedia.org/wiki/Type-in_program

Та я так на РАДІО-86РК і вводив, поки магнітофона не було.

Зате після того легше було писати дизасемблери, дебагери та всякі форти — коли писати іграшки стало нецікаво.

... як то починаючи з класичних переповнень цілих чисел бо «переповнення» просто «не помістилося» в мову при тому що у самому процесорі і у його асемблері переповнення цільком нормально поміщається як біт переповнення у регістрі флагів бо біт переповнення то є прямий наслідок самого принципу роботу «фізичного» alu

нит.

Переполнение знаковых чисел это неопределенное поведение потому что во времена, когда С создавался, было два способа кодирования знаковых чисел — 1’s complement и 2’s complement, которые при переполнении давали разные результаты. Поэтому переполнение знаковых чисел сделали неопределенным поведением, чтоб типа «смотрите, збс С код одинаково работает на машинах с 1’s complement и 2’s complement». В итоге 1’s complement не вынес жестокости эволюции и умер, а неопределенное неопределенное в С осталось.

Беззнаковые перполняются без неопределенного поведенияч потому положительные числа одинаково выглядели в 1’s complement и 2’s complement.

было два способа кодирования знаковых чисел — 1’s complement и 2’s complement

Ты забыл ещё прямой (sign-and-magnitude). По крайней мере формально он мог присутствовать (и оставались ещё безумные железяки с ним).

В остальном всё так.

а неопределенное неопределенное в С осталось.

Которое откровенно заабьюзили в новых целях.

А були реалізації Сі з арифметикою фіксованою точкою? Чи сама назва float натякає на те, що ні...

Я не бачив. Мабуть, таки ні.

Ніби усе руцями робили через бітовий зсув, коли нема FPU на платі.

Такі особливості, як перевантаження операторів: ними можна зловживати, але вони дозволяють містичні/ магічні бібліотеки, такі як boost spirit.

власне як чудовий приклад зловживання )) і то є вже геть сучасна з появи самого boost особливість мови якій тупо не вистачало мови щоб писати генерований код і от якщо їх розділити... але ніт ))

а оператори то оригінально лише логічне продовження ооп яке би б дозволяло робити свою математику на кшталт

а = б + ц

де усі ці а і б і ц лише сутності єдиного поряку які дозволяють операції = та + над ними а отже математику можна записати багато пряміше ніж

a.set( b.plus( c ) )

... і знову же ж таки далі трапилося обмеження тільки наявними операторами бо додати так просто свій

a = b and c

то вже не тривіально

ЗЫ: а ну і як шапочка то єсть «опєратор космоліт» )) <=>

А на гаскелі можна (про форт вже мовчу, бо згадав вище)

Детерміноване руйнування не є синонімом до RAII. Мало мов наблизилися до того, щоб мати такий спосіб життя і безпеку ресурсів.

бо можна було просто тупо запиляти finally і не мучіццо ))

RAII є загальнішим методом управління всіма ресурсами, в той час як garbage collection використовується тільки при роботі з пам’яттю.

на справді там вже багато рішень на кшталт disposals плюс програмісти на фортрані не хочуть створювати зайві об’єкти бо то же ж overhead ))

... ну і так а далі з’явився moveable при цьому виходить странноє з одного боку об’єкт можна «мувнути» а з іншого його всьо дно треба навіть після того «видалити» ... видалити що? ))

С++ сумісний з попередніми С++ версіями протягом кількох десятиліть

це не так

а реалізація підтримувала бінарну сумісність так довго, або майже так само довго.

це взагалі не так

Жодна інша мова програмування не має цього.

фортран ))

Єдиними двома особливостями мови, які не відповідають принципу нульового накладу, є ідентифікація типу виконання та виключення, і тому більшість компіляторів запускають переключення, щоб вимкнути їх.

це не зовсім так і у сучасних компіляторах простіше кажучи зовсім не так то що стосовно виключень бо компілятори вже навчилися нормально розбирати та збирати код з усіма виключеннями включно і знову же ж таки продовжуючи тему

кваліфікований програміст на фортрані може писати на будь якій мові як на фортрані ))

спроба «старих досвічених програмістів» «обігнати» компілятор уже сьогодні призводить до прямої шкоди ефективності виконання

бо ваше повернення коду помилки разом з детальним описом вот єто всьо на справді генерується у код таки реально більший за операціями процесора бо компілятор банально не розуміє і не може оптимізувати ну селяві

ЗЫ: давненько вже не зустрічав щоб треба було явно вимикати rtti тож мені здається саме «правило» то якась плутанина з не надто сильно глибоко розуміння предмету бо мені здається деякі arm компілятору мають exceptions залежними від rtti чи щось таке тож у «інструкціях» пишеться щось простеньке на зразок «вимикайте і буде вам щастя і страшна швидкість» ну і потім далі програмісти на фортрані які пройшли курс по сі++ по $4200 за штуку просто занотовують це і використовують не особливо думаючи чи просто не думаючи взагалі ну селяві

ЗЫ: я не певен що зараз stl вміє без виключень але головне а воно справді треба?

про RTTI...
так отключают совершенно отмороженные embedded товарищи, которые до сих пор сидят на компиляторах 15-ти летней давности и их STL не совместим даже со стандартом 98-го года )

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

что язык с такими областями применения до сих пор не может дать гарантий по времени обработки исключений

в смысле?

Тип исключения неизвестен заранее => оно аллоцируется динамически. Без дин. памяти не работает, а сколько в ней будет занимать та аллокация — один аллах знает. И обратная деаллокация — тоже.

Саттер выдвигал предложение сделать исключения в стиле Ada, когда оно несёт только свой тип, но не параметры. Предложение зависло где-то между тучами и землёй.

Впрочем, я думаю, что тут легче подвинуть авторов спец. компиляторов при наличии какой-то общей настройки типа «исключение может быть не толще 100 байт и без аллокаций по цепочке», чтобы просто заранее в контексте нитки выделить эти 100 байт. А уже по их опыту пытаться загонять это в стандарт.
При этом stdexcept весь, скорее всего, пойдёт кормить голубей — потому что у кучи классов в нём параметр типа std::string.

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

А где там приведение типа?

эммм... а что из себя представляет любое catch выражение?
это фактически dynamic_cast (даже более продвинутый)

Я говорил про предложение Саттера по переносу подхода Ada, где исключение представляет собой фактически не объект класса, а класс или объект одного конкретного стандартизованного класса, или иным образом гарантированно уникальное плоское значение (грубо говоря, типа uintmax_t). Там приведений типа нет, исключения неструктурные, поэтому сравнение это простое сравнение целых, тормозить ему не на чем.
Поэтому с ним нету ни аллокации ни затрат на проверку приводимости.

Цена этого в виде отсутствия сопутствующей информации может быть нивелирована вариантом типа «а позволим ещё 4 штуки uintmax_t в нагрузку, чтобы всем хватило с запасом, главное, что их количество фиксированное и не безумное». Для embedded этого точно должно хватить.

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

лучше какой-нить конкретный класс с возможностью вложить в него что-то типа std::any

коды ошибок это плохо

Откуда взялось про коды ошибок? Я писал:

гарантированно уникальное плоское значение

Как можно было отсюда перейти к коду ошибки? Сами домыслили и теперь меня обвиняете?

Гарантированная уникальность в пределах бинарника может быть обеспечена, например, созданием именованного common-блока минимального размера (хоть 1 байт) как отдельной секции объектника, дальше линкер проставит у всех нужные ссылки. Возможны и другие подходы.

лучше какой-нить конкретный класс с возможностью вложить в него что-то типа std::any

Любая идея добавлять значение произвольного размера, особенно аллоцируемое динамически, убивает всю исходную цель. Удивляюсь, что вы этого не замечаете.

Авжеж.

Я говорил про предложение Саттера по переносу подхода Ada

Інколи мені здається, що не завадить й повністю перейти на Ada :-)

Інколи мені здається, що не завадить й повністю перейти на Ada :-)

Може на ембеді і спрацює.

Але для чого іншого — це мова-параноїк зі своїми заскоками:
«Шо? Тут — масив із 3 символів, а тут — із 4-х? Все — не компілюю, помилка»
(це я так пробував якусь простеньку програму звідкілясь переписати, сильно не вникаючи)

Плюс нема тих напрацьованих патернів, які є на інших, більш поширених мовах.

До речі, класний підсумок про Аду
www.reddit.com/...​&utm_content=share_button

Але для чого іншого — це мова-параноїк зі своїми заскоками:
«Шо? Тут — масив із 3 символів, а тут — із 4-х? Все — не компілюю, помилка»

це ж ти щойно руст обписав ))

Але для чого іншого — це мова-параноїк зі своїми заскоками:
"Шо? Тут — масив із 3 символів, а тут — із 4-х? Все — не компілюю, помилка«

це ж ти щойно руст обписав ))

Так, Руст також, здається, підпадає під «параноїка»
Але у них є велика перевага — напрацьовані паттерни, можливість загорнути страшні місця у обгорточки (всякі бокси, арки, шмарки...), і велике і жваве ком’юніті

Ми такі, правда компілятори свіжі, бо QNX свіжий також. Але -fno-exceptions і сплю спокійно з 2005-го

Chromium не использует ни rtti, ни exceptions, таки что-то понимают в этом :) Как минимум экономят размер бинаря
google.github.io/...​e_Type_Information__RTTI

Chromium не использует ни rtti, ни exceptions, таки что-то понимают в этом :)

гугл ты хотив сказать )) штош видоми прыбацани рэально пацаны на тэми ))

В ембедеді флеш пам’ять набагато дорожча за раму, і є найбільш обмеженим ресурсом. Тому компілюють з -Os та відключають ексепшни (30% розміру бінаря) та STL (вектори замість масивів можуть в рази збільшити код).

якщо вже казати чесно я не думаю що _сучасний_ сі++ _добре_ вирішує проблеми ембеду як і то обмежених ресурсами архітектур

можливо на такі розкладки простіше писати свої компілятори з чого завгодно най навіть пітон подібного або за смаком (далі видалені роздуми про те яким мав стати справжній сі++)

В ембедеді флеш пам’ять набагато дорожча за раму

ну так макбук за 512 гіг продають за $300 дорожче за бакбук на 256 гіг ))

Там всюди, де треба, розставлені noexcept.

В мене з 2005-го цирк в продакшині, наразі досі працює і підтримується.

Те, що ви використовуєте, настільки ж ефективне, як і те, що ви могли б писати вручну.

доречі вельми цікава штука для тих же ж програмістів на фортрані )) я якось пробував «обігнати» те що робив компілятор ну щось на зразок switch по string з хеш функцією таки я наблизився упритул застосувавши наявний set avx simd регістрів та команд але навіть обігнати всьо дно не зміг )) може б і зміг але то є тре було копати ще і ще і ще

тоже у цій фразі «але є одне але» (к) (тм) це там де «програмісти на фортрані яких посадили писати ні сі(++)» розуміють цю фразу з кінця на початок

настільки ж ефективне, як і те, що ви могли б писати вручну.

і вони думають і то навіть переконані і впевнені що те що вони «пишуть вручну» то єсть «настільки ж ефективне» і переконати їх в зад суто технічно не можливо я перевіряв ))

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

ой )) видимо самая интересная фича это работа с объектами прямо на стеке откуда идёт «всё самое интересное»

... скажем каково время хранения временного объекта на который ссылается константная ссылка?

Існують 4 види тривалості зберігання:

автоматичний;
статичний;
поточний;
динамічний.

... это потому что забыты на пример всё те же ж временные объекты с которых кстати происходят всякие lvalue rvalue ))

и всё это только от того что объект может существовать (инстанциироваться) не _только_ динамически

Перевантаження оператора

C++ allows overloading of the unary ampersand (&) operator for class types. The return type of such an operator need not be the actual address of the object. Intentions of such a class are highly debatable, but the language allows it nevertheless.

збс

А ще там можна перевантажити оператор , (кома).
Це взагалі весело.

Якось на одній конференції Стефан Лававей — майкрософтівський чєл, відповідальний за їх реалізацію стандартної бібліотеки — показував приклад з циклом for у реалізації якогось generic алгоритму, де в третій частині інкрементилися два ітератори:

for (...; ...; ++iterator1, (void)+++iterator2)

Питає у аудиторії: «як думаєте, нафіга там каст до void?»
Не вгадав ніхто.

Відповідь: тому що клієнтський код міг оверлоадити оператор «кома» для своїх ітераторів xD

for (...; ...; ++iterator1, (void)+++iterator2)

А три плюси теж навмисно?

Ні, то я заснув обличчям на клавіатурі, перепрошую.

Досить зручно для деяких задач, я так робив. Syntactic sugar непоганий виходить.

«Невизначена поведінка виникає, коли програма виконує щось, результат чого не визначено стандартом.»

тільки той стандарт вже не знає сам страуструп. він настільки розрісся і став складним, що його ніхто не знає повністю. і мова ускладнюється і ускладнюється. і дуже зручно казати «ub, якщо не по стандарту щось робиться», але впевнитись чи по стандарту це чи ні стало занадто важко

А от такі речі в стандарті мені дуже не подобаються. Якийсь прескриптивізм багомєрзкій...

Щось я не в’їхав. Лямбда корутини роблять use after free на рівному місці без попереджень.

Приклад можна?

CP.51: Do not use capturing lambdas that are coroutines

Usage patterns that are correct with normal lambdas are hazardous with coroutine lambdas. The obvious pattern of capturing variables will result in accessing freed memory after the first suspension point, even for refcounted smart pointers and copyable types.

A lambda results in a closure object with storage, often on the stack, that will go out of scope at some point. When the closure object goes out of scope the captures will also go out of scope. Normal lambdas will have finished executing by this time so it is not a problem. Coroutine lambdas may resume from suspension after the closure object has destructed and at that point all captures will be use-after-free memory access.

Ось isocpp.github.io/...​mbdas-that-are-coroutines
та ось github.com/...​ambda-coroutine-fiasco.md

A lambda results in a closure object with storage, often on the stack, that will go out of scope at some point.

класичний «плюси» у спробі натягувати сову на глобус замість надавати по ушам люзєрам і написати нормально на рівні високо рівнєвої мови лишаючи за собою шо там під капотом а також чи воно там сумісне з основним єзігом 120-річної давнини ))

Coroutine lambdas may resume from suspension after the closure object has destructed and at that point all captures will be use-after-free memory access.

а ну доречі так спочатку лямбди були вапчє immutable

Як на мене, тут класична Ваза, коли одна тіма в комітеті не знає, що робить інша тіма, і фічі мови починають схрещуватись в невідомі химери.
www.stroustrup.com/...​977-remember-the-vasa.pdf

Половина з зазначених «переваг» більше схожі на недоліки, а деякі з них — на критичні.
Сам писав на С++, і зрозуміло, що С++ жив, живе, і нас всіх переживе. Але мені це нагадує стокгольмський синдром.

C++ то як зброя — треба поводитись обережно та відповідально. Та не давати дітям до 25 років))

Не розумію, як можна писати на С++ не пописавши до того на асемблері.

А от я з асмом познайомився лише минулого року (та й то чукча читач), після 14 років з С та С++.

У кожного своя доля, і свій шлях широкий... ©

Я ж не кажу, що не можна, а кажу, що не розумію, як можна. Напевне якось... В мене кум також принципово каже, що не знає асемблера. Після паскаля — С++ і VHDL. Ну і вольтметр знає куди втикнути за потреби. Нічого, Голівуд його любить. Досі наш цирк пиляє...

Ну я вивчив Пітон, але він тоді був непопулярним, тому (тіпа) вивчив С++ і в результаті знайшов роботу на С в ембедеді. Ну і як в ембедеді, навколо була купа народу, що знала асм та осцилографи, але не вміла в ООП щоб просто накодити складні речі. В результаті бізнес-логіку кодив я.

В результаті бізнес-логіку кодив я

дАртарьян

Це дивлячись яка в тебе спеціалізація. Якщо ти embedded-розробник, маєш справу з hardware, пишеш драйвери — то так, іноді треба. Якщо ти пишеш десктоп-софт, наприклад на Qt — то тобі це абсолютно не треба. Якщо ти security analyst / vulnerability researcher, то це напевно must have.

P.S. Якщо що, я теж починав саме з Асемблера, але це було в ті роки, коли HDD були об’ємом 20 Мб (так, мегабайт) і були дивом дивним. І на асемблері тоді писали ледь не третину всього що було.

я теж починав саме з Асемблера, але це було в ті роки

коли дос 3 загружав з дискети,
хоча ні, ще з касети МК60, але не дос

хоча ні, ще з касети МК60, але не дос

просто у тебе дісковода нє біло вот ти і бєсішься ))

ЗЫ: коди (машинні) 8080 у виконанні 580вм80а були прості і попятні ну понятно для продвинутого пацана )) тому прямо з роздруківки по 16 байтів в ряд читалися як та твоя матриця зразка 98-го року що правда є один нюанс бо така звичка вже була на читанні кодів мк61 для запису яких була використана она просунута система по 10 комірок в ряд задля зручності визначення адресації але тут оце згадавши і подумавши трохи я мабуть не зовсім сказав правда бо програма мк61 записувалася не у коді а таки у «асемблері» бо і набиралася вона таки не у коді же ж а спеціальними клавішами з конкретними іменами

звісно так #внєзапно потім це все стало у пригоді бо асемблер і машинний код 8086 це власне те саме тіко трохи ширші шина даних та шина адресації та трохи різноманітніші регістри а ну і плюс ця куменда схема адресації коли адреса на справді не по слову але по байту але то вже складна історія і вже більше до чистого заліза ))

ЗЫ: най цікавіше воно було бо я пригадую там все траплялося так швидко що існування було фактично на стику 80/86/286/386 а там і 486 вже підтягнувся а от пєнтіум я вже не так пам’ятаю певно тому що то вже була інша історія скоріш вже «комп’ютер як для користувача»

Pentium 66 потребляет ток в 3,2 А и имеет мощность 16 Вт, что потребовало установки дополнительного вентилятора.

16 ватт з вентилятором ет яку страну про.ли ((

ЗЫ: коди (машинні) 8080 у виконанні 580вм80а були прості і попятні ну понятно для продвинутого пацана ))

S/360 — ось де легко читалось. 8080 — вже значно складніше.
Якби IBM не корпоративні дуболоми, того Intel просто не було б (і, мабуть, на краще).

От до чого доступу не було, до того не було...

ЗЫ: най цікавіше воно було бо я пригадую там все траплялося так швидко що існування було фактично на стику 80/86/286/386 а там і 486 вже підтягнувся а от пєнтіум я вже не так пам’ятаю певно тому що то вже була інша історія скоріш вже «комп’ютер як для користувача»

Асемблер тоді чому був популярний — бо по перше, мнемоніки були зрозуміли, легко запам’ятались, їх було небагато, по-друге сам інтерфейс до DOS був опублікований саме на рівні регістрів, портів та переривань. На С цим користуватись було значно складніше, бо компілятор ті регістри сам використовував, сегменти на свій лад робив, стек вже мав структуру, і т.д. Тобто багато зайвих сутностей. Але, наприклад на С було зручно перевантажувати operator[] для доступу до портів, код був компактний та майже так само ефективний. В мене свого часу непогана ліба вийшла для UART 16550.

по-друге сам інтерфейс до DOS був опублікований саме на рівні регістрів, портів та переривань. На С цим користуватись було значно складніше, бо компілятор ті регістри сам використовував, сегменти на свій лад робив, стек вже мав структуру, і т.д. Тобто багато зайвих сутностей.

на робочих версіях сі які взагалі були це здається сам ms c десь з другої половини 80-х то там уже не треба було бо там же ж був нормальний сішний інтерфейс до системи

але тоді я пригадую сі не такий щоб надто популярний був тоді мені здається було торжество паскаля ну і бєйсіка ))

borland turbo pascal наше всьо )) була версія навіть для віндоус (16-бітний 3.1 чи раніше) ну правда це якщо знаєш вже api

борланд же ж з зробив турбо сі але їх я пам’ятаю погано бо я пам’ятаю щось версії 3.(1?) і щось версії 5.1 це з тих які реально робочі а потім пішла вінда дуже довго була 95 а потім якось одразу 98 і до неї студія 6 і почалася сучасна ера

а ну да і пацани сиділи на nt4 ))

В мене свого часу непогана ліба вийшла для UART 16550.

на справді мені важко уявити реальну не обхідність писати апаратуру на сі в ті часи сі паскаль сгодом «плюси» і object pascal були мовами високого рівня для реалізації вже прикладної логіки і принаймні сі та паскаль дозволяли прямі асебмлерні вставки за участі власних змінних як мені здається а щоб уявити що ліба на uart використовувалася «зовні»... ок дивлячись з сьогодні часу мені важко бо то є певни помилка проектування у рівнях абстракції ))

на робочих версіях сі які взагалі були це здається сам ms c десь з другої половини 80-х то там уже не треба було бо там же ж був нормальний сішний інтерфейс до системи

Cішний інтерфейс до системи зветься «libc» )) Чогось більш специфічного я не пригадую, але що ми тоді знали...

борланд же ж з зробив турбо сі але їх я пам’ятаю погано бо я пам’ятаю щось версії 3.(1?) і щось версії 5.1 це з тих які реально робочі

В універі я писав лабораторні на 3.1, а потім (те що нижче) писав на 4.5. І ото була його «лебедина пісня». Бо під Вінду в них якось не пішло, MS взяв реванш.

borland turbo pascal наше всьо )) була версія навіть для віндоус

Під вінду то вже Delphi звалось))

на справді мені важко уявити реальну не обхідність писати апаратуру на сі в ті часи

А то і не було в «ті» часи, то було значно пізніше — я писав UI на Turbo Vision для промислового контролера с LCD дисплеєм, але він працював на DOS під сучасним клоном i386-40 та мав 4M RAM, тобто по швидкості це було адекватно, а писати на C++ значно швидше ніж на ASM. І ще купа інших залізок була на DOS, промислового призначення.

Cішний інтерфейс до системи зветься «libc» )) Чогось більш специфічного я не пригадую, але що ми тоді знали...

Мова про MS. libc там не було (був CRT в якому була дай боже щоб 1/5 від libc, і дуже зпаплюжено).

Багато кто відкидав його і використовував WinAPI напряму.

Мова про MS. libc там не було (був CRT в якому була дай боже щоб 1/5 від libc, і дуже зпаплюжено).

CRT то і є libc від MS або Borland. Файлові операції, строки, виділення пам’яті, запуск процесів — необхідний мінімум був. І до чого тут WinAPI, коли мова йде про DOS? А щодо Борланда, в мене досі про нього приємні спогади. Все що вони робили, було як треба — Turbo Pascal, Turbo C++, TASM, Turbo Debugger. Останній навіть вмів дебажити віддалено, через UART. Для контролерів не раз рятувало.

Але, наприклад на С було зручно перевантажувати operator[]

Можна приклад? О_о

Якщо тебе триггернуло що це було наче «на C», то не зважай — я вважаю ці мови одним й тим самим)

/*	$Id: Ports.h 6 2007-09-22 15:11:17Z denis $
 *	
 *	Ports.h
 *
 *	Written by Denis Zavorotny (c) 2006
 *
 *	Port class is intended to wrap inportb()/outportb() and inpw()/
 *	outpw() functions into C++ indexer operator. It makes possible
 *	to write port[0x3f8] = 0x12 or char a = port[0x3FA] and get
 *	results as expected. Another benefit is improved portability of
 *	related code. Multiple chain assignments work as expected also
 *	(i.e. port[0x3FA] = port[0x3f8] = 123 is okay). The	mechanics
 *	under the hood is as follows:
 *
 *		port[0x3F8] = 12;
 *
 *	is equivalent to call
 *
 *		port.operator[](0x3F8).operator=(12);
 *
 *	and,
 *
 *		char a = port[0x3F9];
 *
 *	is internally converted to
 *
 *		char a = port.operator[](0x3F9).operator char();
 *
 *	Similary,
 *
 *		port[0x3F9] = port[0x3f8] = 123
 *
 *	is
 *
 *		port.operator[](0x3F9).operator=(
 *			port.operator[](0x3F8).operator=(123));
 *
 *	That's the way it works.
 *
 *	Written by Denis Zavorotny (c) 2006.
 *
 */

#ifndef __PORTS_H
#define __PORTS_H

#include <dos.h>

// Port class definition *************************************************

class Port
{
private:
	word address;
	void operator=(const Port&);
	
public:
	Port()							{ address = 0; }
	Port& operator[](word addr) 	{ address = addr; return *this; }
	byte operator=(byte b)			{ outportb(address, b); return b; }
	void operator&=(byte b)			{ outportb(address, inportb(address) & b); }
	void operator|=(byte b)			{ outportb(address, inportb(address) | b); }
//	word operator=(word w)			{ outpw(address, w); return w; }
	operator byte() const			{ return inportb(address); }
//	operator word() const			{ return inpw(address); }
};

// BasePort class definition *********************************************

class BasePort
{
private:
	word index;
	word base;
	void operator=(const BasePort&);
	
public:
	BasePort(word b = 0)			{ base = b; index = 0; }
	BasePort& operator[](word i)	{ index = i; return *this; }
	const word& Base() const 		{ return base; }	// Read access
	word& Base() 					{ index = 0; return base; } // Write access
	byte operator=(byte b)			{ outportb(base + index, b); return b; }
	void operator&=(byte b)			{ outportb(base + index, inportb(base + index) & b); }
	void operator|=(byte b)			{ outportb(base + index, inportb(base + index) | b); }
//	word operator=(word w)			{ outpw(base + index, w); return w; }
	operator byte() const			{ return inportb(base + index); }
//	operator word() const			{ return inpw(addr); }
};

#endif	// __PORTS_H

І застосовувалось воно десь так:

/*
 *	The function sends break to remote as suggested in National's
 *	PC16650D UART datasheet.
 */
void ComPort::SendBreak()
{
	base[IER] &= ~E_INT_XMT_CLEAR;
	while (!(base[LSR] & LINE_XMTRDY))
		asm nop;
	base[THR] = 0;
	base[LCR] |= SET_BREAK;
	while (!(base[LSR] & LINE_XMTSHR))
		asm nop;
	base[LCR] &= ~SET_BREAK;
	base[IER] |= E_INT_XMT_CLEAR;
}

Мнемоніки IER, THR і т.д. це дефайни на порти регистрів:

static const word THR		= 0;  /*  W: Transm. Holding Register  */
static const word IER		= 1;  /*  Interrupt Enable Register    */
static const word LCR		= 3;  /*  Line Control Register        */
static const word LSR		= 5;  /*  R: Line Status Register      */

P.S. Гарні часи були...

Якщо тебе триггернуло що це було наче «на C», то не зважай — я вважаю ці мови одним й тим самим)

Та то буквоєдство було, звісно С++, просто я подумав що і у Сі є operator[] =)

P.S. Гарні часи були...

Скажи? Я пам’ятаю ми, у 2006 році, у одній іграшці пакували ассети прямо у код, тому що ФС там не було. ScopedArray, AutoPtr, та інші велосипеди. Бага з музикою яка переповнювала нутрощі ліби-програвача та вішала девайс... Сумую за цим))

Ще не було магнітофона, іграшки вбивав з дампів з журналі «Радіо». Були часи...

Ха! В нас було 40 МБ на 286-ій машині. «Пошуки» були тільки з дискетами. І ми боролися з кафедрою фізики за право доступу до комп’ютера. Коротше, ми перемогли.

C — raw pointers
C++ — trying to prevent people from using raw pointers 😉

C++ — trying to prevent people from using raw pointers 😉

and since c++11 it was not actually so bad

смарт-поинтеры особо ничего не решают. Всё равно их нужно контролировать/отслеживать/понимать, когда именно объект будет удалён, это если кратко. Работал в проектах и с их использованием и без. Сам для себя предпочитаю без, не вижу никакого неудобства в указателях, главное «уметь готовить». Раз уж есть нужда в динамическом выделении памяти.

unique_ptr<> вирішує дуже багато.

В нас по коду зараз голих * не дуже багато.

Мені за 6.5 років unique_ptr пару разів знадобився. Інше — RAII в класах.

Better this way:

void foo(moo& datum) {
  if (&datum == nullptr) {
    // oops, compiler eliminates this branch as impossible
  }
  ...
}

int main() {
  moo* p = nullptr;
  foo(*moo); // hmmm
}
foo(*moo); // hmmm

Ну по-перше, мабуть foo(*p). По-друге — нічого він не викидує. Ні з /O0, ні з /O3, ні з /Os. Компілятор Microsoft ® C/C++ Optimizing Compiler Version 19.29.30148 for x64

MS спеціально навчений не викидати у такому випадку, наскільки я чув.

Але для мого світу MS компілятора нема.

Так, мабуть ви праві. Перевірив на Mac mini, тож clang там вже на -O1 викидує...

minGW под Виндовс на -O2 тоже выкидает.
Но есть вариантик

    #include <stdio.h> void func(unsigned int& ref) { volatile size_t x = size_t(&ref); if (x == 0) { printf("Null refrence detected\n"); return; } printf("All OK\n"); } int main() { unsigned* d = nullptr; func(*d); return 0; }
minGW под Виндовс на -O2 тоже выкидает.

Так MinGW содержит GCC внутри. С тем отличием (от cygwin), что ему кроме winapi и стандартного msvcrt ничего не нужно.

#include

В любом случае, я бы тот код, который gcc/clang выкидывают, вообще бы не писал. Т.к. вся суть ссылки и есть, что она не должна быть nullptr, а должен быть объект. Т.к. если по логике допускается nullptr, то должен быть указатель.

Такое бывает если на проект с одной кодовой конвенцией ставят либу third-party с другой кодовой конвенцией. Ну и начинается соединение деталей микроволновки и бетономешалки.

Бачив таке колись в коді пітоніста, який писав щось на С++11.

C — raw pointers

что с ними не так?

Всі вважають, що вони дуже небезпечні. Через це була створена Java, Go та багато інших мов програмування. 🙂

а, ну ок. Пусть считают

Ну... з іншого боку були створені санітайзери. Проблема вказівників більше у тому, що при UB проблеми можуть з’являтися не відразу. Санітайзери це більш-менш фіксять. GC це фіксить. Так, підтримувати ручками трохи більше роботи, але не дуже критично як на мене.

Ну... з іншого боку були створені санітайзери

С другой стороны... cve.mitre.org/...​i?keyword=buffer overflow

Санітайзери це більш-менш фіксять

heartbleed.com — 17% серверов мира оказались уязвимы, продемонстрированы в т.ч. возможность похищения приватных ключей с сервера, отправляя ему HTTP запросы
en.wikipedia.org/wiki/Stagefright_(bug — удаленное выполнение кода на девайсе жертвы отправкой MMS сообщения (для успешной атаки жертва даже не должна открывать сообщение).

не дуже критично як на мене

ок

Подождешь со счётами, пока все системные библиотеки на Расте перепишут? Или будешь использовать небезопасный Линукс?

Использую небезопасный линукс и работаю над тем, чтоб было написано больше безопасного кода в т.ч. в линуксе.

С самими по себе (если их использовать как, скажем, тот же Reference из C#) с ними все более менее нормально (могло быть и хуже).

Проблема в том, что raw pointers интимно связаны с массивами в C++, но при этом не несут никакой информации о границах буфера. Выход поинтера за границы валидного буфера (даже если ты не пытаешься с этого поинтера что-то считать или записать) — это, внезапно, UB.

Даже если игнорить факт UB, написание кода, который корректно работает с буфером с помощью поинтеров в С/С++ — очень нетривиальная задача, мало кому посильная.

Всі ці переваги портить один серйозний недолік: зоопарк стилів і ідіом, від С з класами, до С++20.

Приклад перший, для пет-проекту знадобився один сервіс, який був початково написаний на С++11 і пізніше переписаний на Go. Незважаючи на повністю непрофільну для себе мову, го-шна версія виявилась значно простішою у доробці і запуску під свої потреби, С++ навіть збілдити потребує зусиль, бо автор писав під локальне оточення з дефолтними опціями свого дистрибутиву, і не думав про портабельність.

Приклад другий, franca.github.io/franca
Якийсь архітект не вдасться в деталі, і потім те, що ця хрінь генерує ідіоматичний С++ код, з STL-контейнерами і ексепшнами, і це, ну, не дуже добре стикується з С/С з класами в класичних фреймворках для мікроконтролерів, в результаті воркараунди або ручне написання коду замість автогенерації кратно вилізе за початкові оцінки часу проекту, клієнт буде невдоволений, а у полку С+±хейтерів станеться поповнення...

А автор мови вважав, що проблема в комітеті www.stroustrup.com/...​977-remember-the-vasa.pdf

Мова досягнула повноліття, а татко пробує її вчити жити?

У 70 нянек дитя не только без глазу, а и без рук-ног.

С десятью руками (сначала пришлось добавить еще две потому что первые две выросли из жопы, а потом уже по инерции добавляли на каждый день рождения)

Так это уже не руки, это тентакли (причём без пальцев).

С++ навіть збілдити потребує зусиль, бо автор писав під локальне оточення з дефолтними опціями свого дистрибутиву, і не думав про портабельність.

кваліфікований програміст на фортрані може писати на будь якій мові як на фортрані ))

... проблема сі++ наші дні (а може останні 20+ років) що сі++ дозволяє перш за все класично стріляти собі в ногу і то робити це багатьма різноманітними шляхами зарання не здогадаєся як саме черговий «програміст на фортрані» це зробить

ЗЫ: з останнього то єсть зберігання посилання на тимчасовий об’єкт як параметр функції нє ну а чо компілюється же ж і певно навіть якось там виконується? )) ну так «нубська лажа» не «помилка» навіть бо «помилитися» так не можна бо просто треба знати але хто ж там його знає?

Приклад другий, franca.github.io/franca

узнаю брата колю )) мене від слів genivi особисто просто тіпає )) тобто чуваки узяли шото на стільки першородно мертве та смердить шо ото воно навіть власні посилання не спроможне підтримувати бо вже мертве projects.genivi.org/commonapi ну правильно бо ребрендінг covesa запиляли бо вони саме цим і займаються «красівими презентаціями для панів насяльніка» а от посилання на нове якось «не подумали»

а в результаті...

в результаті воркараунди або ручне написання коду замість автогенерації кратно вилізе за початкові оцінки часу проекту, клієнт буде невдоволений, а у полку С+±хейтерів станеться поповнення...

а ну да всьо так ))

... проблема сі++ наші дні (а може останні 20+ років) що сі++ дозволяє перш за все класично стріляти собі в ногу

Проблема не в том, что С++ разрешает стрелять себе в ногу, а в том, что все предостережения, касающиеся С++, ограничиваются предупреждением «осторожно не выстрелите себе в ногу». С++ программисты вполне логично думают, что речь идет о том, чтоб реально себе в ногу из ружья стрельнуть, и думают «ну я же не идиот, я стрелять в ногу не буду».

Вместо этого нужно показывать, как функции, которые не вызываются в коде, на самом деле исполняются в рантайме, или то как банальное умножение двух _беззнаковых_ переменных вызывает неопределенное поведение, чтоб у читающих возникали мысли «что блин? это как? как на этом языке вообще можно что-то писать?»

Перша бага епічна. Але це бага в GCC, Clang з -O3 праює нормально

Але це бага в GCC

Проблема в том, что любители С++ не догоняют, что это не «бага в GCC», а на самом деле «фича в GCC».

Когда умные люди пишут «в случае вызова неопределенного поведения программа может делать что угодно, даже форматировать жесткий диск», нужно не ржать «хахаха, ты просто не осилил С++», а хвататься за голову и кричать «вот п***ц!!!»

Вот потому я и вижу -O0 в опциях сборки некоего проекта :))

Так так.
Всі настільки дурні, що GPT-4 написали знизу на С/С++, а зверху на пайтоні (скрипти). Нажаль, з доу експертами не проконсольтувались на рахунок UB.

Та лізеш в асемблер, дивишся, що воно нагенерувало, і думаєш, як це обійти.

Тому перехід на нову версію компілятора — непростий процес, якого я намагаюся уникати без особливої на те потреби і повноцінного тестування.

Та лізеш в асемблер, дивишся, що воно нагенерувало, і думаєш, як це обійти.

Давай я буду немного скептически настроен и скажу, что если инженер не смог написать С++ код без неопределенного поведения, то он не квалифицирован для того, чтоб обнаружить это неопределенное поведение в ассемблерном листинге.

Не погоджуся. Людина може знати асемблер для конкретного процесора, але не знати всіх UB.

Ну тут вже питання, чи є для програміста на практиці різниця між шкідливим UB та багами компілятора. Хоча баги зазвичай крешать компілер.

Поміняв отой їхній богомєрзкій cout на наш простий printf, і GCC мені сказало отаке:

$ g++ -O3 q9.cpp 
q9.cpp: In function ‘int main()’:
q9.cpp:6:11: warning: iteration 4 invokes undefined behavior [-Waggressive-loop-optimizations]
     printf("%d\n", j * 0x20000001);
     ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
q9.cpp:5:21: note: within this loop
   for (int j = 0; j < 9; ++j) {
                   ~~^~~
$ cat q9.cpp 
#include <stdio.h>
int main() {
  char buf[50] = "y";
  for (int j = 0; j < 9; ++j) {
    printf("%d\n", j * 0x20000001);
    if (buf[0] == 'x') break;
  }
}

ба більше. самі розробники gcc пишуть, що -O3 для самогубців.

Все життя з -O3 працюємо. Не пам’ятаю жодної проблеми через це. Більша проблема була, коли в GCC поламали оптимізатор, і критичний кусок коду став виконуватися втричі довше.

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

Найти такое в ассемблерном выходе скорее _трудно_, чем _сложно_.

это скорее история про то, что все, что касается переиспользования кода (то, что мы обычно называем «внешними библиотеками») абсолютно никак не стандартизировано (и, пожалуй, первый робкий шажок в эту сторону был сделан только в С++20 — я про модули)

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

C with classes (C++98 noSTL noExcept) вполне хорош для среднеразмерного эмбедеда (10 — 100 KLoC) когда есть динамическая аллокация. Он дает ООП (удобные высокоуровневые абстракции) и почти не требует дополнительных ресурсов по сравнению с С.

Під STM32 цілком собі ок, під різні дівайси писати

Не знаю, какие сложности со сборкой? На Маке есть homebrew. На винде есть vcpkg, и он нереально помогает. Допустим cmake говно, так есть qmake. Я бы так сказал — если у тебя сложности со сборкой, и ты называешь себя Senior, значит ты не senior. Ну а мидлам простительно.

Да ну, сложности постоянно, если что-то лепить из исходников разных либ. Попробуй, например, что-нибудь гугловое собрать и подключить к себе в проект, типа вебртс или хромиум :) Вся матерость синьора улетучится сразу.

Попробуй, например, что-нибудь гугловое собрать и подключить к себе в проект, типа вебртс или хромиум

Ты не поверишь, но это именно то, чем я занимаюсь последние 5 лет)))

И где же там что-то из cmake, qmake, vcpkg? Всё кастомное, под каждую платформу свои сложности. Первая сложность собрать саму либу родным тулчейном, вторая сложность встроить ее в проект. Задача со звёздочкой: собрать своим компилятором и стандартной либой std. Неужели это не «сложности»?

И где же там что-то из cmake, qmake, vcpkg?

Раз уж ты спросил, то в Qt есть модуль QtWebEngine, который инкапсулирует Chromium вместе с WebRTC. Если нужна готовая либа — то вот готовое решение. Конечно, всех на свете тулчейнов в установщике не будет, там обычно самые популярные/последние, но достаточно несложно сделать и свой билд. Сборка Chromium, как и всего Qt, там завернута в qmake, или cmake (в Qt 6).

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

А Chromium это и не либа, которую можно в готовом виде утащить к себе. Это огромный open-source проект, который пилят наверно с ~50 разработчиков, в одной из крупнейших IT корпораций мира. И пилят для себя, а не для тебя. Конечно, там все будет сложно и мягко говоря, нестабильно. Я вообще не уверен даже, что на других языках есть что-то подобное, таких масштабов. А если и есть, хотел бы я посмотреть, как они решают вопросы сотен зависимостей и десятка платформ (что кстати, представляет собой прекрасный пример для подражания).

И что касается WebRTC, она тоже не существует отдельной библиотекой (хотя, на Гитхабе смотрю уже кто-то сделал отдельно). Это часть проекта Chromium (или Mozilla). И нужны определенные усилия, чтобы ее оттуда выдрать.

Неужели это не «сложности»?

Это твоя работа.

Раз уж ты спросил, то в Qt есть модуль QtWebEngine, который инкапсулирует Chromium вместе с WebRTC. Если нужна готовая либа — то вот готовое решение.

QtWebEngine это UI, клиентская часть, в то время как WebRTC часто нужен для серверной части, там Qt не подходит. Да и Chromium тоже может использоваться для рендеринга на бекенде. Так что Qt это конечно удобно, но мало где годится, часто нужна сборка native. Кроме того, лицензии Qt еще не в каждом проекте подходят.

но достаточно несложно сделать и свой билд

Несложно?! Я собирал как-то Qt под Android, у меня неделя или 2 ушло чтоб это сделать, пока подружились между собой версии SDK, NDK, всякие gradle или что там было, весь зоопарк.

И пилят для себя, а не для тебя.

Если бы это было так, то гугл не занимался бы написанием инструкций как это собрать и тулчейны не за чем было бы выкладывать (depot tools).

И что касается WebRTC, она тоже не существует отдельной библиотекой (хотя, на Гитхабе смотрю уже кто-то сделал отдельно). Это часть проекта Chromium (или Mozilla). И нужны определенные усилия, чтобы ее оттуда выдрать.

Существует: webrtc.github.io/webrtc-org/native-code
Да, это часть проекта Chromium, тесно связана с ним, но тем не менее самим гуглом выдрана и существует как отдельная либа. И когда нужно собранную либу подключить в свой проект, начинается веселье.

Неужели это не «сложности»?

Это твоя работа.

Они от этого перестают быть сложностями? :)

Кроме того, лицензии Qt еще не в каждом проекте подходят.

Ну, если LGPL не подходит, тогда я не знаю, как Chromium подходит. Там есть сторонние зависимости, которые чисто под LGPL.

Если бы это было так, то гугл не занимался бы написанием инструкций как это собрать

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

но тем не менее самим гуглом выдрана и существует как отдельная либа

По-моему наоборот — она изначально была отдельным проектом, а потом стала как часть Chromium. Но сейчас уже не важно.

Ну, если LGPL не подходит, тогда я не знаю, как Chromium подходит. Там есть сторонние зависимости, которые чисто под LGPL.

В Qt не все компоненты под LGPL. А использование одного (QtWebEngine) потянет за собой и другие. Но я давно не вникал в детали лицензирования, тут спорить не буду какие под какой лицензией.

Они хотят не чтобы все взяли и к себе утащили, легко и просто, они хотят чтобы все желающие могли участвовать в проекте. Потому что этот проект — браузер в первую очередь.

Опять таки, если посмотреть на вебртс, который якобы только часть бразуера, то они работают над апи (понимать как публичные хедеры):
«The legacy API directories, in addition to things that genuinely should be part of the API, also contain things that should not be part of the API. We are in the process of moving the good stuff to the api directory tree, and will remove directories from the legacy list once they no longer contain anything that should be in the API.»
webrtc.googlesource.com/...​-api.md#legacy-disclaimer
Зачем апи, если это продукт «для себя»?

В Qt не все компоненты под LGPL.

QtWebEngine не тянет ничего, что выходит за LGPL. См. здесь

Зачем апи, если это продукт «для себя»?

Затем, что это правильно. Для себя или нет, не важно.

Затем, что это правильно. Для себя или нет, не важно.

Это может быть правильным только для либы, то есть для внешнего использования. Для внутреннего использования, в смысле просто как части браузера, абсолютно пофиг какая там структура хедеров. Что говорит о том, что предполагается самостоятельное использование вебртс, вне браузера. Без qmake, cmake и прочего, и что является сложным или иногда супер сложным, вот в чем собственно мой поинт

webrtcbydralex.com/...​hard-can-it-be/#more-1013

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

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

Без qmake, cmake и прочего, и что является сложным или иногда супер сложным

Тут вообще не понял. А что не сложное, GNU autotools и make? Серьезно?

И с которой проще работать, когда она отдельно от всего остального кода.

Проекту Chromium все равно как с WebRTC работать. Потому что при сборке Chromium WebRTC собирается вместе с ним, в одной экосистеме, то есть одним тулчейном. Для хромиума нет проблемы, что включая один хедер вебртс, тянутся за ним еще десяток или сотня других. А когда используешь WebRTC как отдельную либу, собрав ее бинарь и подключая хедеры себе в проект, то проблема есть. Должны быть чисто публичные хедеры, которые не тянут за собой внутренние.
«Headers in api/ should, if possible, not #include headers outside api/. It’s not always possible to avoid this, but be aware that it adds to a small mountain of technical debt that we’re trying to shrink.»
webrtc.googlesource.com/...​/heads/main/api/README.md
Я сомневаюсь, что делается это просто потому что так правильно. Пусть даже гуглу это не интересно (использовать их код вне браузера), но есть же вроде мейнтейнеры, это ж оупенсорс, которым это интересно, вот может это их инициатива.

Тут вообще не понял. А что не сложное, GNU autotools и make?

Я имел ввиду что сборка и интеграция WebRTC native сложная.

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

Вот ты сам и нашел решение своей проблемы) Делаешь свою обертку, со своим нужным публичным API, который не тянет потроха WebRTC. И жизнь сразу становится проще.

Я имел ввиду что сборка и интеграция WebRTC native сложная.

Сложно — это когда тебе надо собрать под Apple M1 код, который никто до этого туда не портировал. Когда х.з. что делать со всеми этими SSE и AVX и прочими just-in-time компиляциями. И чтобы оно не только собралось, а еще и работало, хоть как-то. Вот это веселье, да.

Вот ты сам и нашел решение своей проблемы) Делаешь свою обертку, со своим нужным публичным API, который не тянет потроха WebRTC.

Да я-то знаю решение этой проблемы, не в этом вопрос же. Кстати, придется код обертки встраивать в проект вебртс. И еще эту обертку делать pure C интерфейсом, если собирать WebRTC встроенным тулчейном, потому что твой компилятор и тулчейна не будут генерить совместимый плюсовый код. Либо решать задачку со звездочкой — собирать вебртс со своим тулчейном. Что вообще ни разу не

И жизнь сразу становится проще.

и не

Не знаю, какие сложности со сборкой?

Ты путаешь две вещи — собрать чужой проект по инструкции, и интегрировать часть чужого проекта в свой. Первое — не вызывает затруднений (потому что есть инструкция). А второе — никто не обещал, что будет легко. По крайней мере, 5 лет назад, когда я этим впервые занимался — вот такого «твой компилятор и тулчейна не будут генерить совместимый плюсовый код» точно не было. Что под виндой (msvc), что под маком (clang) оно было совместимо. Как оно сейчас, не знаю, код в Гугле сильно поменялся, и утащить его снова — это начинать все сначала.

Ты путаешь две вещи — собрать чужой проект по инструкции, и интегрировать часть чужого проекта в свой.

Не понял, о чем ты, ну соберу я по инструкции WebRTC и получу бинарь либы, и дальше что? Его ж надо интегрировать, что я путаю?
Тред начался с обсуждения сложности сборки и переиспользования чужого кода. Вот на примере использования WebRTC я объяснил насколько это может быть сложно, при чем оба шага: и сборка, и интегрирование.

По крайней мере, 5 лет назад, когда я этим впервые занимался — вот такого «твой компилятор и тулчейна не будут генерить совместимый плюсовый код» точно не было. Что под виндой (msvc), что под маком (clang) оно было совместимо.

На винде и маке не смотрел как, но думаю ситуация такая же как и на линуксе, а под линукс в тулчейне находится свой компилятор clang и своя std либа. Я скинул выше статью, в ней упоминается

Deal with C++ standard (98,11,14, 17),
Deal with C++ standard implementation lib mismatch,
...

Плюс к этому можно добавить что у тебя вообще может быть gcc :) Дальше, по дефолту отключены exceptions, rtti и прочие казусы

На винде и маке не смотрел как, но думаю ситуация такая же как и на линуксе

Странно, что ты не упомянул, сколько еще есть разных дистрибутивов Линукса, и какие проблемы ждут на этапе деплоя) Так и запить можно, с тоски)

так в мире бэкенда и спиться можно, много ли там маков и винды? Всё ж линуксы кругом :)

Первое — не вызывает затруднений (потому что есть инструкция)

бугага, якби ж ще автори оновлювали свої інструкції і не писали їх по принципу «збілдилось на моєму комп’ютері один раз значить норм». Постійно чогось не вистачає, версії залежностей не прописані і вони перестають бути сумісними одна з одним через деякий час, потрібно шукати якісь нерелізнуті патчі (бо реліз раз в 3 місяці) щоб воно збілдилось на твоїй платформі.

Це про щось конкретне, чи так — з розряду байок в курилці?

Да пожалуйста — сборка (не деплой) WebRTC под CentOS: тулчейн хочет более новую версию glibc %)

WebRTC под CentOS: тулчейн хочет более новую версию glibc

Так может, проблема не в C++, а в Линуксе? Под виндой-то с маком, такого зоопарка нет) И проблем, как видишь — нет) Там Майрософт даже C-рантайм теперь сделала обратно совместимым, последняя версия работает со всем предыдущим кодом (начиная с VS2015). И vcpkg — эта штука нереально экономит время. Просто в 1000 раз.

Так может, проблема не в C++, а в Линуксе?

Но с другими языками таких проблем под линуксом нет, как у С++? Взять тот же Rust, интересно, будут ли там такие же проблемы, что ABI разных версий несовместимы, как в плюсах до и после 11 стандарта? Если что я не знаю Rust, действительно интересно. Или как в С++, что один модуль собран с исключениями, а другой без, и имеем неочевидный UB, когда передал колбэк и он кидает исключение из стека вызовов модуля, который собран без исключений. Это и для винды с маком актуально.

И vcpkg — эта штука нереально экономит время. Просто в 1000 раз.

Ну дэк это же «пакетный менеджер», такое и в мире линуксов есть, я тож могу написать в убунте «apt get install» :)

Итого, юзайте C API. Оно должно быть почти у всех серьезных библиотек, поддерживающих интерфейсы для нескольких языков.

Таки да, взять вот Chromium, у него нет С интерфейса, ребята сделали Chromium Embedded Browser Framework. Он оборачивает Chromium и предоставляет С интерфейс, а потом несколько оберток для разных языков, которые используют этот pure C.

Но кстати та проблема с исключением, которое пролетает через стек вызовов чистых С методов, все равно остается.

C не поддерживает исключения, поэтому если исключение пытается пролететь через C API — то это явно недоделаный API.

Не, не в апи проблема, вот пример. C интерфейс принимает колбэк, указатель на функцию. Где-то он там хранится и потом вызывается из глубины С методов. В этом колбэке мы кидаем исключение и ловим его на нашей стороне плюсовой, думая что всё ок, а оно не ок, потому что пролетает стэк вызовов С

Ну как-бы не было синьора на код ревью, который бы пять минут матюкался, когда увидел такой колбек для С.

По-хорошему, все, что отдается в С, лучше помечать extern «C» хотя бы для следующих поколений джунов, чтобы исключений не понатыкивали.

Исключения могут быть и не явно натыканы. Вот смотришь на ревью на тело метода колбэка — вроде норм выглядит, какие-то вызовы других методов, ниче такого, а потом бац — где-то в глубине этих вызовов std либа кинула исключение. Допустим, безобидный метод at() у контейнера, который там был вставлен еще задолго до того как синьор тут работать начал :)

Переписать нафиг на гексагоналку с сообщениями, и не мучать колбеки бизнес-логикой. В результате код отвяжется от библиотек, и очень возможно, что станет shared nothing.
ithare.com/...​el-is-considered-harmful

В нас вся комунікація між потоками на асинхронному паб-сабі.

Напевне найкраще архітектурне рішення в моєму житті.

До речі, як на практиці паб-саб порівняно з акторами (комунікація між потоками на асинхронному меседжингу, де для посилки меседжа надаєш вказівник на клас-адресат)?

У меня бывало, что пролетали. Бутерброд C++ возле main, С внизу остальной программы и C++ в библиотеке.
C поддерживает исключения в зависимости от компилятора.
MS делает это единым механизмом с SEH, это очень удобно.

Взять тот же Rust, интересно, будут ли там такие же проблемы, что ABI разных версий несовместимы, как в плюсах до и после 11 стандарта?

А в Питоне все хорошо? Когда в 3.x синтаксис поменялся, и на совместимость авторы забили. Это хоть и не ABI, но все равно были проблемы, и очень долго. В дотнете, при переходе с CLR 1.1 на 2.0 тоже была потеряна совместимость — новый CLR, новый формат метаданных. Можно еще плюсы под DOS вспомнить, и тоже причитать, что ничего теперь не работает... Смешно. В рамках разумного, все работает. А Линукс, ну они сами себе буратины, со своим зоопарком.

Или как в С++, что один модуль собран с исключениями, а другой без, и имеем неочевидный UB

Исключения — зло, в том числе и по этой причине. Я считаю, что там где они используются, это bad design. Средства, которые есть в языке — это не заповеди, которым ты обязан следовать. И новые стандарты туда же, к слову (C++20).

Ну дэк это же «пакетный менеджер», такое и в мире линуксов есть, я тож могу написать в убунте «apt get install»

Понятно что можешь, но он появился всего пару лет как. Без него, я бы с тобой полностью согласился сейчас — было мало радости этим заниматься. То что было 10 лет назад, и сейчас — это небо и земля.

Исключения хороши для нескольких задач, например — выпрыгнуть из парсера или из работы с файловой системой по ошибке. Не зря в С есть longjmp для тех же целей.

В общем случае, использование исключений может несколько ускорить оптимистический путь выполнения программы, так как не нужно проверять, что вернули вызываемые функции (они ничего не возвращают, а при ошибке бросают исключение).

В общем случае, использование исключений может несколько ускорить оптимистический путь выполнения

Но при этом, код ошибки ты можешь игнорировать, если он тебя не интересует, а исключение игнорировать нельзя. И try-catch без finally даже практически, не имеет никакой ценности. И в случае файловой системы, экономия на проверке ошибок не будет иметь никакого эффекта, т.к. I/O операции выполняются на порядки дольше, чем if/else. Особенно сейчас, когда современные процессоры спекулятивно выполняют обе ветки условия. А парсеры... если там такой стек вызовов, что дешевле выбросить исключение, чем все проверять, то не лучше ли переделать рекурсию в линейное выполнение, и использовать паттерн do-break-while(0)?

И try-catch без finally даже практически, не имеет никакой ценности.

Имеет — освободить ресурсы и показать окошко с ошибкой или отправить 404 Not Found.

А парсеры... если там такой стек вызовов, что дешевле выбросить исключение, чем все проверять, то не лучше ли переделать рекурсию в линейное выполнение, и использовать паттерн do-break-while(0)

1) Неверная интерпретация. Дешевле не проверять, чем проверять, кодга ошибки не было. А ошибки на то и ошибки, чтобы бывать редко.
2) Не знаю такого паттерна, и там нет рекурсии — там одна функция вызывает другую, та — третью... К примеру, на верхних уровнях из пакетов собирается сообщение, глубже идет парсинг синтаксиса начинки, потом из синтаксиса могут собирать семантику. Даже для какого-нибудь языка программирования там полно разных видов ошибок, которые могут вылезти при компиляции или интерпретации на разных уровнях. И надо красиво освободить выделенные ресурсы и написать в консольку что именно в коде не понравилось. Все равно компиляция зафейлилась, продолжать не нужно, finally бесполезен.

Дешевле не проверять, чем проверять, кодга ошибки не было

Не знаю как тебе, а мне часто приходится разгребать кейсы саппорта. Поэтому ценность логов для меня — абсолютна. Поэтому везде где это может иметь смысл, я стараюсь логировать ошибки — что хотим сделать, что не получилось, по какой причине. И в случае парсинга XML это тоже помогает, т.к. XML бывает кривой (никто не совершенен, да). И исключения, только усложняют код, без какого-либо осязаемого бенефита. То что ты приводишь в пример — легко решается без исключений, просто abort() / exit() сообщением и кодом. Раз продолжать не нужно. И тот пример, где дешевле не проверять код возврата — извини, но это экономия на спичках. А там где надо реально экономить на спичках (super-high-performance), например в телекоме или финансах, то там и исключениям в принципе не место.

Не знаю такого паттерна
// Declare resources
bool result = false;
do {
  if (!precondition1)
    break;
  if (!precondition2)
    break;
  result = do_action();
}
while (0);
// Cleanup resources
// Act on result
То что ты приводишь в пример — легко решается без исключений, просто abort() / exit() сообщением и кодом.

Это если ты можешь себе позволить убить процесс, когда юзер ввел неверную команду, или прилетел битый пакет. На бекенде не прокатит под нагрузкой.

Пример

do-break-while(0)

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

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

Все равно, не понимаю, чем размотка стека быстрее множества последовательных return по сбою. Писать может и быстрее, за счет того что не нужно if (!result) return набирать. Но и парсеры часто не пишутся вручную, а генерятся по грамматикам, специальными тулами. Что делает и это преимущество нерелевантным.

Все равно, не понимаю, чем размотка стека быстрее множества последовательных return по сбою.

Снова не туда. Вот же:

1) Неверная интерпретация. Дешевле не проверять, чем проверять, кодга ошибки не было. А ошибки на то и ошибки, чтобы бывать редко.

Быстрее работает, когда все правильно (нет повторных проверок ошибок на всех уровнях, кроме нижнего). И медленнее работает, когда ошибка. Мы надеемся, что ошибки происходят настолько редко, что это окупится.

Быстрее работает, когда все правильно (нет повторных проверок ошибок на всех уровнях, кроме нижнего).

не совсем «кроме нижнего» но «кроме конкретного» он же ж да по логике выходит «нижний» но опять же ж отходя от концепции к реализации имхо тут у «плюсов» своя проблема в сильной сложности и соотв. не детерменированности на компиляции самого языка включая коллбеки и виртуальные функции потому что иначе можно longjump на прямую прямо на обработчик прямо замапленный даже по типу прямо на этапе компиляции пусть даже с раскруткой стека как то в свою очередь «плюсовыми» деструкторами в очереди на стеке

потому я давно говорю «плюсы» ушли не туда от слова совсем и теперь там отдельно код отдельно компилятор отдельно имплементация и вроде как и код чтобы писать легко но теперь уже нельзя на столько как на каком-нибудь разноцветном свифте

www.linkedin.com/...​-6809383833146839040-fszr

потому что начинает держать уже и компилятор и реализация не говоря уже об обратной совместимости и ABI ))

и к.м.к. выхода уже нет даже просто исходя из сложности самого стандарта уже 11 да и сам тот же ж stl тут тоже не пасёт задних

ЗЫ: а ну и да шутка ли теперь грубо половина языка типа реально «написана» именно «на stl» или же ж «как stl» и без него сам язык ну как бы б можно но в общем скорее бес полезный для прикладного применения чем на оборот но при этом «сам stl» писан на том же ж языке ... ага простите а зачем?

Мы надеемся, что ошибки происходят настолько редко, что это окупится.

Если бы процессоры были все еще на уровне первых Pentium, то может быть. А современные архитектуры давно используют speculative execution, т.е. первый же чек на ошибку, автоматически включает все последующие бранчи, которые уже закешированы и только и ждут коллапса волновой функции когда тот кот сдохнет))

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

В современных процессорах там что-то вроде N позиций (например N=16) на каждый условный переход (из запомненных) с фишкой в одном из них, если сработал — фишка сдвигается на 1 вправо, нет — влево. В реальных версиях сложнее, но там уже идут гадания на секретах.

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

Если переход ещё не попадал в базу запомненных (сделана как кэш с алгоритмом на основе LRU), то сейчас на основных арихитектурах считается, что переход назад вероятен (фишка ставится куда-то вправо, например, 13 из 0..15), а вперёд — маловероятен (например, 2).

Компиляторы собирают код так, чтобы весь основной путь (без обработки исключений) лежал вместе в основном теле функции. Код, выполняемый при обработке ошибок, кладётся отдельно, причём последние годы он может быть даже не вплотную к основному телу, а отделён от него (и сложен в другой сегмент линкера). Переходы на него могут быть явными, если это явная проверка на уровне ассемблера, или неявно через таблицу переходов для исключений — в современных стилях (если не форсировать sjlj-exceptions). И все эти явные переходы делаются вперёд (то есть по описанному — процессор по умолчанию считает их маловероятными).

В результате, даже если проверки на исключения делаются явно, затраты процессора на это минимализированы.

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

Вот я провёл простейший тест:

void foo();
void bar(int);

void moo() {
  try {
    foo();
  }
  catch (int& z) {
    bar(z);
  }
}

Компилирую GCC 9.4.0, -O2:

_Z3moov:
.LFB0:
        .cfi_startproc
        .cfi_personality 0x9b,DW.ref.__gxx_personality_v0
        .cfi_lsda 0x1b,.LLSDA0
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
.LEHB0:
        call    _Z3foov@PLT
.LEHE0:
        popq    %rbp
        .cfi_remember_state
        .cfi_def_cfa_offset 8
        ret

Видите код для исключений? И я не вижу. Зато куча пометок для ассемблера и линкера.

Идём дальше:

       .section        .text.unlikely
        .cfi_startproc
        .cfi_personality 0x9b,DW.ref.__gxx_personality_v0
        .cfi_lsda 0x1b,.LLSDAC0
        .type   _Z3moov.cold, @function
_Z3moov.cold:
.LFSB0:
.L2:
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        subq    $1, %rax
        jne     .L10
        call    __cxa_begin_catch@PLT
        movl    (%rax), %edi
.LEHB1:
        call    _Z3bari@PLT
.LEHE1:
        popq    %rbp
        .cfi_remember_state
        .cfi_def_cfa_offset 8
        jmp     __cxa_end_catch@PLT
.L10:
        .cfi_restore_state

Заметили, даже секция другая — .text.unlikely вместо .text? Линкер складывает весь такой код вместе, но отдельно от основного .text, чтобы уменьшить размер основного кода и тем самым улучшить его кэширование. И кто такой _Z3moov.cold? А это и есть сборник обработчиков catchʼей.

Там ещё дальше данные таблиц переходов по исключениям, не буду приводить — сами можете повторить.

Вывод: ваш рассказ про «первый же чек на ошибку» резко сомнителен по одному параметру (закэшированность «бранчей») и неверен по другому (что про этот «чек на ошибку» в варианте с исключениями просто нельзя говорить, а если исключение произошло, то это всё равно другой блок кода и предсказания по нему не влияют на основной код).

Да, в таком виде оно конечно меняет дело. С другой стороны, есть аттрибуты [[likely]], [[unlikely]] с помощью которых можно и обычный код оптимизировать подобным образом.

С другой стороны, есть аттрибуты [[likely]], [[unlikely]] с помощью которых можно и обычный код оптимизировать подобным образом.

Можно, да.
И с ним будет похожее.
Но 1) Место на команду явной проверки.
2) Код unlikely path рядом с основным.
3) Минимальная (считаем 1/10), но всё же возможная трата ресурсов на этот путь.

Ну и likely/unlikely могут учитываться не только в размещение веток. Например, компилятор может захотеть написать код для обоих вычислений сразу, если они примерно равновероятны, а затем через cmov (csel) выбрать одно значение из двух. Или не делать так, если ему объяснили вероятности.
Так что расстановка весьма полезна.

Например, компилятор может захотеть написать код

у цьому трапилася ключова проблема «плюсів» вони захотіли самі царствовать і всєм владєті

оригінально ідея «плюсів» додати ооп до чистого сі як шлях подолання проблеми зростаючої складності і далі пішло питання у тому що «у плюсах» лишилися лише «плюси» абсолютно приховані та закриті стандартом та компілятором

навіть objective c пішов іншим шляхом і доречі здравствує до сіх днів успішно передавши естафети swift але лишившись

скажімо єсть ms word у ньому можна писати внутрє на vb а можна через com прокинути його об’єктну модель до будь чого того же ж сі/сі++ але навряд чи спадає на думку писати у ворд на ворд

хоча так до речі у самому ворд є шаблони ворд які вирішують дуже прикладне питання самого ворд а не усього взагалі

Это если ты можешь себе позволить убить процесс, когда юзер ввел неверную команду, или прилетел битый пакет. На бекенде не прокатит под нагрузкой.

что так? запускай всё входящее отдельно для каждого в отдельный процесс и будет тебе щасте ))

Это если ты можешь себе позволить убить процесс, когда юзер ввел неверную команду, или прилетел битый пакет. На бекенде не прокатит под нагрузкой.

что так? запускай всё входящее отдельно для каждого в отдельный процесс и будет тебе щасте ))

ласкаво просимо у Erlang/OTP

Можно еще плюсы под DOS вспомнить, и тоже причитать, что ничего теперь не работает... Смешно. В рамках разумного, все работает.

Ну прям обсмеяться, когда берешь разные версии компилятора и у них несовместимый ABI :)
For example, if you compile an object using GCC 4.9 and -std=c++11 and another object with GCC 5 and -std=c++11 you will have problems. The C++11 support was experimental in GCC 4.x, and so there were incompatible changes between the GCC 4.9 and 5 versions of C++11 features. Similarly, if you compile one object with GCC 7 and -std=c++17 and another object with GCC 8 and -std=c++17 you will have problems, because C++17 support in GCC 7 and 8 is still experimental and evolving.

А Линукс, ну они сами себе буратины, со своим зоопарком.

Про visual studio:
Static libraries or object files compiled using the /GL (Whole program optimization) compiler switch or linked using /LTCG (Link-time code generation) aren’t binary-compatible across versions, including minor version updates.
То есть статическая либа собранная 16.7 toolset уже не совестима с 16.8.

Исключения — зло, в том числе и по этой причине. Я считаю, что там где они используются, это bad design.

Bad или не bad, но у разных авторов либ свое мнение на этот счет, и дофига популярных либ исключения используют (а некоторые как вебртс — нет), так что часто это не вопрос предпочтений, а необходимость скрестить носорога с бегемотом.

Понятно что можешь, но он появился всего пару лет как. Без него, я бы с тобой полностью согласился сейчас — было мало радости этим заниматься. То что было 10 лет назад, и сейчас — это небо и земля.

Я не совсем понял, как тут помогает менеджер пакетов, мы ж обсуждаем сложности сборки? Ты имеешь ввиду, что можно не собирать самому, а установить собранный уже кем-то пакет?

То есть статическая либа собранная 16.7 toolset уже не совестима с 16.8.

В случае /LTCG это абсолютно естественно и объяснимо. Не аргумент. Про GCC я уже сказал — свобода имеет обратную сторону. Особенность платформы.

так что часто это не вопрос предпочтений, а необходимость скрестить носорога с бегемотом.

В своем коде/проекте, у тебя есть выбор. Как минимум, использовать throw, или нет. Из-за этого же все проблемы?

Я не совсем понял, как тут помогает менеджер пакетов, мы ж обсуждаем сложности сборки?

Как помогает? Ну представь, что у тебя нет apt, и нет пакетов как таковых. И ты хочешь установить Apache, PHP и MySQL. Вот примерно такая же ситуация. И нет, пакеты не собраны кем-то, у тебя просто есть удобный инструмент для этого.

В случае /LTCG это абсолютно естественно и объяснимо. Не аргумент.

Ок, это специфический случай, на винде после 2015 студии попроще с совместимостью стало.

Про GCC я уже сказал — свобода имеет обратную сторону. Особенность платформы.

Это уже объяснение откуда берется сложность, а вначале было непонимание где сложность вообще ;) Слишком много платформ основано на линуксе, чтобы записать это в какую-то экзотику и пренебречь.

В своем коде/проекте, у тебя есть выбор. Как минимум, использовать throw, или нет. Из-за этого же все проблемы?

К примеру, я отказался от использования исключений в проекте, не кидаю их сам, но что делать с теми third party библиотеками, которые их кидают?

Как помогает?

Все равно не понятно. Вот я собирал когда-то давно на винде Qt под андроид, разбирался с зоопарком, убил около 2 недель. Второй пример недавно — собирал на винде CEF из исходников, по инструкции, все равно сборка постоянно спотыкалась. Правда, нужна была не последняя версия, а годичной давности. Потратил почти неделю (учитывая, что сборка очень долгая, и каждая попытка занимает много времени). Чем тут поможет менеджер пакетов?

Слишком много платформ основано на линуксе, чтобы записать это в какую-то экзотику и пренебречь.

Так это и не экзотика, а особенности платформы. В Питоне тоже не все хорошо с пакетами, тоже возникают проблемы. Везде есть свои сложности.

К примеру, я отказался от использования исключений в проекте, не кидаю их сам, но что делать с теми third party библиотеками, которые их кидают?

Ну что, обрабатывать. Ты же не хочешь чтобы процесс крешился? Просто включаешь в своем проекте -fexceptions, раз они тебе нужны, и обрабатываешь. Это ничего не ломает. А вот прежде чем выбрасывать, надо хорошо подумать.

Просто включаешь в своем проекте -fexceptions, раз они тебе нужны, и обрабатываешь. Это ничего не ломает.

Не так просто, тут уже упоминалось какие бывают грабли с этим. Здесь
dou.ua/...​rums/topic/42888/#2607219
и здесь
dou.ua/...​rums/topic/42888/#2607805

В нас була одна закрита бібліотека під камеру, з ексепшинами.

Кожне звертання до їхніх функцій обгорталося в try-catch.

На щастя, це вже в минулому.

К примеру, я отказался от использования исключений в проекте, не кидаю их сам, но что делать с теми third party библиотеками, которые их кидают?

заворачивай и игнорь ))

Ні, обгортай, логуй і обробляй

Взять тот же Rust, интересно, будут ли там такие же проблемы, что ABI разных версий несовместимы

Не будет, потому что (checking notes) ABI Rust не имеет спецификации, нет дает никаких гарантий совместимости ABI двух разных версий, поэтому каждый раз когда вы компилируете программу, все зависимости пересобираются из исходного кода (да, все библиотеки Rust распространяются в исходном коде), одним и тем же компилятором.

1. Мультиплатформна бібліотека А не білдилась по інструкції під вінду (прийшлось тащити патч з пулл-реквестів)
2. Бібліотека B встановлювала бібліотеку С з пакет менеджеру без вказання конкретної версії і використовувала код який потім став deprecated
3. В інструкції бібліотеки D ніслова як збілдити її зі статичним лінкуванням (хоча можливість є, інструкцію знайшов в багтрекері бо комусь теж знадобилось)

Все, я вже зрозумів — ваші проблеми пов’язані не з C++, як з таким, а з Лінуксом, бо там дике поле всіх цих пакетів та залежностей. Співчуваю.

Я вообще не уверен даже, что на других языках есть что-то подобное, таких масштабов.

Linux kernel?

И какой там «другой» язык?

Програміста «ядерного» ООП на чистому С на сучасний С++ доведеться донавчати правильним патернам, і навпаки, в дусі що наприклад, void* не просто не вимагається, а і взагалі небажано кастувати вручну в С, або використання memset() для ініціалізації структур не (так часто) стріляє по ногам, і так далі.
На даному етапі, язик не повернеться назвати С і С++ одною мовою.

На даному етапі, язик не повернеться назвати С і С++ одною мовою.

Речь не об этом, а о системах сборки. И компиляторах, который у C и C++ один и тот же.

А що у систем збірки одинакове? Хто на autotools, хто на cmake, той же Лінукс поверх мейкфайлів свою кастомну реалізовує, або хтось тупо лабає мейкфайл під свій хост, забивши на портабельність і інформацію про те що понаставив системним пакетним менеджером в принципі.
А ще є «системи збірки» типу AOSP чи Yocto, які білдять тисячі ліб і апок в один дистрибутив, язик не повернеться назвати їх «одинаковими», і з вмінням з ними працювати не народжуються.
Моя особиста претензія до «С/С++» полягає у тому, що під цим розуміють широкий спектр знань мов і екосистем, який одна людина в принципі в себе не помістить, але кожен вважає що саме його підмножина є загальноприйнятою.

C/C++ це мова доволі низького рівня. Бо все інше — написано на цьому. Нижче рівень — більше складнощів. Якщо ти розробник C/C++ — це якось по-дитячому на це скаржитись. Це все одно, що прибиральник буде скаржитись на те, що бруд навколо. Або моряк, що нудить від шторму. Таке життя.

P.S.

типа вебртс

vcpkg install libdatachannel

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

qmake — його вже немає. Qt самі на CMake. А KDE здається нісколи qmake і не юзали — так з automake на CMake і перейшли. А проектів без Qt що юзалиб qmake — 0. qmake — забудьте

qmake — його вже немає

З якого переляку, його вже немає? Вам його хтось заборонив? Qt перейшли на cmake, бо вирішили хай краще гівно, але всюди розкидано та всі їдять. Ніж своє допилювати :(

qmake — забудьте

Щас )))

Якщо є проджект на qmake і все працює, тоді забувати не треба, але нічого нового на ньому починати не треба, особливо щось без Qt — так думаю.
А в чому така перевага qmake над CMake? Питаю бо працював з тим і тим, перевак qmake не побачив. Хоча jom.exe непогана штука на вінді.

Може це суб’єктивно, але мені qmake був зрозумілий відразу, інтуїтивно, як що робити і як воно працює. Він має зручний синтаксис та не намагається бути ще однією мовою, яку ти маєш вивчати (та пам’ятати). І так, звісно — він був створений для Qt, але як на мене, то й Qt був створений, щоб зробити C++ придатним для людей)

Хоча jom.exe непогана штука на вінді.

Jom то не частина qmake, він відноситься до QtCreator. Хоча можна вважати, що то все одна екосистема. В останніх релізах, QtCreator навіть краще за студію, і на 500% краще за XCode.

Можна обмежитись тим, що вона багатопарадигмова з повним доступом до ресурсів, а також історично широковживана. Але тоді статті не буде :)

Ще б не кожного року завозили в стандарт фічі різних бібліотек, а лиш публікували стандартизовані API до відповідних реалізацій, гарантовано без UB...

Після С++11 мова стала смітником.

Ви не платите за те, що не використовуєте.
Те, що ви використовуєте, настільки ж ефективне, як і те, що ви могли б писати вручну.

Нажаль, з урахуванням проблем Data Locality в різних VTable та відсутністі ефективних оптимізаційних проходів в компіляторах (polyhedral штуки всякі ще не дуже доступні), та відповідних SSA форм (під mlir llvm мож завезуть mSSA aSSA tSSA нормальне), це твердження хибне (давно стало мемом).

Якби воно дійсно так працювало — Qt був би такий же портабельний як й Glib, й поточну реалізація ООП Glib в рамках GObject, на голому С, можна було б порівнювати з усілякими MОС кодогенераторами, що й як раз вирішують питання життєвого циклу об’єктів, яке не вирішувалось самою мовою (в Qt6 C++20 вже норм)... а в OpenCV не треба було б inline’ити купу simd intrinsic’ів бо LLVM/GCC «не вміє нормально в усі процессорні інструкції» майже будь якої архітектури.

На ринку, загалом, немає проектів не те що «по останнім стандартам», а хоча б просто з налаштованими статичними аналізаторами та автоматичним форматуванням коду... «бо бізнесу треба доказати», й оперує лише задопомогою «шостого почуття п’ятої опорної точки» бо «знати — страшно». Про вплив нефункціональних вимог на життєздатність продуктів — історія замовчує... «магічно для бізнесу» то на прибуток «не впливає».

З тендецією «ми хочемо допомогти» Україні — тому «звалюємо увесь непотріб за копійки» під час війни, й примушуємо працювати з тим, з чим більше ніхто в світі не хоче працювати... ситуація з Legacy C++ помийкою, в принципі, ще гірша, ніж там з якимось Legacy Angular1 зі ставками по 10-13К$ fulltime, які ніколи не будуть виплачені, бо ніхто більше двох тижднів на проекті не затримується.

просто поток сознания — новые стандарты, Qt, SIMD, помощь Украине и Angular

а лайки тут ставят, похоже, просто за объем текста комментария
ну или за то, что чем больше текста, тем больше шансов, что там найдется фраза, которая придется по душе )

Ок, розшифровую ...

1. С++ VTable має Overhead, бо поточні компілятори занадто тупі щоб вирішити проблему Data Locality, та навіть не здатні використати увесь наявний набір інструкцій процессора... тобто «якщо переписати вручну» — завше буде швидше, й то обмеження вже суто цільових тулчейнів й відповідних фронтендів LLVM / GCC (фронтенд компілятора не має ніякого відношення до фронтенду в вебі)

2. ООП не працює «Ексклюзивно в С++», бо були більш портабельні й ефективні реалізації в рамках GLib, на С. Якби там в С++ дійсно був би нормальний дизайн — не потрібна була б кодогенерація для прозорого RC/ARC, зараз наче стало краще в нових стандартах.

3. Але легасі стільки, що про нові стандарти мови й не може бути. Організаційна зрілість проектів на нулі — продають AdHoc за Agile. Суцільна підміна цінностей на ринку, й повний Ship It обмазаний мемом здивованого пікачу, що «від надійності рішень, виявляється, залежить рівень продажів».

4. Під час війни Legacy проекти з надзвичайним Turnover’ом потрапляють до України під виглядом «допомоги», хоча на справді там навіть немає ніяких гарантій оплати праці, незважаючи на високий рейт. Пойнт про «якби більше платили», а по факту — цифри в кеш не завжди зараз переводяться. Ситуація дуже схожа на ринок Legacy фронтенду.

Що Вам з цього не зрозуміло ?

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

кто когда говорил, что ООП это эксклюзивная фича плюсов? к чем вообще это?

насчет legacy все по разному бывает.
если проект забросили, по любому будешь ковыряться в окаменевших фекалиях.
а есть примеры, когда огромные кодовые базы двигаются в ногу со временем. вон Chromium уже для сборки C++17 требует.

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

Так это статистический факт.

Так нема ж пів лайка. Чи можливості лайкнути саме ту фразу, яка сподобалася.

Так і живемо...

Ми в свій час, починаючи з 1999-го, одну таку індуську помийку розгребли в щось набагато ефективніше і набагато краще підтримуване

Ну на Джині світяться кілька сіньорних позицій на 8500 без релокації. Але там вони досить специфічні.

Специфічні це блокчейн? Чи є щось нормальне?

В одній треба розробити гігабіт езернет для автомобілю. В іншій — хочуть автомотів архітектора з досвідом в секьюріті. Третя — 3Д рушій для чогось браузерного. Це десятки наразі.

а ще є HW SW OpenGL kernel uboot PCB розробник, по з.п. не знаю, але чим більше вимог, тим неадекватніша оплата

О, в цьому цікаво буде на пенсії поколупатися. Тільки не OpenGL...

а що не так з OpenGL?
чи вже заколупався?

Мені бракує уяви до 3д-графіки. Біти і байти — без проблем. 3д і фінтех — never again.

не зовсім зрозуміло як так:
3Д — це я так розумію на кшталт кубік рубіка крутити, нє?
що не так з фінтехом?

з.і.
а як щодо блокчейна і WEB3?

Що не так з фінтехом написав Талєб у своїй книжці «Statistical Consequences of Fat Tails: Real World Preasymptotics, Epistemology, and Applications», і я з ним згідний.

Ніколи не розумів, чому ціни моделюють логнормальним розподілом (бо ціни типу завжди додатні).

Якщо коротко, не розумію я цих фінансових моделей, які зовсім неінтуїтивні. Не складається картинка в голові.

ну хз, в мене хіба одна модель хвінтєха це арбітраж

Ми серйозно займалися variable annuities. Та й досі займаємося, тільки без мене.

це ж портофоліо менеджемент, в мене хвінтєх хіба асоціюється із бистрою Hастею, тобто HFT

Мені бракує уяви до 3д-графіки. Біти і байти — без проблем.

У 3D саме важке це матриці. Оті усі projections: model view, perspective, viewport... Як тільки їх освоїв то усе інше — це ті сами біти і байти. Нескінченная гонка за fps! Іноді текстури дійсно треба складати з байтів до бітів (якщо це наприклад маска якась), нарізати на шматочки аби воно добре влізло у текстурний кеш, та інше.

Тобі б сподобалось, але якщо разом з досвідченим програмувати — іноді трапляються важкі задачі — наприклад контурний рендерінг через трафарет.

схоже на якийсь ігрострой

Наприклад навігація у авто — усі навігатори ваг
Будь який augmented reality, медицина та інше

там же ще шейдери 3х видiв чи може вже бiльше

там же ще шейдери 3х видiв чи може вже бiльше

Мінімум це vertex та fragment. Усі інші опціональні. Tessellation, geometry, compute. Останній може бути зручним іноді, але скоріше це буде OpenCL або CUDA

Та мені вже на першому курсі сподобалось.

Я ніби розумію, що будь-яку матрицю можна привести до жорданової нормальної форми, де за кожним вектором бази масштаб і зсув... Але ні, розвертати кватерніони в голові не можу, бракує просторової уяви.

На трудовому навчанні в 4-му класі навчили мене думати в проєкціях, так простіше.

Хіба блокчейн тільки на плюсах? Мені здавалось там повно вакансій і на джаві

Підписатись на коментарі