Комментарии 73
А как оно должно было работать? Почитал и не особо понял. Допустим, аргумент должен быть строго больше нуля. Сейчас я делаю так:
using FooResult = std::expected<uint, FooError>;
FooResult Foo(int val)
{
if (val <= 0)
{
F.Error(L"val <= 0, val: ", val, L". Check this: …");
return std::unexpected(FooError::InvalidVal);
}
…
if (!SomeApiCall(val))
{
F.Error(L"SomeApiCall() failed for val: ", val);
return std::unexpected(FooError::SomeApiCallFailed);
}
…
return result;
}
Пишут, мол, хорошо отделять ошибки вызова API (!SomeApiCall(val)) от логики нарушения контракта (val <= 0) и последнюю формализовать в pre, чтобы её видели все, от статанализаторов до IDE, а чем хорошо — не пишут. Зачем мне их разделять? Что практически полезного я получу?
Почитал, как там у соседей. Пишут, что в C# был System.Diagnostics.Contracts, но он тихо умер. Вместо него юзают ArgumentNullException.ThrowIfNull и атрибуты с предикатами. Кастомные атрибуты, где предикат можно было записать хотя бы строкой — вот от чего я бы не отказался в плюсах.
И как оно должно интегрироваться в написанные системы? У кого-то — локальный логгер с круговой записью, у кого-то — отстуки на сервер, у кого-то — конфигурируется для прогона тестов.
Когда я смотрел примеры (сейчас не могу найти статью) мне показалось, что контракты в предложенном виде, это свое рода комбинация static_assert и рантайм проверок.
Да, примерно так и пишут, но я практически полезных (для меня) сценариев не увидел, и поэтому не понял, надо ли огорчаться и насколько сильно.
К сожалению из за контрактов зарезали весь стандарт в том числе рефлексию
Возможно по процедуре у них была возможность голосовать только за утверждение всего стандарта целиком.
Если люди за несколько десятилетий не способны придумать или хотя бы подсмотреть (например, у Scheme) нормальную процедуру голосования, то их способность породить вменяемый язык программирования вызывает сомнения. Это же всё элементы алгоритмического мышления.
Так на этом этапе голосуют за принятие в качестве стандарта уже готового вылизанного документа. Голосование за включение туда отдельных фич происходит сильно раньше, в рамках встреч рабочей группы.
за несколько десятилетий
Уже скоро девятое пойдёт, кстати.
Ну как бы ненормально, если фичи по отдельности приняли, а итоговый документ - нет. Это очевидный изъян процедуры.
Про девятое десятилетие не понял.
Про девятое десятилетие не понял.
Это процедуры ISO, которому вот-вот 80 лет стукнет.
Ну сам по себе ISO содержит ведь и эффективные комитеты наряду с неэффективными. Например, чтобы далеко от программирования не ходить, комитет по Фортрану был в расслабленном состоянии до середины 1990-х, а в 21 веке прямо очень шустро и по делу заработал. Комитет по PL/I не сделал ровным счётом ничего за всё время существования этого языка, по Паскалю – тоже почти ничего. Так что не только в регламенте ISO дело.
К сожалению из за контрактов зарезали весь стандарт в том числе рефлексию

Ещё это похоже на завтипы, с помощью которых компилятор может часть проверок провести во время компиляции.
std::vector::operator[] нет возвращает ошибку при неправильном индексе. Именно для таких кейсов и нужны контракты. Сейчас это делает через самописные макросы внутри реализации + документации.
К тому же там в будущем хотят вроде интегрировать контракты в фундаментальные языковые конструкции. Навроде проверки указателя на nullptr (но тут могу ошибаться, для этого есть отдельный пейпер не на C++26, см. p3850r1).
Если начать проверять индексы, это убьет пооизводительность.
Если бы у бабушки были колеса, то она была бы велосипедом. Убьет 0.3%.
И никто не помнит про `.at()`...
Для этого есть отдельный метод std::vector::at
Я для себя ответил на этот вопрос так: нарушение контракта означает что разработчик чего то не учел, либо если это библиотека или меж модульный интерфейс, то разошлись версии. В обеих ситуациях это непредвиденное событие, а значит программа должна немедленно остановиться и показать пользователю что у разработчика проблемы и надо ему об этом сообщить (ну или что уже сообщено если есть разрешение на сбор данных). Обычный АпликэйшнЭксепшен для этого не годится. В некоторых языках эксепшены уже разделены (например в java есть RuntimeException и к нему уже относятся другие ошибки разработчика типа деления на ноль. Ловить RuntimeException без особой на то нужды - моветон, т к это приведет к маскировки серьезной проблемы, которую в 99% случаев само приложение не устранит). Поэтому я вижу контракты, как во первых удобный сахар для этого. И во вторых по хорошему контракты должны продолжать логику интерфейсов. Если в пре-кондишен записано "не ноль", а в вызове передается константой 0, то компилятор должен возапить об этом и отказаться продолжать. Если этих двух вещей нема, то это хреновая реализация контрактов. Может быть поэтому комитет и отказал (я не разбирался детально а что собственно предлагали ибо плюсами не пользуюсь )
нарушение контракта означает что разработчик чего то не учел, либо если это библиотека или меж модульный интерфейс, то разошлись версии. В обеих ситуациях это непредвиденное событие, а значит программа должна немедленно остановиться
А представьте, что Foo() из примера выше сравнивает версии, а int val это номер билда. В реальности, правда, по-другому было, не так тупо, «со спецэффектами», но для упрощения пусть будет так. Однажды в чью-то светлую голову приходит идея помечать бета-билды отрицательными номерами. Опять же, в реальности было немного иначе, но пусть будет так. Вы обновляете всех юзеров до того билда, который знает о новом контракте, проверяете, что все юзеры онлайн обновлены, и переходите на новую схему. А потом выясняется, что часть юзеров всё-таки не обновилась… с Интернетом упорные неполадки, так часто бывает, в последнее время особенно… и у них при попытке проверить версию происходит нарушение контракта. Беда-караул, «программа должна немедленно остановиться». Если же Foo() возвращает FooError::InvalidVal, вызывающий вряд ли на этом основании выйдет, логичнее пожать плечами и считать, что обновляться не на что.
Это искусственный пример, можно убрать с сервера все отрицательные билды и вернуть все заблудшие «Аполлоны» под управление ЦУПа (кроме тех, кто видя, что программа выходит при старте, сносит её и переключается на программу конкурентов), но в реальности, ещё раз говорю, всё было сложнее, и вернуть удалось не всех. Не помню, сколько юзеров потеряли безвозвратно, что-то от 10 тысяч. Это были большие деньги. Никого не уволили, но все люди взрослые, стали думать, как больше не наступать на эти грабли. И пришли к тому, что не надо так писать: «Ой, контракт нарушен, прощай, мир». Кстати, насколько помнится, не я был причиной, хотя если бы был, то с удовольствием бы забыл. Но всё-таки думаю, что не я, поскольку и до этого не понимал, зачем нужны «синие экраны», даже с дорогими коллегами жаркий спор как-то вышел.
P.S. Минус не мой.
Странно что в C# умерло, т.к. оно там играло роль не защитного программирования, а метаданных для компилятора и ide. Можно было например описать, что при входе null ответ всегда false.
И тогда припроверке вызова функции на истинность, дальге можно было использовать указатель как гарантированный не null и компилятору и ide и стат анализу было норм, а без этого, нет контракта, хз что там внутри, значит валим сборку и проверки по разименованию нала.
А как оно должно было работать?
Ну, вот как-то так и отправили на доработку. А так вообще выглядит как consteval значения и какой-нибудь panic в рантайме при нарушении контракта.
>"а чем хорошо — не пишут. Зачем мне их разделять? Что практически полезного я получу? " - Прочитал сейчас в Википедии - оказывается, я неправильно понимал "Контрактное (ПО)": как фичи, предоставляемые конкретному пользователю (страны, предприятия, физлица), согласно конкретного Контракта (заключенного) с ним (совместимое с законодательством страны, политикой организации, желанием индивидуума). Т.е. либо "так собираемое" ПО, либо runtime - фиче-управляемое. И разработка - без хардкода (и даже не столько "величин", как самой "логики"):
- "зачем (разделять логику от ошибок)?" - чтобы исходники не (сильно) менялись, при добавлении и поддержке (особенностей) др.стран;
- "что полезного (я получу)?" - более инженерный код (более блочный -> более понятный -> ...).
PS: надеюсь, что распространятся AI-сервисы на контрактной основе (в моей интерпретации), как фактор нормирования "доверия к AI" (задавать и делать "только то, что в Контракте прописано").
И читаю "не смогли договориться (по контрактам)" - как по "актуально важному вопросу".
Если почитать того же Мейера, который продвигал контракты, то там важно, что он делает сразу три вещи:
runtime-проверка
документирование (не надо смотреть в код - из заголовка функции видно, какие требования)
правила наследования - если виртуальная функция переопределяется, то ужесточать контракт нельзя, а ослаблять можно.
Собственно без этих трёх вещей контракты нафиг не нужны (и пользы нет, и реализуются просто так).
То что вы пишите - это защитное программирование. Только надо не исключения кидать, а панику.
Подозреваю, контрактное программирование в С++ будут использовать не для того, чтобы обнаружить нарушение контракта на вызов - а для того, чтобы "оптимизировать" сгенерированный код, типа "удалим лишние проверки, т.к. контракт не должен быть нарушен".
Рискну предположить, что это было написано мне. Если да, в этом коде не бросаются исключения. Я придерживаюсь той точки зрения, что ошибки надо обрабатывать там, где есть максимум контекста, то есть на уровне вызова сразу после вызова.
100%. Будет всегда выключенный флаг компилятора. Как с границами массивов и остальным подобным.
Если добавить явные проверки, сейчас оно так и работает.
Может все же почитать pr?
из-за чего отклонили предложения всего стандарта C++26 целиком
Чот сурово, почему просто не выкинули контракты? Там много чего ещё революционного ожидалось, рефлексия, std::execution ит.д. И от контрактов ничего из этого вроде не зависело
А там вроде уже назначили новую встречу комитета. Там наверно и вырежут лишнее.
Чот сурово, почему просто не выкинули контракты? Там много чего ещё революционного ожидалось, рефлексия, std::execution ит.д. И от контрактов ничего из этого вроде не зависело
Учуяли время перемен. Уходит эпоха когда язык программирования был для человека, а не для машины. Сейчас наступает эпоха когда нужен язык программирования для машины. Все эти абстракции и синтаксические сахара только мозги ей пудрят. Вобщем комитет вовремя тормознул осмотреться и не создавать лишнюю работу разработчикам компиляторов.
Насколько я понял - процедура голосования предполагает только варианты - "за все" и "против всего", по за части голосования не было. Подразумевается что к этому моменту все проблемы должны быть устранены.
Казалось бы есть нормальные типы. И ассерты когда уже никак. С++ опять идет своим путем.
Нормальные типы реализуются бесплатно в рантайме, если надо. Тут проблем нет.
Да, выглядит как плохо управляемая смесь compile time (те же типы) и runtime проверок
С++ опять идет своим путем.
тем временем contract programming живет еще с восьмидесятых и C++ там не причем. https://en.wikipedia.org/wiki/Design_by_contract
Оно примерно там и осталось, поскольку добавляет опции (читай - сложностей) на ровном месте. То, что можно проверить статически проверяет тайп чекер. То, что нельзя - нужно безусловно отстреливать в рантайме. Больше сюда нечего ни добавить, ни убавить. DoC это фантазии на тему более умного тайпчекера чем у вас есть и магического рантайма в одном флаконе.
Весь CS уже был придуман в 80тых. Но не все взлетело или оказалось хорошим на практике. Контракты точно проиграли типам.
Я не понимаю, в этой ветке прочитавших определение DbC нет что ли?
Элементарная вещь, к типам отношения не имеющая.
Просто ассерты, сгруппированные в декларации функции. Вход данных, выход и инвариант.
Сразу для полноты, чтобы не утруждались Вики открывать - проверки только для отладочного режима.
Как предлагалось реализовать Комитетом, не смотрел.
Кто близок к науке носится с DbC со времен Eiffel. За эти годы было много надежд и попыток адаптировать подход в популярных языках программирования. Обычно что-то стоящее внедряется быстро. Здесь же за 40 лет нет практически ни какого прогресса. Хотя многие идеи по пути растаскали, упаковав в более удобные формы. Если вы хотите обобщенных доказательств в математическом смысле - используйте солверы теорем. Если можете довольствоваться честными случаями - пишите тесты. Примерно та же история как с функцональным программированием.
Просто ассерты, сгруппированные в декларации функции. Вход данных, выход и инвариант.
В своё время довелось поучастововать в разработке одной довольно важной и ответсвенной софтины еще когда никаких стандартов на С++ не было. И там практиковалась практика “паранаидальных ассертов” на почти всё, что можно и чуть чуть - что нельзя. В дебаге софтина еле ворочилась на топовом железе, но зато в релизе, где макрос ассертов отключался, - летала и не падала.
Это я к чему - все эти алерты работают только в купе с мощной ситемой тестирования и мешаются вне этих тестов. Так что если вы не хотите вешать тестировангие на конечных ползователей (не хотите ведь? Да?) - то зачем эти контракты?
Контракты в релизе тоже выключены
Это и есть разновидность параноидальных ассертов.
Сейчас масса тривиальных условий проверяется тайпчекерами - статически - анализаторы стали гораздо умнее. Они ловят основную часть проблем.
Проверка нетривиальных условий обычно требует дополнительных вычислений или походов за данными - наружу. Включение и выключение таких проверок само по себе влияет на поведение программы. Такие решения всегда останутся частными, а не универсальными.
DbC изначально был методологией дизайна, а не программирования (что следует из названия). То есть он предполагает много думать upfront, а кодить уже потом. Для этого в Eiffel был специфичный тулчейн, который связывал решения design time, compile time и runtime одним разработческим процессом. Штука весьма нишевая, особенно в нашем безумном мире вездесущего Agile, где принято сперва делать, а думать - потом.
Тащить в C++ это в виде отдельных огрызков нет ни какого смысла. А предлагать менять всем процессы - утопия.
То, что нельзя - нужно безусловно отстреливать в рантайме
контракты этим и заниматься (выбираете семантику enforce/quick-enforce). Но добавляют эту информацию в декларацию функции, а не реализацию. Туда, где ей и положено быть, чтобы клиент этой функции смог правильно ее использовать.
DoC это фантазии на тему более умного тайпчекера чем у вас есть и магического рантайма в одном флаконе.
откройте хоть пейпер что ли… Текущее предложение это только про рантайм и ни о каких “умных тайпчекерах” пока речь не идет (p2900r14).
Ощущение такое, что я не помню, в котором измерении потерял нить рассуждения...
Надеемся все же на выход C++26
Интересно, начиная с 20 стандартов кто-то вообще сам на них пишет, абсолютно не читаемый код, набор кракозябр, нет никакого желания в этом разбираться.
Да вроде все понятно, на удивление они даже рефлексию, сделали так, что с первого взгляда атас, а когда уже понимаешь что к чему, понимаешь, что это ещё очень даже хорошо
Меня еще в 20 стандарте смутила реализация Ranges, у меня тогда конечно не сильно много опыта было в плюсах, но чисто визуально казалось, что в других языках гораздо удобнее реализован pipeline
Ну слушайте, все же не надо идти на поводу моды и писать переусложненные конструкции просто потому что они есть. Я бы даже сказал, куда полезнее оставаться где-то на уровне C++11 в начале жизни проекта, а когда уже вырисовывается скелет реализации - сворачивать в верхние стандарты. Учитывая развитие ИИ, такая операция будет стоить дешево.
Может вообще не надо начинать новый проект на C++ ? Сейчас столько красивых, удобных новых языков появилось, без вот этого всего мазохизма.
“Борьба разума со здравым смыслом”, и разум, увы, побеждает… PDF про с++26 больше PDF про c++98 почти в пять раз, - язык стал в пять раз лучше? хоть по какому-то критерию?
Пора уже на раст переходить
Ну, из плюсов в 20 подвезли концепты, которые, наконец-то, добавляют вменяемую типизацию в шаблоны.
Но, по ощущениям, в последние несколько лет все просто ушли на Rust, Go и прочие штуки вроде Zig.
Но, по ощущениям, в последние несколько лет все просто ушли на …
Это преувеличенные ощущения. На сях ещё очень очень очень много чего пишется. И будет писаться ещё с десяток лет - так точно. Есть надежда, что ИИ убъёт С++ (т.к. сильно облегчит “уползание” на языки програмирования), но даже до такого сценария - ещё очень далеко.
Само собой. php, вон, уже лет 15 пророчат смерть, а он живее всех живых. Никто не будет переписывать многие миллионов строк работающего кода за несколько лет.
Но новые проекты на C++ – скорее исключение.
Никто - скорее да. А вот насчет ничто - то не факт. Процесс в общем и уже пошел:
SQLite переписали на Rust за пару часов: В июле 2026 года рой ИИ-агентов без исходного кода и интернета воссоздал знаменитую СУБД SQLite на Rust, успешно пройдя до 80% независимых тестов
Это не процесс пошёл. Это хайп полез из всех щелей: переписали sqlite, photoshop и т.д.
Вот прямо сейчас зашёл на сайт sqlite-а и там, вы не поверите, упоминается чистый Си для кода. А rust не упоминается. Грустно, да? И всё потому, что кожаные при выборе компонентов руководствуются не логикой маркетологов от ии, а какими-то своими соображениями. Обнаглели! Не дают развиваться ии! - Это был сарказм.
А если по сути, то вспоминаем про IPv4 и осознаём, что новый модный молодёжный IPv6 что-то буксует с захватом мира (а уж где там АТМ с кучей плюшек и гарантированной полосой - лучше не вспоминать). И, вы не поверите, новые проекты в сетевом программировании часто делаются с расчётом на IPv4.
Поэтому rust, конечно, хорошо. НО... Но у меня (например) большинство новых проектов делается на C++ (и работа и не-работа), оставшиеся на чистом Си (и далее в микродозах питон) - это ответ RH215, чтобы два раза не вставать. А чтобы новые проекты начинать на условном rust-е, нужно самому этот раст знать на оверхорошо. И программисты вокруг тебя тоже должны этот раст знать. И если про себя ты можешь инициативить (изучу новое, безопасное и т.д.), то с остальными это сильно тяжело.
А так-то "будем посмотреть", время покажет.
Но у меня (например) большинство новых проектов делается на C++ (и работа и не-работа), оставшиеся на чистом Си (и далее в микродозах питон) - это ответ
Дайте угодаю: у вас нет контрактов с госами США. Потому, что у кого они есть, стартуют новые проекты на Rust и занимаются переписыванием старых, даже если снаружи кажется, что в этом нет ни какого смысла.
Всего ничего, осталось какие то 20% оставшихся пройти, угу… Первые 90% работы занимают 90% времени, а оставшиеся 10% другие 90%.
С++ не моя сильная сторона. Но вот какие мысли постучались в голову...
UB внутри самой проверки. Если выражение в
pre/postсамо содержит баг (нулевой указатель, гонка), это UB в контракте. То есть инструмент, который должен повышать безопасность, сам становится источником UB — классический footgun. Вместо safety-фичи получаем ещё одну дыру.Слишком многое отдали имплементациям. Стандарт оставляет компиляторам широкую свободу в обработке нарушений. Итог — разъезжающееся поведение между тулчейнами: на Clang одно, на MSVC другое, на GCC третье. Для переносимости и предсказуемости это больно.
Сложность и «магия». Синтаксис и семантика получились мутными: поведение кода зависит от флагов компиляции, взаимодействие с control flow неочевидное. Для больших кодовых баз это налог на сопровождение и риск тихих регрессий.
Не покрыты важные кейсы. В первой версии нет контрактов для виртуальных функций и указателей на функции. Обещают в C++29 — но пока фича недоделана для реального продакшена.
Некоторые очевидно проблемные конструкции (например, вызов неконстантных функций) в контрактах запрещены, так что это уже лучше, чем assert.
Да вроде нормально. Разрешены расширения, но они и так бы были, а у четырёх стандартных режимов на мой взгляд вполне чётко поведение описано.
Возможно, но это во многом и к assert и разнообразным сторонним аналогам относится, а их все используют.
Виртуальные функции да, а с указателями что не так? Там для них описано ровно то поведение, какое я и хотел бы видеть: контракты функции действуют и при вызове через указатель, попытка определить новые контракты при взятии/определении указателя — синтаксическая ошибка.
что-то ваших мыслей тут не видно :)
один ИИ’шный слоп
Другими словами, сложность новых предложений … почитали недостаточной, из-за чего отклонили предложения всего стандарта C++26 целиком.
Забавно выстроенная фраза превращается в отличный каламбур в контексте C++.
Касательно контрактов, то, как я понимаю, они по сути ничего не гарантируют, зависят от настроек компилятора и могут быть отключены в рантайме. Ну могли бы просто отправить на доработку и убрать из спеки стандарта как они обычно это делают, но, кажется, у кого-то просто уже котелок закипел.
WG21 has been pursuing contract assertions for more than two decades
Двадцать лет я бежала за вами... Почти (с)
Двадцать лет рожать функциональность, описание которой в D или Nim занимает 1 (одну страницу), родить 63 страницы предложения канцеляритом P2900R6 и 42 страницы возражений P3846r0 и.... не принять ничего.
Мне кажется, это конец. Такой комитет нам не нужен!
Он похоронен тоннами бумаг, Астанавитесь!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Контрактное программирование и C++26 все :-(