Pull to refresh

Comments 427

а концепты попадут в стандарт только в виде required clause (1) или варианты 2/3 тоже «доедут»?
// 1
template <typename T>
    requires Copyable<T>()
void foo(T x);
// 2
template <Copyable T>
void foo(T x);
// 3
void foo(Copyable x);


А есть ли возможность сделать перегрузки constexpr/runtime для функций? Отсутствие этой фичи не позволяет считать на этапе компиляции многие математические функции, в т.ч. exp/log.

Какой статус у Ranges?
Required clause (1) вошли и будут в C++20.
Варианты 2 и 3 активно обсуждаются и полируются, в них есть проблемы:
  • Вариант 2 не позволяет работать с концептами принимающими сразу два шаблонных параметра
  • Вариант 3 вызывает недоумение в случае
    void foo(Copyable x, Copyable н);
    Типы у переменных x и y должны быть одинаковые или нет?


А есть ли возможность сделать перегрузки constexpr/runtime для функций?

Идёт работа над волшебной функцией is_constant_evalueated(). Она позволит внутри функции делать ветвление, в зависимости от того, выполняется функция на рантайме или на этапе компиляции:
    constexpr double power(double b, int x) {
      if (std::is_constant_evaluated() && x >= 0) {
        // A constant-evaluation context: Use a
	// constexpr-friendly algorithm.
        double r = 1.0, p = b;
        unsigned u = (unsigned)x;
        while (u != 0) {
          if (u & 1) r *= p;
          u /= 2;
          p *= p;
        }
        return r;
      } else {
        // Let the code generator figure it out.
        return std::pow(b, (double)x);
      }
    }


Какой статус у Ranges?

Идёт активная работа по принятию из в C++20. Есть все шансы на успех.
Вариант 2 не позволяет работать с концептами принимающими сразу два шаблонных параметра

это критично с точки зрения РГ даже с учетом того, что для этого есть fallback синтаксис?
Типы у переменных x и y должны быть одинаковые или нет?

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

Кому-то не нравится вариант 3, так как из сигнатуры функции не понятно что это шаблонная функция (нужно знать, что Copyable — это концепт; проблем не возникает в случае со стандартными концептами, однако для больших кодовых баз со множеством своих концептов разобраться будет намного сложнее).

Кому-то не нравится вариант 2, потому что он громоздкий :)

Про модули и сопрограммы что-то известно? Herb Sutter даёт прогноз, что сопрограммы скорее всего войдут, а насчёт модулей нет ясности.

По модулям будет отдельная встреча этим летом. Будут обсуждать только модули.

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

Сопрограммы скорее всего войдут в C++20. Были попытки выдвинуть другое (конкурирующее) предложение по сопрограммам. Поддержки новый подход не получил. Если новых предложений или возражений не поступит, то Coroutines TS окажется в C++20.
Что мешает просто договориться, что модули и макросы несовместимы? Ведь все равно с приходом модулей придется библиотеки переписывать в какой-то степени, почему бы «под шумок» не выкинуть оттуда макросы, заменив на всякую constexpr-магию? И волки сыты и овцы целы.
Потому что многих людей не устраивает, что придётся код перелопачивать сильно на новые модули. Поэтому и придумывают, как бы так безболезненно заменить хедеры на модули с оставшимися экспортированными макросами, так как есть много кода, который ну очень сильно завязан на макросы.
многих людей не устраивает, что придётся код перелопачивать сильно на новые модули

Помню как Дельфи помер, когда очередная новая версия оказалась потерявшей совместимость со старым кодом.
Это текущий Modules TS (модули в первоначальном виде).

Ряду компаний очень не понравилось отсутствие макросов и, как следствие, невозможность «модуляризировать» системные заголовочные файлы.
а то, что макросы плюют на namespace и чужой код, а также в разы более убогие, чем constexpr, ряду компаний нравится?
Хочу напомнить, что некоторые вещи без макросов в С++ по-прежнему нельзя реализовать.
Или я просто не знаю, как, но что-то сомневаюсь.
UFO just landed and posted this here
И для рефлексии в том числе, но не только. Например, макрос, который я назвал EXEC_ON_SCOPE_EXIT, принимающий лямбду и создающий на стеке объект, выполняющий эту лямбду в деструкторе. Тонкость в автоматическом именовании этого объекта, чтобы включал номер строки (что гарантирует уникальность, если не пихать по два в строку):

#define EXEC_ON_SCOPE_EXIT auto CONCAT_EXPANDED_ARGUMENTS_2(scope_exit_executor_, __LINE__) = detail::makeOnScopeExitExecutor


В общем случае, макросы, использующие стрингификацию и/или конкатенацию строк, обычно решают задачи, не решаемые средствами С++.
UFO just landed and posted this here
зато код на меня не кричит

Вы имеете в виду upper case? Можно же переименовать :)
То, что вы предлагаете, функционально тождественно, но много лишних символов, я от такого люблю избавлять программиста.
reflexpr

Это что-то, чего мы ждём в С++20? Очень мало результатов в Гугле по этому термину.
UFO just landed and posted this here
Например, constexpr преобразование указателей. Что-то типа:
static constexpr volatile uint32_t* MY_PTR = (volatile uint32_t*)0x12345678;
А без этого невозможен никакой embedded и низкоуровневое системное программирование. Приходится всё только на многоуровневых извращённых макросах делать.
Колоссальный косяк, кстати, который не позволяет писать под железо на полноценных плюсах. А всё потому, что бородатые дядьки опять заигрались в стандарты.
Тогда просто не будут переходить на модули те, у кого макросы в апи. Тот же gtest/gmock, например.
В текущем виде да. Или они там могут просто дождаться модулей+рефлекшн и переписать свои фреймворки на тот же подход, который используют C#, Java и прочие. Понятно что переписывать никто ничего не хочет, но это цена прогресса.
Вот и получается, что либо ждем лет 10-15 всех необходимых фич, либо поддерживаем макросы в модулях и делаем быстрый и безболезненный переход.
И практически не получаем выигрыша в производительности :)
А вот тут хочется подробностей, полагаю, что авторы Modules TS и ATOM должны были проводить какие-то эксперименты на своих кодовых базах.

Я читал про эксперимент на модулях от clang (а авторы proposal наверняка вдохновлялись и такими модулями), они давали прирост 60% (или 10% по сравнению с использованием precompiled headers).

Хочется еще узнать, а как реализованы Modules TS и ATOM в gcc. Используют ли они предложенное Страуструпом бинарное представление пропарсенного C++ кода? Или у них какое-то свое представление? И таскают ли в реализации ATOM текстовую копию кода для поддержки экспорта макросов?
Работать 10 лет и получить прирост скорости компиляции на 10% больший чем дают precompiled headers — это провал. Люди работающие над clang — одни из авторов ATOM. Можно приблизительно представить, какой прирост производительности с подходом из ATOM можно получить.

Один из разработчиков Modules TS утверждает, что макросы — одна из основных причин тормозов. Поэтому пока вы не начнёте использовать модели без макросов — довольствуйтесь 10% прироста скорости.

Лично мне ATOM нравится простотой внедрения в большие кодовые базы, и не нравится своим небольшим приростом скорости компиляции.

Modules TS и ATOM в gcc сделаны и информация об актуальном состоянии есть вот тут gcc.gnu.org/wiki/cxx-modules
Вот поэтому и хочется узнать о результатах экспериментов от разрабов Modules TS. Что-то вроде: «Вот мы сделали модули без макросов и компиляция ускорилась в 10 раз, а сделали с макросами — получили прирост в 0,1».
Может подход в духе precompiled headers и достигает теоретического предела в приросте?

Я ни в коем случае не защищаю макросы и не пишу своих в кодовой базе. Я заинтересован в том, чтобы разработчики библиотек активно переходили на модули, но как бы не получилось так, что будем еще 10 лет разрабатывать модули, а потом 10 лет переходить на них.
Такое ощущение будто вы хотите одним седалищем усесться на два стула. Чтобы и модули были, и легаси не пришлось переписывать. Так, скорее всего, уже не получится. Если в модулях будут макросы, польза от их введения будет весьма условной.
Лично я не жду от модулей в языке с открытой внешней компоновкой никакой пользы кроме вреда. Яву уже превратили в породистую корову, несущую несколько дорогих разукрашенных стампидных седел. Пришла очередь C++?
Что это за зверь такой «открытая внешняя компоновка»? C++ единственный язык который до сих пор не имеет концепции модулей, которая даже старше ООП и появилась с изобретением процедурного и структурного программирования. Чтобы использовать библиотеки в C++ нужно как в С лезть на низкий уровень и заниматься флагами компилятора, служебными макросами (#ifdef/#endif), включением исходных файлов (в C это оправдано, поскольку язык предназначен для системного программирования и прекрасно для этого подходит). Еще «лучше», т.н. header-only библиотеки, которые часто используют еще и шаблоны что приводит к непомерному дублированию кода, ведь полная оптимизация возможно только в полностью функциональном языке, но никак не в императивном.
Простите за это очевидное объяснение, но хочется избежать недопонимания.
Возможно, моя русская терминология неточна: она формировалась в 90 годы прошлого века во время чтения сделанных издательством «Мир» переводов классических англоязычных руководств по C и С++. Имеется в виду тот факт, что язык C++ работает с отдельными единицами компиляции, превращая их в объектные файлы определенного для целевой платформы формата (с функциями и глобальными переменными). Этот формат, соглашения о вызовах и ABI, устройство выполнимых бинарных образов и разделяемых библиотек, строго говоря, к языку C++ не относится, а относится к целевой платформе и в основных чертах остается неизменными со времени изобретения ассемблера. В частности, часть платформы — компоновщик, перебирает указанные ему объектные файлы, смотрит, на какие имена внешних объектов они ссылаются и найденные объекты (тела функций и определения глобальных переменных) как-то платформозависимо укладывает в выполнимый файл. Если компоновщик не найдет нужный объект — будет ошибка времени компоновки.
Противоположностью этому являются варианты явной компоновки, когда компилятор формирует бинарные файлы не только со скомпилированным кодом, но и с мета-информацией о видимых снаружи именах. Такие файлы и называются (обычно) модулями. В единице компиляции требуется указывать не только имя внешней сущности (функции или переменной), но и то, из какого модуля ее следует брать. В этом случае компоновщик не ищет первое попавшееся имя, а ищет конкретный объект в конкретном модуле, что и быстрее, и «типобезопаснее». Примеры — Turbo Pascal и Modula2, C#, можно продолжить.
Совместимость с C и используемый в C (взятый из экосистемы ассемблера) инструментарий — совершенно сознательное решение Страуструпа, сделанное при создании C++. По моему мнению, это решение сыграло важнейшую роль в том взрывном распространении C++, которое мы наблюдали в 90 годы XX столетия))).
И мне не очень понятно, где тут место для сколь-нибудь развитой системы модулей. Опасаюсь, что в результате такого «скрещивания ужа с ежом» получится нечто, что будет смотреться столь же органично, как седло на корове или пятая нога у собаки. Впрочем, это мое личное мнение и я его никому не навязываю.
Информация о символах есть всегда. В тех же объектных файлах, или как вы себе представляете работу компоновщика? То что сам язык об этом не знает, это большой минус языка. С другой стороны, если язык ограничен в ресурсах, а C был системным языком для ограниченных в ресурсах платформ, то поэтому делегирование такого знания компоновщику было элегантным решением. Но для C++ это — тяжелое наследие детства с «игрушками прибитыми к полу» минимальной рудиментарной системой модулей (те же объектные файлы и т.н. «библиотеки» — архивы объектных файлов).
С чем именно это сравнивалось? Наиболее тормозной вещью в сборке С++-кода является раздельная компиляция. Дефолтная ситуация выглядит так: есть один cpp-файл на 1к строк, в него инклюдится 100500 хедоров на 1кк строк, тем самым каждый файл раздувается до 1к1к строк.

Насколько я понимаю, в модулях единицей трансляции является не файл, а множество файлов — модули. Это, скорее всего, и даёт весь профит. И это нужно учитывать при сравнении.

Верно. Распишу поподробнее:

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

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

Ещё одна проблема — невероятно количество макросов (они пораждаются например include guards). Держать их всех в памяти и препроцессировать документ становится тяжёлой задачей.

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

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

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

И наконец, финальная стадия сборки проекта — линковка. В данный момент линковщик много времени тратит на выкидывание одинаковых блоков скомпилированного кода (вы подключили <string> 100 раз — линкер выкинет 99 скомпилированных std::string). С модулями линкеру не придется этим заниматься, т.к. файл модуля не будет вкомпиливаться внутрь собранного cpp файла.

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

Когда-то пытался измерить — всегда получал очень малый вклад этих стадий во время компиляции.

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

Оно одновременно и замедляет и ускоряет. Замедляет за счёт того, что происходит более глобальный ipo, а ускоряет за счёт того, что не нужно 10раз оптимизировать одни и те же функции в контексте того же header-only, которого в С++ предостаточно.

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

Макросы так же засирают глобальный неймспейс.

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

И сразу открытый вопрос: а почему нельзя совместить оба подхода, с экспортом макросов и без? Даже если модуль с макросами будет собираться дольше, пусть это будет опцией при сборке модуля. Если опция не включена, то и хранить модуль можно в более оптимальном виде, и знать можно, что он не повляет на последующие текстовые инклюды. Дескать, если разработчик настолько сильно желает экспортировать макросы в своем апи, что готов пожертвовать временем компиляции у конечных потребителей — пусть. Пользователям решать, использовать такую библиотеку или нет.
Юнити билды становится адово использовать в случае локальной разработки. Скажи разработчику что каждый инкрементный билд будет по полминуты занимать (вместо 1-2 секунд) — он криво на тебя посмотрит.
Да и на сервере, не так чтобы сильно ускоряется сборка. Я делал такую штуку — в cmake делал автоматическое объединение cpp всей цели для одной либы. Ускорение — не больше, чем в два раза. И плюс мы получаем тьму неудобств от необходимости следить за анонимными неймспейсами и т.п.
Если еще и локально одна схема, а на билде другая — это вообще адъ.

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

Я пока раздумываю как в свою систему распределённой сборки модули прикручивать. Получается, что по сети не только единицу трансляции гонять придется, но и все используемые скомпиленные модули.
У меня коллеги ускорили сборку своего проекта с 15 до 5 минут с помощью unity билдов. И думаю, что скорость инкреметного билда их не сильно разражает, т.к. большую часть этого времени все равно занимает линковка пачки статических зависимостей.

Распределенную сборку они тоже пробовали — не взлетело, слишком медленно. Полагаю, что уперлись в wifi сетку.

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

«скорость инкрементного билда их не раздражает» — какие спокойные разработчики! Лично меня цикл сборка-запуск больше 5 секунд начинает заметно напрягать, а 15 — откровенно раздражать.

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

Ну и компоновка всего проекта как кучи статичных либ — вообще сомнительное удовольствие)

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

Ах да, еще замечательное — т.к. все файлы идут вместе, начинает теряться необходимый набор хедеров в каждой единице трансляции. Начинаются вещи вроде «убрали один cpp файл — все отвалилось», а файл этот может исключаться из сборки какой-то опцией, т.е. нужно тестировать все комбинации опций при вообще любой правке, даже не в хедерах.
В общем, в плане поддержки это очень тяжело. От распределённой сборки все же треба только железо/сеть, ты никак не думаешь о каком-то специфичном коде.
Детали эксперимента можно тут посмотерть: reddit. Это всего лишь локальный эксперимент какого-то разработчика, но все же лучше, чем ничего.

Если рассуждать о скорости компиляции, то модули подступаются к этому вопросу с нескольких сторон.

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

Или, например, объявление модуля. Не знаю, как там после последней встречи объединили Modules TS и ATOM, но конкретно от ATOM ранее было предложение разрешать объявление модуля только на верху единицы трансляции. Это позволит компилятору прочесть только заголовки файлов и построить граф зависимостей между файлами, чтобы потом оптимальнее подгружать их при компиляции.

Другой момент касается шаринга явных и неявных инстанцирований шаблонов: в модулях их стараются расшаривать по-максимому, чтобы не делать повторно одни и те же инстанцирования. Я правда не знаю, как современные компиляторы работают с инстанцированиями — пытаются ли запоминать, что какой-то шаблон уже обрабатывали и инстанцировали в другой единице трансляции? Если нет, то здесь модули так же дадут ускорение, т.к. это одно из главных вкладов во время компиляции.
UFO just landed and posted this here

Zapcc делает именно это — кэширует инстанциации между ТУ. Для этого они держат в памяти сервер с кэшем.

UFO just landed and posted this here
Из того как работает zapcc, я так понимаю, модули его не сильно и ускорят, он и так каждый файл препроцессированным держит? Можно интересно считать, что то ускорение, которое дает zapcc- оценка сверху для ускорения от модулей?
Согласитесь, если вам вместо файла с исходниками дадут вот такую единицу модуля — компилироваться все в итоговый файл будет быстрее.

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

Выше я уже писал, что не получал особого профита от тех же pch. Может на обычных инклюдах с одними декларациями это и даёт что-то, но это уже давно не модно.

Опять же, один из тех же профитов, который даёт header-only — простую кросскомпиляцию и между платформами и между компиляторами. Дадут ли тоже самое модули? Сомневаюсь.

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

Поэтому совместно с шаблонами используется header-only подход.

Мы потеряем то самое преимущество, особенно в контексте header-only, когда мы всегда можем увидеть как минимум сигнатуры функций.

Не совсем понял. Под «увидеть» понимается посмотреть на код в human-readable формате? Если да, то вопрос тулчейна, он должен научиться показывать интерфейс модуля в читаемом виде, все необходимое для этого в модуле есть.

Опять же, один из тех же профитов, который даёт header-only — простую кросскомпиляцию и между платформами и между компиляторами. Дадут ли тоже самое модули? Сомневаюсь.

Какая связь между кроссплатформенностью и header-only? Как в header-only спокойно можно писать непереносимый код, так и не в header-only можно писать кроссплатформенно. Это ортогональные понятия.
Если да, то вопрос тулчейна

И текущие реализации модулей это умеют? Как это должно быть реализовано? В ситуации с хедерами всё понятно — я открыл файл и прочитал( в самом примитивном случае). Что же будет делать тулчейн? Создавать файлы, которые будут эмулировать хедеры, перекладывать это на шею ide?

Какая связь между кроссплатформенностью и header-only?

Прямая.

Как в header-only спокойно можно писать непереносимый код, так и не в header-only можно писать кроссплатформенно.

Дело не в этом. Нельзя просто так взять и собрать одну часть одним, а другую часть вторым компилятором. Т.е. бинари теряют вообще какой-то смысл по части дистрибуции, надо будет опять всё собирать.

Опять же, самая сложная проблема — собрать всё, собрать все зависимости. Дефолтный подход требует системы сборки, где добиться кроссплатформенной сборки — то ещё приключение. Даже не смотря на модный нынче cmake, и попытки создавать пакетные менеджеры.

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

Далее, дистрибуция. Это опять приключения. Нужна статическая линковка, либо приключения с контейнерами. Статическая линкова сама по себе то ещё приключение и мало кто её поддерживает из зависимостей.

В случае же с header-only нет никаких проблем с дистрибуцией самих зависимостей — просто носишь собой/забираешь из vcs. Нет никаких проблем со сборкой — нет никакой сборки вообще. Нет никаких проблем с дальнейшей дистрибуцией бинарей — нет никаких бинарные зависимостей, линковки и прочих приключений

Создавать файлы, которые будут эмулировать хедеры, перекладывать это на шею ide?

Почему бы и нет? Либо просто показывать интерфейс в менюшке автокомплита, как это делается во многих ide сейчас?

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

Далее, дистрибуция. Это опять приключения. Нужна статическая линковка, либо приключения с контейнерами.

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

Если и о компиляторах думать не хочется, можно распространять только исходники и собирать их самостоятельно в модули, в любом случае соберутся быстрее старого подхода. header-only библиотека ортогональна простоте сборки: у неё точно так же могут быть зависимости, которые потребуется собрать, у неё могут особые требования на флаги компиляции, которые желательно носить рядом, в том же cmake. Хорошие header-only либы этими проблемами не страдают, ну так и модули можно писать точно так же, избегая сложных зависимостей и сложной сборки.
Почему бы и нет? Либо просто показывать интерфейс в менюшке автокомплита, как это делается во многих ide сейчас?

Интерфейс мало кому нужен, к тому же было проигнорировано множество тезисов, как и множество деталей.

В подсказке описание типа/класса тоже можно показать? Да и сигнатуру функции, особенно с какими-то концептами, контрактами и прочим. Где это всё показывать? И в каком виде? В виде кода?

Опять же, куда девать коменты — нужно ещё и это эмулировать.

Так же, я говорил не только о сигнатурах, но и о самих исходниках, которые всегда и удобно доступны в ситуации использования header-only.

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

Проблема в том, что в ситуации с header-only думать не нужно. И пока это лишь фантазии, а я сравнивал реальность. Я утверждал, что модули дают профит по части кросскомпиляции и задавал вопрос, а дадут ли тот же профит модули? Я не сравнивал модули и header-only. Был вероломно подменён тезис, что очень некрасиво.

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

Про старый подход я и не говорил. В контексте же правильного header-only не существует всех тех проблем, которые и дают модулям какой-то профит.

header-only библиотека ортогональна простоте сборки

Header-only предполагает header-only, и это очевидно. Если header-only тянет какие-то левые бинарные зависимости — это уже не header-only.

у неё точно так же могут быть зависимости, которые потребуется собрать

Header-only зависимости не нужно собирать.

у неё могут особые требования на флаги компиляции, которые желательно носить рядом

Флаги компиляции чего? У меня такое чувство, что вы мало знакомы с темой, очень мало. Header-only это встраиваемая зависимость, она не собирается — она собирается в контексте вашего кода. Для неё не может быть каких-то отдельных флагов для cmake — они будут накладываться на всё то, куда вы это заинклюдили. И если это «куда» является так же header-only, то инклюдится это ТОЛЬКО в ОДНО место, а значит флаги всегда ОДНИ.

К тому же, у стандартного кода может быть только один флаг — это std, который совместим сверху вниз, а значит в любом случае код использующий header-only зависимость нужно собирать с std не ниже необходимого для зависимости.

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

Модули — это уже лишние телодвижения. При этом уже нужна какая-то система сборки, уже нужно будет делать всё тоже, что и
при header-only, только с куда большим набором заморочек. При этом просто в голословное «можно» я поверить не могу.

Модули для леги кода? Да. Модули для блобов? Да. Преимущества очевидны, а вот в сравнение с современными техниками — преимущества очень не очевидны, по крайней мере мне.
У меня такое чувство, что вы мало знакомы с темой, очень мало.

Может, не стоит скатываться в обвинения о непрофессионализме? Это оскорбительно и лишь провоцирует на взаимные обвинения.


Давайте лучше почитаем proposals по модулям и предположим, как эти предложения смогут реализовать компиляторы. А для начала договоримся об одном понимании header-only библиотек, какие "гарантии" они дают, а какие — нет.


К тому же, у стандартного кода может быть только один флаг — это std, который совместим сверху вниз, а значит в любом случае код использующий header-only зависимость нужно собирать с std не ниже необходимого для зависимости.

Предположим, я пишу проект на последнем C++17 и подключаю к нему header-only библиотеку, которая:


  • Использует в своей реализации старенький std::bind1st. Эта вещь была удалена в 17 плюсах. Gcc спокойно скомпилирует заинклюженную в мои cpp файлы эту header-only библиотеку. Clang тоже. Но только если будет использоваться std либа от gcc. Если я подключу std от clang, то получу ошибку — там эта вещь была удалена в соответствии со стандартом. Флаг std не совместим сверху вниз, в нашем примере он навязывается проекту от header-only либы, иначе проект не соберется только из-за несборки кода header-only библиотеки.
  • Использует чуть более современный std::thread. Чтобы мой проект собрался с этой header-only либой на линуксе, я должен слинковаться с pthread.
  • Использует std::filesystem. Тогда на компиляторе gcc я должен слинковаться с stdc++fs.
    Вывод: header-only библиотека может зависеть от бинарей даже в случае, если используется только стандартный c++.

А теперь о том, какие же "гарантии" дает header-only либа. А она гарантирует лишь:


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

А так же гарантирует минусы:


  • включая в себя все необходимые файлы с реализацией, соответствующий заголовок при включении в конечные cpp файлы будут требовать разбора/инстанцирования/компиляции одного и того же кода раз за разом в каждом cpp файле.
  • Сделает видимым конечному потребителю все детали реализации, давая возможность заиспользовать то, что не должно быть использовано напрямую.

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


И почему вообще header-only библиотеки вы называете "современным" подходом? Что в этом современного? Его разве изобрели пару лет назад?


Неужели эти "гарантии" не смогут предоставить модули?
Способ поиска модуля при импорте — implementation defined. Компилятор точно так же может сделать директиву, где будет принимать директорию для поиска подключаемых модулей.
В случае, если будет принято предложение ATOM об объявлении модуля только на верху файла, тогда компилятору будет достаточно прочесть заголовки всех файлов в этой директории, чтобы понять, что потребует сборки. Это не так быстро, как чтение хедера по прямому пути из инклюда, но выполняется однократно, в отличии от многократного чтения хедера при каждом включении. Если предложении от ATOM не будет принято, компилятору придется прочесть/распарсить каждый файл целиком, чтобы найти там объявления модуля. Это соответственно дольше.
Всё! Гарантии header-only либы могут быть соблюдены и модулями! При этом они лишены недостатоков header-only либы — вся внутренняя реализация библиотеки не будет видна конечному коду и будет компилироваться только один раз.


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

Использует в своей реализации старенький std::bind1st. Эта вещь была удалена в 17 плюсах. Gcc спокойно скомпилирует заинклюженную в мои cpp файлы эту header-only библиотеку. Clang тоже. Но только если будет использоваться std либа от gcc. Если я подключу std от clang, то получу ошибку — там эта вещь была удалена в соответствии со стандартом. Флаг std не совместим сверху вниз, в нашем примере он навязывается проекту от header-only либы, иначе проект не соберется только из-за несборки кода header-only библиотеки.

Вы утверждали, что нужны какие-то флаги(отдельные) отдельно для header-only, на что я вам ответил — никаких отдельных флагов нет, а всё, что может наложить header-only на ваш код — это std.

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

По поводу «навязывается» — я уже об этом говорил, в любом случае он навязывается.

По поводу совместимости — оно всё совместимо, а ссылаться на 1-2 исключения имеет мало смысла, которые, к тому же, мало кто использовал и которые выпиливаются банальным реплейсом.

Использует чуть более современный std::thread. Чтобы мой проект собрался с этой header-only либой на линуксе, я должен слинковаться с pthread.

Будь она какой угодно — вам нужно будет линковаться с pthread.

Вывод: header-only библиотека может зависеть от бинарей даже в случае, если используется только стандартный c++.

Очевидно, что стандартные зависимости мало кого волнуют, ведь они стандартные. О них никто и не говорил, их никто не учитывает. Если что-то требует с++17 — обеспечьте это. -pthread, -lstdc++fs — это всё заморочки конкретного компилятора/платформы.

К тому же, я всё это заранее проговаривал:

Если header-only тянет какие-то левые бинарные зависимости — это уже не header-only.

Я уточнял и про левые зависимости, и про то, что header-only может их тянуть, и про то, что я не считаю подобное header-only.

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

Это не проблема, организуйте нормальную(модульную) сборку. К тому же, ничего не мешает организовать в проекте подобное и без сборки. Разделите проект на header-only модули. Он будет работать и собираться быстрее.

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

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

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

Это просто придирки. Таких проблем ни у кого нет, а если кому-то нужно что-то заиспользовать — он заиспользует это и реализация «отдельно» ему никак не помешает.

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

Такая же придирка, как и в случае выше. Простой пример /usr/include? Инклюды обладают очень простым свойством — их можно накидать куда угодно и использовать, язык это умеет.

И если что-то и нужно передавать — это корневой каталог по типу /usr/include и не более того. И даже если это какая-то сложная иерархия, то они организуется из любого thrid_payty без каких-либо проблем.

В сравнении с тем, что нужно сделать в ситуации со сборкой — это 1 против 100.

И почему вообще header-only библиотеки вы называете «современным» подходом? Что в этом современного? Его разве изобрели пару лет назад?

Изобрели их дано, массовым явление стало достаточно недавно.

В современных реалиях «старый» подход оправдан очень в малом кол-ве случаев. И в противовес старому подходу я называю header-only новым подходом.

Неужели эти «гарантии» не смогут предоставить модули?
Способ поиска модуля при импорте — implementation defined.

Если бы они сделали инкрементальную сборку, то это да — даст очень большой профит. Что ещё даст какой-то профит — я не знаю.

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

Меня не особо радуют всякие ограничения про «вверху файла». К тому же, одно дело «может», а другое дело «делает».

Всё! Гарантии header-only либы могут быть соблюдены и модулями! При этом они лишены недостатоков header-only либы — вся внутренняя реализация библиотеки не будет видна конечному коду и будет компилироваться только один раз.

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

Опять же, «внутренняя реализация видна» — для меня является не минусом, а плюсом. Никакого минуса я тут не вижу вообще.

По поводу «компилироваться» — это недостаток по части оптимизации. Это будет очередная линковка. Если же это не линковка, то компиляция будет, а модуль будет просто частично скомпилирован( и то, лишь в теории).

Видимо, мы друг друга не убедим до тех пор, пока не увидим, как implementation defined детали работы с модулями будут реализованы в компиляторах.
Хм, мне всегда казалось, что компоновка в этом аспекте не уступает компиляции, а часто компиляцию превосходит. Нет?
Или у них появятся форки, в которых макросы заменены на constexpr силами людей, у которых ещё есть шило в одном месте…
В конце концов, столько времени потратить на то, чтобы минимизировать влияние препроцессора на время компиляции, и опять мы суём макросы, чтобы увеличить влияние препроцессора на время компиляции… зачем?
Модулей нет, зато есть куча всякого ада. А еще нет вирутальной шаблонной функции. Просто браво…
Ну это кому как. Я контракты второй год с нетерпением жду, а std::bit_cast люди хотели ещё лет 20 назад…

Ну и не стоит так надеяться на модули, в 10 раз они компиляцию не ускорят (в ближайшее время).
Ускорят. Просто надо дать время компиляторописателям, чтобы они это написали. А так… пока в Стандарт не приняли, некуда и спешить.
Ускорят.
Боюсь, будет сильно зависеть от кода. По опыту работы с некоторыми header-only библиотеками получается, что иногда львиная доля времени во время компиляции уходит на инстанцирование и оптимизацию шаблонов, а не на загрузку и обработку заголовочных файлов.
Modules TS предлагает по-максимому расшаривать explicit и implict инстанцирования, идущие с модулем.
Контракты — это очень хорошие новости. Я правильно понимаю, что для них ещё нет имплементации ни в gcc ни в clang ни вообще нигде, даже прототипа?
Увы, пока нет. Но в контрактах очень заинтересованы крупные компании, скорее всего имплементация появятся в скором времени.
UFO just landed and posted this here
UFO just landed and posted this here
Offtop: кстати, девушка с видео есть на фотке из поста
Offtop^2: главный нелюбитель фоток (Бьярни) спрятался за мной, пока я фоткал фотографа.
Логика подсказывает, что если у нас есть constexpr std::regex значит у нас есть «сделай из строки что угодно на этапе компиляции», т.е. фактически те же макросы что в языке D. Это конечно породит большой поток ненависти от некоторых, но мне очень даже нравится, т.к. намного проще инжектировать прямо в компилятор строки нежели пытаться использовать API самого компилятора, строить там ООП структуры через специальный API, все такое. Это, если хотите, «метапрограммирование для чайников».
Ждём настоящего «нормального» метапрограммирования с приходом Reflection в язык. Вот тогда уже можно будет делать очень много интересных вещей (а если ещё и скрестить с металкассами...)
Вангую что с метаклассами мы получим:
* генерацию парсеров из EBNF и других форматов описания грамматик
* ORM классов из коробки
* сигнал/слоты Qt без макросов
* расцвет DSL
* красивый RPC
* ???
сигнал/слоты Qt без макросов

github.com/woboq/verdigris
У автора в блоге (https://woboq.com/blog/), кстати, много всего интересного написано про нестандартное использование C++.
Но ведь по ссылке с макросами… :(
Да, точно. Имел ввиду, что без MOC.
А про сами метаклассы что-нибудь слышно, кстати?
Метаклассы базируются на рефлексии, рефлексия требует constexpr контейнеров и алгоритмов.

Constexpr алгоритмы мы дотолкали до стандарта. Надеемся увидеть в C++20 constexpr контейнеры; рефлексию — в C++23. Где-то в это время и появятся метаклассы в стандарте.
Спасибо!
Надеюсь, доживем :)
UFO just landed and posted this here
Парсеры на шаблонах: github.com/taocpp/PEGTL
В принципе почти хватает и сейчас, без метапрограммирования
constexpr!
Я вместо if(!result) пишу if (result == false), ибо восклицательный знак легко теряется из виду.
Может стоит дать другое имя?
И будут ли весь math модуль делать constexpr? Мне бы не помешал constexpr синус и косинус.
Да и может быть стоило бы всё-таки сделать какой-нибудь «std2»?
А какое имя вы предлагаете?
да даже и не знаю. Возможно, какой-нибудь static_expr или compilet. Сходу так не скажешь. Но наличие восклицательного знака однозначно напрягает. В C++ нет ни одного ключевого слова с восклицательным знаком. Зачем нарушать эту тенденцию?

Ещё одно занятное изменение, которое рассматривается для приёма в C++20 — это constexpr! функции.

Я когда прочитал это предложение, то не понял в тот же миг, что оно говорит о чем-то новом)
Я вообще когда только начал знакомиться с constexpr, был крайне раздосадован, что они как раз не ведут себя «всегда constexpr!»., если бы можно было вернуть время вспять, я бы предложил constexpr и maybe_constexpr какой-нибудь (соответствующий сегодняшему)
абсолютно с вами согласен
Поддерживаю, делать настолько похожие ключевые слова — не лучшая идея.
Я уж молчу про то, как легко слово «констекспр» произносится вслух -_-' Но говорить вслух «констекспр!» будет вообще невозможно.
«Констекспрбанг»? «Констэкспр экскламейшн марк»? «Констэкспр восклицательный знак»?

Гуглить тоже очень тяжело будет.
Вы правы. Закиньте идею на stdcpp.ru, чтобы она не потерялась. Я автору предложения передам ваши возражения, думаю он прислушается
А можно ссылку на оригинальный пропозал или на существующее обсуждение? А то я уже ничего нагуглить не могу -_-'
В вашем примере восклицательный знак «зажат» между скобкой и идентификатором, от чего плохо читается (например, я бы предпочел такое условие обособить). За обсуждаемой конструкцией почти всегда будет следовать пробел.
И всё-таки, насчёт библиотеки: constexpr(2011), концепты, метаклассы, модули. Чем не повод сделать абсолютно новую и чистую либу, которой будет легко и приятно пользоваться, которая не будет страдать от миллиона проблем?
Это всё мне напоминает чемодан, который с каждым годом становится всё дырявее и дырявее, а его выкинуть жалко, поэтому дырки латают и красят новым модным цветом.
Да все контейнеры (также их итераторы) и алгоритмы будут гораздо проще и выразительнее с концептами и с учётом существования compile-time выражений почти всё можно переписать и сделать чище.
При этом всём не нужно выкидывать старую библиотеку (дабы всеми необходимая обратная совместимость осталась). Но и развивать старую библиотеку я не вижу смысла.
Такой потенциал теряем…
А что бы вы хотели такого сделать в стандартной библиотеке, что поломает обратную совместимость? Концепты и контракты можно добавлять к уже имеющимся классам стандартной библиотеки, и всё будет OK.
Всё. Нужно позволить себе провести тотальный рефакторинг, с учётом появления супер-плюшек, появившихся за последние 16 лет и не бояться потерять обратную совместимость. Со спокойной душой пересмотреть большинство концепций и сделать библиотеку изначально заложив туда свежий фундамент, а не достраивать этажи поверх старого и не прочного фундамента.
Я понимаю, что я как-то сегодня прилично так умничаю, но просто мне кажется, что для молодых специалистов (как и я) был бы C++ более привлекателен с подобной новой и чистой библиотекой.
Недавно в теме по std::visit происходили серьёзные стычки между сообществом C++ и сообществом Rust. Аргументы Rust-овщиков были вполне убедительными, поскольку Rust — это молодой язык, который создавался опираясь на горький опыт своих предшественников.
А тут такой шанс по новому взглянуть на мир в пределах плюсов, с новыми возможностями и не заменять старую библиотеку и не приклеивать новые возможности к старым с помощью скотча, а сделать всё сразу и хорошо.
Ну ведь сейчас так и происходит, что все новые фичи прикручивают к старым. Тот же constexpr почти по всей библиотеке можно прилепить. Но это ведь в корне может поменять все концепции библиотеки.
Концепты, модули и метаклассы — это через чур глобальные изменения для того, чтобы быть использованными поверх старых наработок. Необходимо использовать сей потенциал. Иначе, всё-равно все станут писать свои велосипеды. Так и происходит сейчас даже. constexpr std::swap мне приходилось писать свой, абсолютно такой же, но с constexpr. Вы продвигаете constexpr std::regex. Контейнеров новые штуки также касаются. Итераторы будут безумно изящнее с концептами. Подобных примеров уже существует уйма. Библиотека std простаивает.
Мне кажется, если молодому специалисту объяснить, что «безопасное» подмножество C++ — это то, что соответствует Core C++ Guidelines, а все остальное не рекомендовано и должно быть инкапсулировано, ему станет гораздо легче жить. А огромное количество старых вещей в Core C++ Guidelines запрещены или разрешены лишь для короткого списка исключений.

Да, такой безопасности, как в Rust, не будет. Но будет гораздо лучше, чем раньше.
О, я вас узнал сразу)
Но я же говорю не о безопасности, а обо всей библиотеке в целом. Да её можно сделать хоть смешав с GSL.
Факт в том, что на базе новых новшеств и базируясь на том, что язык за последние 20 лет тотально прокачался, нужно пересмотреть стандартную библиотеку вдоль и поперек. А старую оставить доживать ради обратной совместимости и всё.
Если сейчас посмотреть предложения на stdcpp.ru, то там большая часть предложений начинается со словосочетания «добавить constexpr к ...». Появляются модули, концепты и метаклассы. Это приведет к чему? К тому же самому, что происходит с constexpr. При этом модифицируя существующие сущности, которые уже много раз итак модифицировались и базировались совсем на других вещах, комитет получает головную боль, поскольку нужно все сделать аккуратно и нигде ничего не сломать и не переименовать и т.д. и т.п.
Также, что-то теряет свой смысл и со временем выбрасывается. Но это всё вытекает в то, что библиотека растянет на долгие годы (ибо стандарт выходит раз в 3 года) своё эволюционирование и в итоге придёт к тому же самому, что можно сделать здесь и сейчас и в разы проще.
Есть история с Python3, которая пугает всех в комитете.

Если мы сделаем std2, то как заставить пользователей на него быстро переехать? Если переезжать долго, то придётся десяток лет развивать две версии стандартной библиотеки, получать лучи ненависти от людей, вынужденных мигрировать на новую версию библиотеки…

А главное — в чём плюсы то? Пока что, всё что хочется, все новые фишки, можно добавлять к стандартной библиотеке не ломая обратную совместимость.
Если мы сделаем std2, то как заставить пользователей на него быстро переехать? Если переезжать долго, то придётся десяток лет развивать две версии стандартной библиотеки, получать лучи ненависти от людей, вынужденных мигрировать на новую версию библиотеки…

Комитет ведь что-то сподвигло сделать C++ Core Guidelines и GSL.
И я подразумеваю то, что развитие старой библиотеки прекращается. Новая в себя будет включать идентичные сущности, но без мусора, в адекватном и чистом виде.
Я не настаиваю на том, что я абсолютно прав и нужно сделать так как я говорю) Не мне решать.

А главное — в чём плюсы то? Пока что, всё что хочется, все новые фишки, можно добавлять к стандартной библиотеке не меняя обратную совместимость.

Как по мне, «пока что» — это до C++20. Уж слишком масштабное обновление будет.
Согласитесь, что constexpr уже сместил сильно с места стандартную библиотеку, о чём я выше написал.
Я вижу библиотеку сейчас, как фундамент, который не был изначально рассчитан на тот груз, который на нём стоит. И со временем любое строение изнашивается и становиться более хрупким, тем-более если сверху достроить пару этажей и дополнительные балкончики, а также кондиционеры навешать)
Аналогии почти всегда неверны. Если сейчас поломать либу, то никто на неё просто не будет переходить и получится разделение как с Python 2/3, только С++ before 20 и C++ 20 и выше. Такое себе, лично я считаю.
Ну вот как раз сейчас любой желающий может сделать такую библиотеку для себя, смешав с GSL по вкусу, поэтому проблема не сказать, чтобы была очень острой, на мой взгляд. А второго Python3 нам не надо.
Странно записывать std::visit во что-то древнее, ему ведь всего 1 годик :)

Так что конкретно вы хотите изменить в стандартной библиотеке? Какие основы хотите пересмотреть?
Странно записывать std::visit во что-то древнее, ему ведь всего 1 годик :)

Там весь конфликт был не вокруг std::visit, а вокруг того, что Rust-овщики пришли прогонять C++ сообщество со своими правильными умными указателями)
Кто же спорит — указатели в Rust самые правильные!

Жаль только что пользоваться ими не удобно, оттого и серьёзных програм на нём нет.</troll-mode>
Я не буду с вами спорить и я сам тремя руками за C++.
Просто в том споре мы знатно присели, оправдывая себя «обратной совместимостью» и возрастом.
Уж слишком убедительные и настырные противники были.
Но я вот совсем не планирую уходить на Rust, поскольку душа у меня к плюсам тянется.
Также я не требую «такие плюшки как в Rust». Я сейчас о другом говорил всё время. О том, что уже есть, но под новым углом.
Пусть растоманы пишут что хотят, вам то какая разница? Каждый выбирает средство сам. Не хотят растоманы поддерживать тонны уже написанного кода и написать с нуля? Пожалуйста, кто ж против.
Вот я же не первый, кто пишет об «std2». Это периодически мелькает перед глазами:
вот
и
вот
и я искренне надеялся на то, что так и будет с C++20.
Вы ведь и сами желайте всё то, что есть модифицировать, но с учётом обратной совместимости вам это обходиться сложнее, чем могло быть. Мне кажется, что с 20-ым стандартом всё станет еще в пять раз сложнее.
Назревает новая эпоха, а библиотека Modern C++ всё еще не Modern.
Я видел на ютубе видео с конференции, где поднимали тему std2, нужна ли она, в каком виде, но названия видео не вспомню. Нашел только презентацию от одного из участников той дискуссии: www.youtube.com/watch?v=fjtwfauk7a8.
Да. Абсолютно тот же мотив.
Да, я там был. И могу рассказать о настроениях в зале — скептическое.
Очень надеюсь, что такой ломающей совместимости никогда не будет в Стандартной библиотеке. И в любом треде, где это будет подниматься, я буду топить исключительно против.
никто и не говорит о том, что нужно выбросить нахрен существующую библиотеку. Говорится о новой библиотеке, которая будет эдаким ремэйком старой, но с учётом новшеств последних 10-ти лет, коих предостаточно.
Вам не кажется, что внедрение концептов, метаклассов, контрактов, constexpr-выражений в текущую библиотеку не станет более пагубным действием и слишком размазанным по следующему десятилетию?
Суть в том, что будет абсолютно свежий код, который станет более поворотливым.
Что предлагают сейчас:
constexpr std::regex
Переименовать все новые *_ref функции и классы в *_view
algorithm который можно использовать в constexpr
std::complex который можно использовать в constexpr
Убедить международный комитет не дублировать все контейнеры стандартной библиотеки
Constexpr для std::char_traits и std::string_view
constexpr для begin/end/reverse_iterator/move_iterator и базовых методов array
std::allocate_unique

Наверняка где-то простое добавление constexpr к сигнатуре функции невозможно в связи с текущей реализацией. Далее следует цепная реакция. В итоге придётся перелопатить чуть-ли не всё, что есть. Чем это лучше?
Что вы выберете, сделать эко-жигуль, впихнув в старый эко-двигатель или сделать нормальный и современный эко-мобиль и далее уже плясать вокруг него?
Или вам нравится, когда требования к тем же контейнерам выражаются письменно где-то здесь? Это хотя-бы концептами поправят…
Говорится о новой библиотеке, которая будет эдаким ремэйком старой, но с учётом новшеств последних 10-ти лет, коих предостаточно.
Для этого не нужен комитет по стандартизации. Есть пример boost. Если найдётся достаточно заинтересованных людей, которые готовы будут написать эту библиотеку — мы все только рады.
Нет, там конфликт был из-за того, что один Rust-оман, плохо знающий C++, подумал, что unique_ptr и shared_ptr используются в C++ для тех же целей, для чего родные ссылки в Rust-е. И пришлось потратить кучу времени, чтобы понять это :(

Ну а так, конечно, std::visit и std::variant по сравнению с полноценным паттерн-матчингом и алгебраическими типами данных выглядит скромненько. Только вот в обозримом времени АлгТД в C++ вряд ли завезут. Если бы завезли, то в C++ было бы проще переиспользовать полезные практики из других современных языков.
ИМХО, проще тогда уже сделать новый язык, который по синтаксису, идеологии и каким-то основным приемам будет настолько сильно напоминать C++, что при желании можно будет брать имеющийся C++ный код, править его на 20-30% и получать код на новом, более современном, удобном и безопасном языке. Заодно можно было бы в приказном порядке закрыть проблемы с отсутствием единой системы сборки и общей системы управления зависимостями.

Ценность же самого C++ в том, что сохраняется совместимость с древним кодом, который написали и отладили давным-давно, но который все еще работает. У меня самого были в практике случаи, когда доставался старый код, написанный и заброшенный к тому времени уже лет 8 как, просто компилировался новым компилятором и шел в дело. Или когда старый код, который эволюционировал около 10 лет в рамках C++98 за пару вечеров был переведен под C++11. Потребовалось в ряде мест поменять std::auto_ptr на std::unique_ptr и еще что-то по мелочи. Или когда узнаешь, что где-то работает твой код, который был написан еще году в 2008-ом, и в котором были переиспользованы куски кода, написанного в 1998-ом.
ИМХО, проще тогда уже сделать новый язык, который по синтаксису, идеологии и каким-то основным приемам будет настолько сильно напоминать C++

Не соглашусь и уже далеко не одна подобная попытка была.
Стоит всё-таки разделить язык и его стандартную библиотеку.
Сам язык позволяет написать новую «идеальную» библиотеку, так что нет необходимости делать новый тысячный ЯП.
Все ключевые изменения 20-ого стандарта относятся строго к языку, а не к библиотеке, в том то и дело. И всё то, о чём я здесь так много писал, относиться только к библиотеке.
Не соглашусь и уже далеко не одна подобная попытка была.
А вот я не помню ни одной заметной попытки, где хотели бы оставить синтаксис и идеологию максимально близкой к C++.

В D изначально добавляли GC, сначала его делали без шаблонов, а когда шаблоны добавили, то у них оказался совсем другой синтаксис. Хотя и при этом портировать код с C++ на D было не очень сложно, но все-таки язык с GC — это в другую нишу.

В Rust-е и синтаксис другой, и borrow-checker с lifetimes заставляют по другому думать. И объектная модель совсем другая. Так что переделать C++ный код в Rust — это даже больше, чем переписать, тут еще и перепроектировать потребуется.

Что еще?
Открыл для себя наличие ключевых слов not, or, and в C++ и забыл о крипто-синтаксисе !, &&, ||, хотя последние два еще читаемые, но вот тоненький восклицательный знак почти незаметен. В новом коде самое то.
UFO just landed and posted this here
Пока что, любой side effect является Undefined Behavior. Скорее всего так и останется.
UFO just landed and posted this here
вам о возможном нарушении контракта скажет компилятор еще на этапе сборки.
UFO just landed and posted this here
контракты — механизм для уменьшения числа проверок в рантайме к минимуму (плюс бонусом дополнение к документации). Вы предлагаете разменять доп. быстродействие на вывод в консоль? На мой взгляд, это мало чем лучше ассертов.
UFO just landed and posted this here
Ну вы почитайте дальше, в секциях 2 и 3 подробно описано что это за «build system setting»: вы собираете в одном из режимов: «без runtime проверок», либо «только дешевые проверки» и «все проверки». Первое предназначено для релизных сборок, а второе и третье — отладочных

Я имел в виду с++ контракты. Впрочем, они полностью подходят под общепринятое понятие контракта.
UFO just landed and posted this here
UFO just landed and posted this here
Там во многих местах при работе контрактов нарочно поставлены UB.

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

А до тех пор, комитет решил выпустить контракты в минимальном виде. Виде, который подходит для использования в стандартной библиотеке и для использования в кодовых базах ряда крупных компаний.
UFO just landed and posted this here
т.е. на данный момент контракты — это просто doxygen-friendly assert и ничего более?
А кто будет гарантировать, что тело контракта — это pure function? C++ компиляторы такое осилят?
UFO just landed and posted this here
Именно, я хотел сказать, что «не делать их» — это плохой совет т.к. нет возможности гарантировать их отсутствие, следовательно рассчитывать на то, что сайд-эффектов не будет — очень ненадёжно. Это практически гарантирует UB время от времени т.к. человек не может не делать ошибок, а тут их сделать очень легко, а найти — может быть очень трудно.
UFO just landed and posted this here
Ну более error-prone механизм не припомню. Обычно UB можно избежать следуя довольно простым правилам.
компиляторы времен царя гороха поддерживают аттрибуты типа pure/const
const — это не pure. Какой C++ компилятор поддерживает pure?
они позволяют объявить что-то pure, но я не вижу, что бы компилятор мог вывести pure для функции.
float twice(float x) __attribute__ ((pure)) 
{ return x*2.0f;}
float fourtimes(float y) 
{ return twice(twice(y));}

Компилятор не узнает, что fourtimes — pure.
GCC должен уметь такое выводить. Попробуйте перепроверить, где-то на уровне IR должна выставиться инфа о том что функция pure.
тогда почему purity не вводится в стандарт вместе с контрактами и не требуется для контрактов, а вместо этого нарушение purity объявляется как UB?
Разные компиляторы умеют выводить эти атрибуты в разной степени. Думаю вы не будете рады, если у вас код с контрактами соберётся на одном компиляторе, а на другом будет говорить «error, not pure»
именно, потому нет способов гарантировать отсутствие side effects, но приходится требовать его, что точно приведёт к UB, т.к. для человека гарантировать отсутствие side effects на практике нереально, слишком нетривиальная задача.
Узнает, причём знает без всяких pure. Не будет он знать только тогда, когда у него не будет доступа к телу функции.
Он может узнать, но гарантий нет, их нет в стандарте. Стандарт не требует от компилятора, что бы он узнал. И не может требовать, на данный момент.
но гарантий нет

В контексте:
C++ компиляторы такое осилят?

Стандарт мало кого волнует. Они могут и смогут. И никаких гарантий со стороны стандарта компиляторам для этого не нужно.

Нужно, если компилятор будет выдавать ошибку компиляции. Пользователям не понравится, если компиляторХ компилирует с флагами по умолчанию, а компиляторУ — вываливается с ошибкой. Компиляторы могут выдавать предупреждения. Но на сегодняшний день это не может работать в общем случае.
Пользователям не понравится, если компиляторХ компилирует с флагами по умолчанию, а компиляторУ — вываливается с ошибкой.
Как вариант, можно проверять лишь при нестандартном флаге. Как с -Werror сделано.
Скорее всего так и будет. Но опять же, мне кажется, сделать такой C++ компилятор, что бы он всегда мог правильно определить область значений параметров в заданном контексте нереально. А значит часть контрактов не смогут быть проверены на этапе компиляции.
Все эти рассуждения про «компиляторы» имеют очень мало смысла в контексте современного С++, у которого существует только 2 живых компилятора и один полу-живой(вендорлочный). Все они это умеют, а остальные «компиляторы» не умеют даже с++17, не говоря уже о с++20.

Поэтому для подавляющего большинства пользователей С++ это будет работать, даже если не будет обязательным, либо вообще указано стандартом. А общий случай это подавляющие большинство пользователей мало интересует. Пусть работает где-то, чем не работает везде. И это правильно.

Даже с чисто формальной ТЗ стандарта подобное поведение можно включить в «необязательно» и пусть те, кто не могут — не могут дальше.

Пользователям не понравится, если компиляторХ компилирует с флагами по умолчанию, а компиляторУ — вываливается с ошибкой.

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

Не говоря уже о самой поддержке стандартов, где код вообще может не собираться. И не собирается.

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

Мой тезис: никакие гарантии от компилятора по отношению к контрактам в contracts proposal не вводятся. И не факт что какие-то серьезные гарантии могут быть введены со временем. Contract-based programming с гарантий на уровне компилятора — это не решенная на данный момент задача. И нет предпосылок к тому, что в C++ будет достигнуто решение.
остальные «компиляторы» не умеют даже с++17
software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler
Остальные не проверял. Уже как минимум 4: g++, clang, microsoft, intel — немало.
для подавляющего большинства пользователей С++ это будет работать
Будет как-то работать непредсказуемым образом. Типа как статический анализ.
одного компилятора может быть ворнинг у другого нет
Но не еррор. Разговор начался с «не скомпилируется с ошибкой», подразумевая гарантированное поведение, на которое можно положиться.
Не говоря уже о самой поддержке стандартов, где код вообще может не собираться. И не собирается.
Если поддержка соотв. стандарта не объявлена. Если объявлена — в основном всё работает или считается багом и чинится.
С технической точки зрения компилятор всё знает и никаких проблем нет.
Только в теории. Если бы всё было так просто, были бы мейнстримные языки с compile-time contract based design, а их до сих пор не существует! Даже в Rust не могут договорится о том, как их сделать и делать ли.
компилятор сможет за этим следить.
В общем случае — не сможет. Как и не сможет надёжно оптимизировать код так же качественно, как тренированный человек. Максимум что можно будет сделать — это ещё один тип диагностик в статическом анализе, которые могут словить, а могут и не словить проблему.
Contract-based programming с гарантий на уровне компилятора — это не решенная на данный момент задача.

На каком основании?

Откуда вообще вы взяли свой тезис, если отвечал на:

C++ компиляторы такое осилят?

Как-то вы быстро тему поменяли. Ничего похоже на ваши тезисы я в этой ветке не видел. Зачем вы начали ссылаться на «осилят», если сейчас это для вас перестало быть существенным, и вообще играть какую-либо роль?

software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler

И? Вы увидели там c17 и вам показалось, что оно умеет в с++17? Нет, вам только показалось.

Остальные не проверял.

Остальных нет.
g++, clang

Да.
microsoft

Кое как.

intel — немало.

Нет.

И того 2.5, о которых я и говорил. Больше нет.

Будет как-то работать непредсказуемым образом. Типа как статический анализ.

И? Из этого что-то следует? Нет. Опять же, я тут вижу попытку манипулировать, ведь оно работает очень предсказуемым образом. Оно не гарантирует срабатывание на все ошибки, но на те, что срабатывает — оно срабатывает предсказуемо.

Но не еррор. Разговор начался с «не скомпилируется с ошибкой», подразумевая гарантированное поведение, на которое можно положиться.

На ворнинг можно положиться. Так же, к чему тут эта отсылка на ерррор — мне не ясно. С++ такой язык, а рантайм такая штука, что ничего предсказать нельзя и отсутствие ошибок не говорит о каком-то «положиться». Вообще нигде и никогда это ни о чём не говорит. Если вы пытаетесь апеллировать к этому, то это не тот язык, как и не любой другой в этой реальности.

Если поддержка соотв. стандарта не объявлена. Если объявлена — в основном всё работает или считается багом и чинится.

Это так смешно читать после того, как вы засыпались с поддержкой С++17 у icc.

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

Только в теории.

В реальности, особенно в вашем примере.

Если бы всё было так просто, были бы мейнстримные языки с compile-time contract based design, а их до сих пор не существует!

Дело не в этом. Проблема знать компилятору обусловлена либо невозможность(даже в теории) что-то знать, ведь если состояние зависит от рандомного поведения рантайма, то уже никакие теории не помогут.

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

Даже в Rust не могут договорится о том, как их сделать и делать ли.

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

В общем случае — не сможет.

Я уже писал об этом — это мало кого интересует. Никакая собранная программа на С++ в общем случае работать корректно не может. Дальше что?

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

И? К чему это всё. Как минимум в ситуации, когда оно может словить проблему статически — оно не будет генерировать рантайм-проверки. Это уже профит, 100% профит.

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

Как-то вы быстро тему поменяли
Да нет, просто «осилят» было в контексте, который Вы проигнорировали.
вам показалось
Нет, я увидел список поддерживаемых из c++17 фич, которых на данный момент 75% и динамика на увеличение с каждой версией.
Остальных нет.

EDG eccp более 80%, Intel > 75%, PGI ~50%, а что будет через 2 года?
На ворнинг можно положиться.
Только если он гарантируется. Здесь же не факт, скорее будет как в статических анализаторах.
вы засыпались с поддержкой С++17 у icc
Засыпался? тем что привёл ссылку на список поддерживаемых и не поддерживаемых фич? Я не говорил, что icc поддерживает c++17, лишь указал что он движется к этому.
Дело не в этом
Не в чём?
ведь если состояние зависит от рандомного поведения рантайма, то уже никакие теории не помогут.
По идее в таком случае области определения — R или что там, в зависимости от типа. Вот и вся теория.
определить наличие чистоты у всех функций не представляется возможным.
Я об этом говорил, что это в C++ это нереально.
Раст не показатель
Согласен, вы знаете примеры, где реализован compile time design by contract?
Дальше что?
а то, что исходное утверждение
вам о возможном нарушении контракта скажет компилятор еще на этапе сборки.
не совсем корректно т.к. пропущено «если повезёт».
По поводу самой отсылки к каким-то гарантиям. Они никого не интересуют.
Ну как, исследования то идут.
Когда я писал «уже как минимум 4», я имел ввиду, конечно, что поддерживают c++17 хоть как-то, а не «вполне», сори.
Там вся куда сложнее, но мы потихоньку до этого дойдём.
Давайте по по порядку, чтобы сложнее было уходить от ответов:

Если поддержка соотв. стандарта не объявлена. Если объявлена — в основном всё работает или считается багом и чинится.

EDG eccp более 80%, Intel > 75%, PGI ~50%

Дак работает, либо на 50%?

У вас вилка, если вы признаете, что «в основном работает» — это 50%+, то ваши рассуждения на тему «должно собираться» — не работают, ведь оно попросту не будет собираться.

Это является попыткой опровержения моего тезиса:

Не говоря уже о самой поддержке стандартов, где код вообще может не собираться. И не собирается.


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

Ваши два ответа мне противоречат, по крайней мере, друг другу. Разрешите противоречие.

Дак работает, либо на 50%?
Если объявлена, то работает. Что объявлено, то и работает.
Заявленная поддержка стандарта должна позволять компилятору собирать то, что заявлено. На данный момент 2.5 компилятора заявили практически полную поддержку, и еще 2-3 двигаются в этом направлении, два из которых довольно близки к достижению полноценной поддержки, как мне кажется.
Если объявлена, то работает.

Она не работает, значит не объявлена(на самом деле объявлена(Use compiler option -std=c++17), но это будет слишком просто), значит не поддерживает, значит не собирается.

Таким образом ваша попытка оспорить:

Не говоря уже о самой поддержке стандартов, где код вообще может не собираться. И не собирается.

Целиком и полностью несостоятельна.

Точно так же, как и:

остальные «компиляторы» не умеют даже с++17

software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler
Остальные не проверял. Уже как минимум 4: g++, clang, microsoft, intel — немало.

Таким образом, вы пытались выдавать тех, кто не умеет:

два из которых довольно близки к достижению полноценной поддержки

Близки не значат могут. За тех, кто умеет. Это нехорошо.

Таким образом, как я и говорил, умеют в с++17 только 2.5 компилятора.
Целиком и полностью несостоятельна
Ну почему же. Поддержка может быть частичной.
остальные «компиляторы» не умеют даже с++17
Умеют частично, как минимум 2 на более чем 70% фич. Умеют почти полностью 2, и один — с натяжкой. В той или иной мере — не менее 5.
Ну почему же. Поддержка может быть частичной.

Абсолютно неважно, она несостоятельна в любом случае, если этот случай не «полная поддержка». Напомню, что вы оспаривали возможно собираться/не собираться на разных компиляторах стандартного кода. С частичной поддержкой — он так и будет, то собираться, то не собираться и никакой «уверенности» у вас не будет.

Умеют частично, как минимум 2 на более чем 70% фич.

Инфу вы скорее всего взяли отсюда:

en.cppreference.com/w/cpp/compiler_support

Смотрим на поддержу с++14 у icc — даже она не полная. Я проверил constexpr у complex — его нет, т.е. NA == не работает.

Смотрим на поддержу С++17 — она никакая. Нету там 70%. От силы 50%

Что такое «EDG eccp» мне неведомо. Гугл находит только какой-то фронтед, т.е. это вообще не компилятор, либо гугл о нём не знает.

И того, 2.5 компилятора(можно даже msvc записать третьим, если поверить в честность МС. Но он вендорлочный, а так же слаб как компилятор) + какие-то куски поддержки у icc. Всё, больше мир компиляторов не знает.

icc я считал по таблице с сайта интел, там 9 из 37 пунктов указаны без поддержки — ~75% поддерживается. Но я не знаю, есть ли другие. Точнее я знаю, что есть какие-то специфические компиляторы для HPC от вендоров (IBM, HP?), но не могу сказать ничего конкретного.
icc я считал по таблице с сайта интел, там 9 из 37 пунктов указаны без поддержки — ~75% поддерживается.

Ну они как минимум могут написать не все пункты из стандарта(что мы и видим) + разные пункты не равнозначны.

en.cppreference.com/w/cpp/compiler_support — вот тут есть и оракловский и айбиэмовский и хпешный. Результаты нулевые.

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

Вот и остаются gcc/clang из портабельных + один вендорлочный, который недавно так же начал догонять. Посмотрим, на сколько его хватит.

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

Не в чём?

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

Я об этом говорил, что это в C++ это нереально.

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

Согласен, вы знаете примеры, где реализован compile time design by contract?

Зачем вы пытаетесь пропихнуть новые, свободно-трактуемые, термины в обсуждение? Это настолько глупо.

Любая реализация чего-либо сравнивается с идеалом, причём этот идеал определяется не вашими фантазиями на тему. У нас есть какой-то код, есть некий порог, объективный порог — то, даже чего не может пойти статический анализ. За него зайти нельзя.

И нет смысла рассуждать о том, что находится за этим порогом. Это просто демагогия и попытка отмыть свои суждения. Если конкретней, то нет смысла рассуждать о том, чем в принципе существовать не может. Не может существовать 100% полного как compile time design by contract, так и рантайм. Всегда будут какие-то допущения, да в части кода исполняющего в рантайме ратайм проверки более мощные, но они имеют и свои минусы, которые на поверхности.

В какой-то мере design by contract существует в компиляторах и сейчас, только он не прямой и эти контракты описываются косвенно. Ссылаться же на другие языки не имеет смысла, С/С++ имеет самые передовой компилятор в сравнение с которыми все остальные — детский садик.

не совсем корректно т.к. пропущено «если повезёт».

Во-первых я говорил не об этом, а вторых оно корректно. Ваши «если повезёт» — глупости. Всё в этом мире работает в ситуации «если повезёт». Но дело не в этом.

Давайте подумаем:
о возможном нарушении контракта

Это можно трактовать как допущение ложно-положительных срабатываний, в ситуации когда компилятор не может ничего проверить. Т.е. он будет говорить «я не знаю — возможно нарушение — вставлю проверку/сообщу пользователю». Если же он что-то нашел, то результаты уже объективны и он точно сможет сказать — есть ли там нарушение, либо его нет.

Таким образом ситуации в которой контракт будет нарушен, но компилятор об этом не скажет — быть не может, может быть ситуация «нарушений нет, а показывает, что „возможно есть“», но это не проблема.

Ну как, исследования то идут.

Они могут идти сколь угодно долго, но это ничего не даст. Это невозможно. В общем случае поведение рантайма в компилтайме предсказать невозможно, точка.

Есть два варианта: это изучать и основывать свой анализ на поведение конкретного окружения, но это не будет «в общем случае». Либо, создавать искусственное окружение с какими-то заранее определёнными свойствами, либо другой путь того же — отсекать возможность у программы делать то, результат чего может быть непредсказуем.

Но всё это сужение общего случая.

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

С чем вы спорите? С каким утвержденим? Напомню, мой основной тезис:
В C++ нельзя положиться на то, что нарушения контрактов (как они есть в текущем proposal) будут обнаружены на этапе компиляции, как писал изначально Antervis. Есть некоторое число несложных частных случаев, когда это сработает, но в основном — нет. Это и понятно, ведь, если я правильно понял, contracts proposal — это немногим более чем новый синтаксис для assert. Если Вы с этим тезисом согласны, то о чем спорите?

В общем случае поведение рантайма в компилтайме предсказать невозможно, точка.
Для меня не очевидно, почему в рамках конкретного (возможно, пока не созданного) языка программирования это невозможно, возможно с исключениями типа unsafe в Rust, но всё же. Есть же seL4 и Spec#.
В однопоточном случае обнаружить нарушение контракта компилятор скорее всего сможет. Или, как минимум, потенциальное нарушение контракта при отсутствии ub (типа записи через const_cast по const &).

Что до многопоточного случая — формально, data race без примитивов синхронизации — ub. А при наличии примитивов синхронизации уже известно, где теряются гарантии контракта.

По сути, в коде без ub контракты будут работать на уровне поддержки их компилятором.
В однопоточном случае обнаружить нарушение контракта компилятор скорее всего сможет
Не верится в «скорее всего». Для этого фактически компилятор должен свести условие контракта до constexpr!, что в C++ чаще невозможно, чем возможно.
По сути, в коде без ub контракты будут работать на уровне поддержки их компилятором.
Что Вы имеете ввиду? Согласно пропозала полная поддержка компилятора означает просто поддержку проверки в runtime (как assert), если я правильно понял.
Кроме того, как компилятор будет отличать многопоточный случай от однопоточного?
Для этого фактически компилятор должен свести условие контракта до constexpr!, что в C++ чаще невозможно, чем возможно.

Компилятор может оптимизировать и то, что не constexpr, так ведь? Только гарантий нет. Вот здесь то же самое.
Что Вы имеете ввиду? Согласно пропозала полная поддержка компилятора означает просто поддержку проверки в runtime (как assert), если я правильно понял.

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

отслеживать входы/выходы из критических секций.
Компилятор может оптимизировать и то, что не constexpr, так ведь?
Так. То есть вероятность, что нетривиальный контракт будет проверен на этапе компиляции примерно такая же, как и вероятность того, что аналогичное выражение было бы оптимизировано компилятором до `noop`. Невысокая, как мне кажется. Ну и, что хуже всего — это то, что программисту будет очень трудно уверенно предсказать, когда это сработает, а когда нет. Для оптимизаций это нестрашно, а для контрактов — весьма неприятно.
Так там же принцип «better safe than sorry» — если компилятор не может вывести гарантии для requires, он попросту предупредит «вот здесь может быть нарушен контракт». А там уже дальше либо программист еще гарантию припишет, либо будет работать с лишней проверкой. В самом худшем случае мы получим то же самое число лишних проверок пред/постусловий, что и в коде до контрактов. В конце концов, сейчас многие проверки (типа пресловутых nullptr?) оптимизируются из-за дублирований, а с контрактами у компилятора больше инфы
он попросту предупредит «вот здесь может быть нарушен контракт».
Если он будет предупреждать об этом в 9 контрактах из 10, то толку? С лишней проверкой будет работать только в дебаг билде.
В самом худшем случае мы получим то же самое число лишних проверок пред/постусловий, что и в коде до контрактов
Ну да, точно как с assert.
почему для вас «отсутствие формальных гарантий» эквивалентно «будет работать в 1/10 случаев»?
Так я примерно оцениваю возможности компилятора оптимизировать нетривиальный код типа argument.is_valid() до noop
Вы завели диспут по поводу второстепенного по отношению к обсуждаемой теме технического нюанса, с которым я и не спорил, и сами у себя его выиграли. Ну ОК.

Таких нюансов было много, просто они очень показательны. Они говорят о серьёзности аргументальной базы.

В C++ нельзя положиться на то, что нарушения контрактов (как они есть в текущем proposal) будут обнаружены на этапе компиляции,

Контракты не являются контрактами времени компиляции. А то, что будет работать на этапе компиляции — на это можно положиться.

Опять же, ложно-положительную(в контексте нарушения) ошибку компилятор дать не может, а значит всё валидно и я на это уже отвечал.

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

Это и понятно, ведь, если я правильно понял, contracts proposal — это немногим более чем новый синтаксис для assert.

Нет, вы ещё утверждали то, что „они ничего не отличаются от асертов“/»преимуществ не имеют", а как мы выяснили — имеют.

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

Для меня не очевидно, почему в рамках конкретного (возможно, пока не созданного) языка программирования это невозможно, возможно с исключениями типа unsafe в Rust,


«За исключениями» — противоречит общему случаю. У общего случая нет исключений, на то он и общий. Вам это тоже не очевидно?

Есть же seL4 и Spec#.

Это всё типичная подмена понятий и манипуляции. Никакого отношения они к гарантиям не имеют и никакое поведение рантайма не предсказывают.

Они просто живут в песочницах, которые им организовывают те самые песочницы на языка, которые ничего не гарантируют и не предсказываю, а предсказывают конкретные люди и для конкретных случаях. За рамках этих случаев — песочницы не работают.

Это не контекст нашего разговора
А что же это? Это то сообщение, на которое я отвечал. Как это может не быть контекстом? Вы продолжаете этот контекст игнорировать.
Вы так и не показали определяющих контекст ваших заглавных постов
Многократно показывал. Повторяю последний раз.
Вот комментарий, на который я отвечал:
вам о возможном нарушении контракта скажет компилятор еще на этапе сборки.
И мы с автором уже разобрались в вопросе. Что Вы цепляетесь к каким-то вырванным из контекста репликам?
это ваши попытки отмыть себя
Что Вас так заботят мои внутренние мотивации? Кругом враги? Оставляю вопрос об истоках хамства, глупостей и демагогии в Ваших постах Вам на самостоятельную проработку.
Многократно показывал. Повторяю последний раз.
Вот комментарий, на который я отвечал:

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

Контекст определяют ваши ответы, которые были до моих. ВАШИ, а не чьи-то. Ссылать на левый пост задним числом трактовать его хочется — это не контекст.

И мы с автором уже разобрались в вопросе. Что Вы цепляетесь к каким-то вырванным из контекста репликам?

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

Что Вас так заботят мои внутренние мотивации?

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

Неудачи есть — пациент врёт. А раз он врёт, то ни о какой объективности и вменяемости в споре с ним речи быть не может.

Оставляю вопрос об истоках хамства

Покажите хамство? Я думаю точно так же, как вы мне показали четвёртый поддерживающий С++17 компилятор.

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

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

Меня мало интересует то, на что вы где-то отвечали.
Я и говорю, Вы сами с собой спорите и вырываете мои слова из контекста, который задаётся всеми предыдущими репликами в диалоге, не только моими. Как крайний пример «согласен» вообще без предшествующей реплики не имеет смысла.
Ссылки на левые комменты, которые вы пытаетесь задним числом трактовать
Задним числом? Я не менял позицию по поводу той реплики и даже уже коротко излагал её повторно. Все мои реплики станут Вам понятны, если вы прочитаете диалог не с середины, а с начала.
Да, юлить можно сколько угодно
Аппелировать к личности тоже, но мне это не интересно. По сути Вам сказать, видимо нечего.
я ловлю
Никого Вы не ловите, только демагогию разводите, вырывая из контекста и не понимая, что прочитали.
пациент врёт
Кругом враги и дураки?
вы мне показали четвёртый поддерживающий С++17 компилятор.
Ну снова вы за меня додумываете и демагогию разводите. Так-то и одного поддерживающего С++17 компилятора не существует, у всех есть хотя бы небольшое несоответствие. Там в контексте было понятно, что речь шла о, возможно, частичной поддержке.
почему вы не отвечаете и не показываете их
Потому что это мне не интересно, меня Ваша личность не интересует, мне не нужно самоутверждаться, пытаясь кого-то поймать на чем-то.
у меня есть прецедент на который я могу постоянно ссылаться
Во первых, Вы можете ссылаться только на свою демагогию и придирки не по теме. Во-вторых даже если бы я ошибся, это ничего не доказывает.
Это бесполезно, я вижу одно враньё. Но мы пойдём другим путём, я докажу, что данный человек — болтун. Очень просто юлить и врать на абстрактные темы, а вот некие конкретные темы, тот же разговор о С++17 достаточно очевиден и тут сложно врать.

Ну снова вы за меня додумываете и демагогию разводите.


остальные «компиляторы» не умеют даже с++17

software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler
Остальные не проверял. Уже как минимум 4: g++, clang, microsoft, intel — немало.


Т.е. тут человек отрицал моё утверждение, в противном случае он бы это не писал. Это явное отрицание:

С чего же всё началось:

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


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

Это человек пытался крыть тем, что вы видите выше. Т.е. отсылкой на то, что это плохо.

На что я ответил тем, что вы так же видите выше. Компиляторы итак не собирают один и тот же код ДАЖЕ С ЛЕВЫМИ ФЛАГАМИ, не говоря уже о «по умолчанию».

И того мы видим тут явную ошибку. Ошибка и по части утверждений в сторону поддержки стандарта у более, чем 2.5 компиляторов. И в части попытки съехать на «компиляторы не могут компилировать/не компилировать код по своему желанию», которая так же оказалась неудачной. Могут, при это ещё как.

Я уже один раз позволил человеку уйти от ответа, но похоже он ничего не понял и продолжает врать. На этот раз такого не будет.

И тут существует два варианта, первый — человек объясняет почему и как так вышло, и почему неправ я, а не он. Второй — я могу с чистой совестью признать оппонента вруном и болтуном, которые не мотивирован к честному диалогу, а мотивирован врать и оправдывать собственные неудачи/ошибки.

UFO just landed and posted this here
Всё очень просто. Вы отвечаете на «предсказать поведение в рантайме», отвечаете примерами неких языков. Эти языки существуют в рамках не реальных, а виртуальных исполнителей. Т.е. виртуальных машин с виртуальной же моделью памяти и всем остальным.

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

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

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

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

Таким образом, упомянутый вами языки не являются предсказуемыми. Они существует в рамках непредсказуемой ВМ, непредсказуемых биндингов, ограниченных возможностей. Каждый из этих пунктов целиком и полностью противоречит понятию «общий случай».
UFO just landed and posted this here
Даже если говорить о языках, о всякой хардкорщине вроде языков с зависимыми типами, некоторые из них вполне себе могут взаимодействовать с внешним миром.

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

Естественно, есть некоторые предположения о нижележащей реализации.

Дело не в этом, каждая реализация обладает своими свойства и завязка на конкретные реализации работает только в рамках этих конкретных реализаций. Всё, точка. Учитывать нюансы работы каждой конкретной платформы в общем случае невозможно.

Ни о каких микродах и космических лучах речи не шло — это было бы слишком просто, для меня.
Всё в этом мире работает в ситуации «если повезёт».
Это демагогия. В контексте нашего разговора примером гарантированного поведение (в противоположность «если повезёт») являются гарантии, которые обеспечивает Rust или Haskell, например. В C++, contacts proposal не предполагает вообще никаких дополнительных гарантий, кроме тех, что есть у assert. Я предполагаю, что эта ситуация, к сожалению, фундаментально не улучшится из-за особенностей C++: его системы типов, и устройства C++ компиляторов которое продиктовано самим языком.
искусственное окружение с какими-то заранее определёнными свойствами
Стандарт языка программирования и есть этим искусственным окружением. C++ не достаточно сужает контекст что бы реализовать сколь-нибудь серьезный static contact-based programming, как мне кажется.
В контексте нашего разговора примером гарантированного поведение (в противоположность «если повезёт»)

Это не контекст нашего разговора — это ваши попытки отмыть себя. Вы так и не показали определяющих контекст ваших заглавных постов(не тех, которые были после, а те, которые были в этой ветке на момент моего ответа).

которые обеспечивает Rust или Haskell

Ничего они не обеспечивают «в общем случае» и я уже об этом говорил. Но будучи вами очень удобно спорить — можно просто брать и игнорировать неудобные тезисы/ответы.

Всё это работает в рамках неприемлемых для С++ рамках, в которых существует раст/хаскель. Что тут делает хаскель — мне неясно. Но у раста даже формально есть ответ — unsafe и всё, всё очень быстро и сразу перестаёт работать.

В C++, contacts proposal не предполагает вообще никаких дополнительных гарантий, кроме тех, что есть у assert.

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

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

Стандарт языка программирования и есть этим искусственным окружением.

Не в случае С++.

C++ не достаточно сужает контекст что бы реализовать сколь-нибудь серьезный static contact-based programming, как мне кажется.

Оно и не нужно. Нужно лишь лучше ассертов, а всё остальное — ваши придумки.

UFO just landed and posted this here
Только в том случае, если вам условные данные приходят по сети или из файла, и статически что-то про них гарантировать вы не можете при всём желании. Тогда да, конечно. Но от этих проверок вы и так никуда не убежите.


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

А все рассуждения уровня «ну не все же можно» — простая демагогия, когда нету ответов.

По части оптимизации — да. По части системы типов — ну…


Это простая ссылка на то, чего в С++ нет, но это не значит, что это является чем-то более сложным, чем то, что есть сейчас в С++.

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

UFO just landed and posted this here
Вы ещё говорили о том, что рантайм-проверки более мощные.

Естественно, что в рамках рантайма рантайм-проверки более мощные.

Либо у нас с вами сильно разное определение мощности, либо это совсем не так.

В чём конкретно проблема?
UFO just landed and posted this here
Дело не только в информации, ею всё не ограничиваются. Состояние исполнителя, факт его работы, состояние памяти и т.п.

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

В контексте кодогенерации. Прокомментирую это более подробно. В случае с данными «по сети»/«из файла» я уйду от проверок статически. Не ото всех, но уйду. Статически я могу имплементировать оптимальные проверки. Таким образом, даже если некая валидация данных происходит в рантайме — чистые компилтайм-проверки проигрывают миксу.
UFO just landed and posted this here
А в каком языке это хорошо работает? На сколько я знаю, в передовых (?) в плане design by contract языках Eiffel и Spec# так и не довели идею до того что бы можно было интегрировать в мейнстримные языки.
UFO just landed and posted this here
Спасибо за ликбез. Исправляю свой тезис: «не решенная в мейнстримных языках задача».
UFO just landed and posted this here
UFO just landed and posted this here
Можно, только не нужно дёргать фразы. Там говорилось «положится» в контексте false-positive. Компилятор всегда может сказать — есть гарантия, либо её нет. И если он дал эту гарантию — она есть и на неё можно положиться.

UFO just landed and posted this here
Решённая. Просто не в С++.
А где? Самое продвинутое, что мне известно — это Eiffel и Spec# и не то что бы там прям вот всё гладко. Как я понял, авторы решили, что для внедрения в мейнстрим не готово. Тянуть доказательства вручную через весь код — это, как мне кажется не достаточно хорошее решение. Слишком много геморроя, нужна автоматизация.
UFO just landed and posted this here
ну почему же. Вот есть у вас, скажем, простое выражение foo(bar());. bar() обещает (ensures) вернуть вам ненулевой unique_ptr, который foo() ожидает (expects) на входе. Перепроверять гарантии не нужно, потому что компилятор убедится в выполнении контракта в bar() и выкинет лишние проверки внутри foo()
компилятор убедится в выполнении контракта в bar() и выкинет лишние проверки внутри foo()
А как? А что если у нас многопоточный код?
UFO just landed and posted this here
Нельзя просто так взять и посмотреть в ассерты библиотечных функций.
UFO just landed and posted this here
модули тоже могут распространяться в виде бинарников
UFO just landed and posted this here
что мешает распространять бинарник с описанием модуля и библиотеку с реализацией двумя файлами?
Режим без runtime проверок аналогичен режиму с выкинутыми assert — не гарантирует безопасности. Т.е. если программа корректно скомпилировалась в режиме «все проверки» ещё не значит, что без проверок все будет ОК, т.к. результат проверки зависит от рантайм-значений.
механизм для уменьшения числа проверок в рантайме
И чем же контракты в этом плане выигрывают у std::assert? Что такого в них есть, что позволит снизить число проверок в рантайме, по сравнению с использованием assert?
как минимум, гарантии функции (ensures). А еще проверки предусловий вынесены из функции, соответственно вы будете знать о том, что библиотечная функция не умеет принимать nullptr на этапе компиляции, а не отладки.
чем ensures отличается от assert перед return? С точки зрения компилятора?
вы будете знать о том, что библиотечная функция не умеет принимать nullptr на этапе компиляции
Нет, придётся смотреть документацию всё равно. Узнать о том, что библиотечная функция не умеет nullptr можно будет только намеренно подав в неё nullptr и запустив эту ветку кода, дождаться исключения.
чем ensures отличается от assert перед return? С точки зрения компилятора?

а если компилятор не знает о том assert'е?

Нет, придётся смотреть документацию всё равно. Узнать о том, что библиотечная функция не умеет nullptr можно будет только намеренно подав в неё nullptr и запустив эту ветку кода, дождаться исключения.

Если у функции объявлен контракт, компилятор даже не соберет код с ошибкой «вы собираетесь передать nullptr в функцию, которая его не ждет» или «здесь возможен nullptr, а вот эта функция его не умеет, добавьте проверку»
а если компилятор не знает о том assert'е?
Согласен, в случае бинарной библиотеки контракт — это удобный способ сохранить assert <в header файле>.
не соберет код с ошибкой «вы собираетесь передать nullptr в функцию, которая его не ждет»

int value(int* ptr) 
[[expects: ptr != nullptr; ]]
{return *ptr;}
int main()
{
  int a=1;
  int* p=&a;
  //in parallel thread: p=nullptr;
  return value(p);
}

Компилятор не знает на этапе компиляции, что такое p.
«здесь возможен nullptr, а вот эта функция его не умеет, добавьте проверку»
Врядли компилятор такое сможет в общем случае. Вывести всё множество возможных значений параметра в c++ звучит фантастически. Нет даже механизма указать, что p — !nullptr, можно только надеяться, что компилятор откуда-то сам это выведет. Но нет никаких гарантий, что там, где один компилятор выведет — другой тоже осилит, этого нет в стандарте.
в однопоточном случае компилятор очень даже может вывести nullptr/!nullptr, иначе бы он попросту не умел оптимизировать код. Контракты только увеличат информацию, которой располагает компилятор.

Что до многопоточного — наверно, как всегда надо аккуратно проектировать и использовать потокобезопасные инструменты. А ошибки ловить контрактами в отладочном режиме. Даже в вашем примере, в силу отсутствия volatile, после оптимизаций в value(p) может передаться &a.
Может, но не гарантировано. Мы обсуждаем то, будет ли компилятор выдавать ошибку компиляции в случае нарушения контракта. Я говорю что не будет т.к. нет возможности обеспечить одинаковое поведение для разных компиляторов. Ошибка компиляции должна гарантироваться (стандартом) на всех компиляторах, этого обеспечить на данный момент нельзя. На этапе исполнения — да, все компиляторы смогут проверять такое.
Ошибка компиляции должна гарантироваться (стандартом) на всех компиляторах

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

Поэтому, в контексте «а могут ли больше, чем ассерты?» — могут. И стандарту для этого гарантировать ничего не нужно. А в ситуации с асертами — этим никто заморачиваться не будет, и там даже «могут» не сработает.

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

Нету, но разговор был не об этом. Вы просили назвать преимущества — вам их назвали. Ссылки на «а вот в стандарте нет» тут мало применимы, ворнингов и прочего статического анализа в стандарте тоже нет, но это же не значит, что его нет.

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

Преимущество очевидно.

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

Ну дак это ничему не мешает, в стандартном режиме можно и подавлять ошибки. В любом случае ворнинги никому не помешают, а уже этого достаточно. А Werror — есть всегда.
Вы просили назвать преимущества — вам их назвали
Не совсем, я отвечал на «не соберет код с ошибкой» и говорил, что в общем случае компилятор не сможет выдать ошибку. А в остальном я согласен — такой себе assert-based static analysis.
И чем же контракты в этом плане выигрывают у std::assert? Что такого в них есть, что позволит снизить число проверок в рантайме, по сравнению с использованием assert?

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

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

Вот заглавие ветки.
Но это ещё не весь контекст.
Нет смысла гонятся за мистическим «общим случаем», которого нигде в реальности нет.
А с этим никто и не спорил. Человек написал «вы будете знать о том, что библиотечная функция не умеет принимать nullptr на этапе компиляции» и «не соберет код с ошибкой», а я добавил "… если повезёт". Всё.
Но это ещё не весь контекст.

Нет, весь. Я не видел и не вижу ничего, что добавляло деталей в этот контекст. Может я плохо искал?

Человек написал «вы будете знать о том, что библиотечная функция не умеет принимать nullptr на этапе компиляции» и «не соберет код с ошибкой», а я добавил "… если повезёт". Всё.

Это уже контекст другого человека, а не ваш. К тому же, это целиком и полностью противоречит вашим начальным утверждениям. Не всегда всегда больше никогда, а вы утверждали «никогда»(либо, по крайней мере, «меньше, чем не всегда»).

Это контекст этого обсуждения. Все впосты, предшествующие моему следует учитывать. То что я писал, я писал в ответ на все это обсуждение.
Моё утверждение в том, что «вы будете знать о том, что библиотечная функция не умеет принимать nullptr на этапе компиляции лишь иногда/если повезёт». Где я писал иначе?
Нету никакого контекста, вы его придумали. Я вам задал вопрос — покажите. Говорить о чём-то можно сколько угодно, рассказывая, что что-то есть, когда его нет.

То что я писал, я писал в ответ на все это обсуждение.

Так не бывает, вам показали противоречия в ваших суждениях, когда вы вопрошали о «а что же может быть в сравнении с ассератами» — вам показали, вы начали ехать в сторону «а ну дак это же не гарантирует». Там вы много где засыпались, начиная с того, что само требование гарантий времени компиляции в контексте С++, да и вообще какого-либо реального язык — целиком и полностью несостоятельны.

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

Моё утверждение в том, что

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

Ассерты выкидываются на этапе препроцессинга. Поэтому компилятор ничего не знает о том что они там были. Контракты выкидываются самим компилятором. Компилятор выкинет проверку на, например, ненулевой указатель, но все равно сможет использовать "сакральное знание" для оптимизации.

Это справедливо только для бинарных библиотек. Если у компилятора есть исходники — «сакральное знание» из assert ему также доступно. Вот и выходит, что контракты — это doxygen-friendly assert, + требование расположения в header файле подобно шаблонам.
Это было бы практически очень полезно и сделало бы из контрактов функциональный аналог «зависимых типов с эрейзом после тайпчекинга». Я правда, не представляю, как это реализовать в компиляторах.
UFO just landed and posted this here
По идее это можно автоматизировать через механизм изменяемых типов/Typestates. Но к плюсам это, конечно, не относится.
UFO just landed and posted this here
Ну это когда (под)тип переменной меняется (автоматически) в по ходу выполнения. Например
float sqrt(float x)
{
  if constexpr(stateof(x, float#nonnegative) {
    return ::sqrt(x);
  }
  return NaN;
}
int a=123; //int
int b=(i+5)*2; // int#even
int c=b*b; //int#even#nonnegative
float d=sqrt(c); //call sqrt(float#notNaN#nonnegative)

где «теги» (state) типа (#even, #nonnegative, etc) определяются на этапе компиляции. И в функции они передаются тоже, в идеале прозрачно для пользователя.
UFO just landed and posted this here
я в этом совершенно не разбираюсь, просто видел блогпосты с подобными примерами, когда читал про typestate и его выкидывание из Rust.
UFO just landed and posted this here
Эти две статьи пересекаются, но не являются подмножествами друг другу :)
Это всё здорово, многое становится действительно проще имплементировать, но с каждым стандартом всё сильнее увеличивается порог входа (потому, что надо ещё уметь читать легаси код). Это хорошо для старых кодеров — их цена повысится, новых будет всё меньше. Но для бизнеса — это довольно печально.
А у Вас есть какие-то предложения, что с этим можно сделать?
Избавляться от обратной совместимости понемногу, т.е. отключить её по дефолту — включать только с определённым флагом при компиляции.
Ну или оставить всё как есть, а продакшн переводить на какой-нибудь другой язык (rust?). Как мне видится — с++ превратился в поле для экспериментов: много удобных фич там появляется, а потом часть медленно кочует в другие, более простые языки.
1. Я думаю, Вы понимаете, что люди просто не будут выключать режим обратной совместимости и всё, так как написаны тонны кода.
2. Для новых проектов не вижу проблемы использовать только свежее подмножество С++
3. Продакшен с С++ на Rust никто так просто не возьмётся переписывать, потому что на это не так просто выбить денег. Да и есть ли смысл?
4. Хм… а мне вот кажется, что наоборот в С++ тянут вещи из других ЯП. В С++ же всё обсасывается годами (что неплохо, имхо)

Выделить подмножество языка, которое будет проще и не будет содержать в себе устаревших и небезопасных фич. Добавить библиотеку, которая поможет использовать именно это подмножество более или менее везде. Добавить статический анализатор, который ругается на любой выход за рамки подмножества, за исключением помеченных сигнальным комментарием и инкапсулированных.
Получится Core C++ Guidelines.
А потом, чтобы можно было всем этим пользоваться, добавить секцию unsafe. И получить то же, что имеем сейчас, только с unsafe {}
Новое ключевое слово не нужно, потому что есть сигнальные комментарии. А статический анализ совершенно не обязательно включать сразу со всеми проверками, можно понемногу добавлять. Ну или вообще не добавлять, если кто-то не хочет.
Главное, чтобы поддержку сети занесли.
Считаю, что поддержку многопоточности, файловой системы и сети надо было добавлять самыми первыми до синтаксических улучшений.
+1. Всё что есть в Python и C# из коробки, должно быть и в C++ из коробки.
Если бы весь этот минимум (многопоточность, ФС, сеть) сразу добавили в С++, то возможно Java и C# не появились бы :)
Да, и смешно: получается обработка одного файла (указанного в настройках программы или интерактивно пользователем), в один поток (при этом с зависанием интерфейса) и с отображением результата выполнения программы только на этом компьютере (нельзя отправить результаты по сети или сообщить о завершении работы). Попахивает 80- ыми годами :)
С++ только Qt спасает, иначе бы умер язык как не соответствующий текущим реалиям.

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

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

проблема не в том, что у всех свой строковый тип а в том, что он у всех разный. Сравните std::string, bstr_t и QString — они отличаются буквально всем. Увы, но так просто всем не угодишь.
Наличие «мелких» дублирующих типов- моя основная претензия к Qt. Например, если необходимо перебрать файлы каталога, то предварительно надо название каталога конвертировать в QString. И так практически со всеми методами библиотеки.
Зачем эти лишние телодвижения?
Ларс Кнолл на вопрос о том, когда выкинут QVector, QString и прочие велосипеды сказал, что вообще, может быть, это было бы неплохо, но планов таких нет. Имхо, было бы круто иметь Qt, в котором есть только QML/QWidgets и больше ничего.
Эй, братишка, тормозни!
А как же QRunnable + QThreadPool, которые позволяют работать на более высоком уровне задач, а не функций (как в классическом С++). Я еще там юзаю события, QReadWriteLock и файловую систему (которую пока компиляторы не реализовали).
Для этого всего есть стандартные или специализированные альтернативные реализации более высокого качества, чем в Qt.
А вы сомневаетесь в том, что есть инструменты предлагающие более высокие уровни абстракции, чем QRunnable+QThreadPool?
Я сомневаюсь в том, что человек говорящий загадками что- то реально знает.
А мне вот доводилось общаться с DaylightIsBurning раньше и есть ощущение, что у него просто достаточный кругозор чтобы так говорить. Если хотите примеров подобных инструментов, то это TBB, HPX, CAF, SObjectizer, FastFlow, QP. И это еще не все, что есть в C++ для parallel и concurrent computing.
Можно назвать другие более высокоуровневые инструменты, но нельзя из- за этого называть их более качественными. Ну а использование слова «стандартные» (т.е. из коробки) выдает брехуна с головой.
Ну да, ну да. Когда вы попытаетесь на QRunnable+QThreadPool изобразить что-нибудь, что в TBB или HPX доступно из коробки, слово «качественный», скорее всего, заиграет новыми красками.
Почему вы слово «высокоуровневая» подменяете словом «качественная»? Это разные понятия.
Наверное потому, что возможность распараллелить вычисления в HPX всяко качественнее, чем отсутствие оной в Qt.
Я где-то видел ваши(либо не ваши, но с со) баталии на тему многопоточности, когда решение на том же thread pool было проще и удобнее. Вы это объяснили тем, что «задача такая была». Так сразу нагуглить обсуждение не смог.

Ну и вот, если у нас такая задача, что нам хватает тредпулла, то каким образом оно стало менее качественным? Оно наоборот стало более качественным, ведь чем меньше всяких заморочек, тем меньше шансов ошибиться. И тут это слово не играет новыми красками.
Я где-то видел ваши(либо не ваши, но с со) баталии на тему многопоточности, когда решение на том же thread pool было проще и удобнее.
Возможно, речь про «обедающих философах», которые обсуждались на LOR-е.
Ну и вот, если у нас такая задача, что нам хватает тредпулла, то каким образом оно стало менее качественным?
Если под «качеством» понимать реализацию конкретно тред-пула в разных библиотеках, то может ваша точка зрения оправдана. Если под качеством понимать удобство решения задач из области parallel или concurrent computing, то более специализированный инструмент мне представляется более качественным.

Но тут, полагаю, идет спор о том, кто и как воспринял слова DaylightIsBurning, а не о том, что именно он подразумевал. Так что лучше дождаться его объяснения.
Я подразумевал оба компонента: то, что есть в Qt выглядит недостаточно featureful для законченного продукта но даже то, что реализовано уступает компактным аналогам. Да и изначальный посыл не только в том, на сколько хорошая реализация не-виджет частей в Qt, а есть ли смысл запихивать всё это в один Framework. Если я реализую компонент, в котором мне нужен несложный параллелизм, то тянуть зависимость от всего Qt даже как-то странно. Та же критика, что звучит в сорону boost, только в случае Qt она ещё острее, так как не-GUI части в Qt менее развиты чем в boost.
Тут обратная ситуация, если вы используете Qt для интерфейса, то вам не нужно интегрировать что-то стороннее для базовой многопоточности.
Я не вижу в этом преимуществ. Выходит, что я уже знаком с какой-то хорошей библиотекой многопоточности и использую в разных приложениях, но в случае использования Qt мне навязывают свой корявый велосипед, который более нигде не пригодится.
Нет не выходит, ну вот никак. Вы используете Qt для графического интерфейса и вам нужна базовая многопоточность, тогда за каким… извиняюсь, вам тянуть специализированную библиотеку, и жаловаться что «оно кривое», если ваше изначальное решение использовать для «базовой» многопоточности «специализированну» библиотеку — кривое. Если же вам нужна «не-базовая» многопоточность, то тут уже что Qt, что не Qt, а интегрировать сторонню графическую библиотеку со сторонней библиотекой многопоточности придется (если этого за вас уже не сделал кто-то другой, а в случае библиотек C++ — можете быть почти уверенны что нет).
за каким… извиняюсь, вам тянуть специализированную библиотеку
Принцип единственной ответственности. В этом вся суть программирования — брать наиболее удачные/подходящие реализации необходимых компонент (библиотеки) и комбинировать их. Задача авторов библиотек — сделать так, что бы они могли хорошо интегрироваться со сторонними библиотеками. Задача языка — предоставить для этого возможность. Разработка своих собственных уникальных и неповторимых решений для уже решенных задач — это недостаток, а не преимущество для фреймворка. Ну не могут Trolltech объять необъятное и предоставить лучшее модульное решение для всех направлений, которые Qt пытается закрыть. Раньше это было оправдано, когда альтернатив не было. Сейчас же QVector — это ненужный велосипед, который вынуждает только лишний раз морочить голову с передачей QVectror<->std::vector. Если решение от Qt не может конкурировать с альтернативами за пределами Qt приложений — скорее всего оно не нужно.
А «тянуть ещё одну библиотеку» — это вообще не проблема, если библиотека нормально организована. Тут, конечно, было бы круто иметь модули и пакетный менеджер, как в других языках типа Python или Rust.
Извините, но Вы живете в мире розовых единорогов и с реальной разработкой на C++ лично не знакомы? Какое «брать реализации необходимых компонент и комбинировать их» на C++? Это не всегда возможно даже в таких языках как C# и Java, которые проектировались с этой парадигмой. А в C++, «задачей языка — предоставлять для этого возможность» изначально не было (да и сейчас с этим — очень, не очень). Разработками своими собственными уникальными и неповторимыми решениями уже решенных задач весь Гитхаб забит (на всех языках). Извините, но вы реально витаете где-то в облаках «идеального мира программирования». Trolltech уже много лет никакого отношения к Qt не имеет, значит и о Qt вы знаете только понаслышке.
Почему это QVector ненужный велосипед, а не std::vector? В QVector, к примеру, можно настроить копирование POD элементов через системный intrinsic, а в std::vector это делается только через стандартные конструкторы копирования. И морочить голову с передачей std::vector туда-обратно не нужно, все методы уже есть в QVector, с самого его появления. Кроме того, в отличие от std::vector, QVector это не header-only класс, а значит его использование не приводит к «распуханию» результирующего кода, что актуально в больших проектах (потому как контейнеры — это базовые «кирпичики» и применяются на всех уровнях).
Trolltech уже много лет никакого отношения к Qt не имеет, значит и о Qt вы знаете только понаслышке.
Да я в курсе, что они название поменяли, но люди остались те же. Ларс Кнолл, который сейчас CTO пришел ещё в Trolltech. Так что не спешите с выводами.
А то что на практике не выходит всегда всё «по науке» это понятно, но это не повод теперь намеренно проектировать через ж.
А в C++ у вас не получится «по науке». Либо шашечки, либо ехать. Там есть такой принцип — «если вы это не используете, вы за это не платите». Это значит, что если ваша библиотека не использует, к примеру исключения, то объединить ее с другой, использующей их, будет почти невозможно и обязательно это будет получаться только через жо.. это самое. Нет, можно конечно учесть «все пожелания», но на выходе получится трудноуправляемый монстр с кучей параметров конфигурации, обмазанный макросами и шаблонами до полного FUBAR'a.
в C++ у вас не получится «по науке»
Тем не менее развития языка и библиотек представляет возможность сделать более «по науке». Qt от этого развития, на мой взгляд, по некоторым параметрам отстаёт из-за того, что продолжает тянуть «ненужные» куски. Я согласен с Вашим посылом, просто, мне кажется, за время развития Qt оптимум между «правильностью» и «практичностью» сильно сдвинулся в пользу «правильности», а Qt не везде за ним поспевает.
Ну как я писал — «либо шашечки, либо ехать». Практическая ценность Qt пока очень сильно перевешивает «академическую правильность» стандартной библиотеки, в области десктопных приложений :-).
Да, но только за счёт того, GUI части Qt. В плане GUI у Qt нет серьезных библиотек-конкурентов и можно потерпеть технологическое отставание в о стальном. Если бы был форк QtWidgets/QML без всего остального — мне кажется он был бы ещё предпочтительней чем Qt. На мой взгляд в тех частях, где std и Qt реализуют одно и то же, std выигрывает и по практичности и по «правильности» архитектуры. И это при том, что std в этом плане не всегда является лучшим доступным вариантом.
Это значит, что если ваша библиотека не использует, к примеру исключения, то объединить ее с другой, использующей их, будет почти невозможно и обязательно это будет получаться только через жо… это самое

почитайте про паттерн «адаптер»
Есть две библиотеки, одна — исключения бросает, вторая — коды ошибок возвращает. Вам они нужны. Но они бинарно несовместимы. Помните, «если вы это не используете, вы за это не платите»? Поэтому «адаптер» вам не поможет, там все не так просто. Здесь вовлечены такие вещи как «отвинчивание» стека и деструкторы. Вам придется копировать данные между этими библиотеками вручную, а если там еще и обратные вызовы нужны… Короче, вам придется целую третью библиотеку для этого написать и на месяц «загулять» в отладке.
Почему это QVector ненужный велосипед, а не std::vector?

как минимум потому, что у QVector'а v[i] = 0; может выполняться линейное время. Он был хорош до с++11, но не после введения move-семантики
А может и не выполнятся за линейное время; так себе аргумент. Qt это единственная библиотека GUI на C++ имеющая полный набор необходимый возможностей. То что она не использует контейнеры STL, трудно назвать недостатком. Ну допустим применялась бы в Qt стандартная библиотека. Это бы хоть-как то помогло с интеграцией ее с другой сторонней библиотекой, также использующей стандартную библиотеку C++? Практика показывает что нет. А семантика перемещения, это свойство самого языка и контейнеры Qt тоже смогут ее применять; некоторые методы ее уже используют.