Comments 269
На ней люди (в основном)
То есть там еще и рептилоиды присутствовали?
Очень интересно, буду ждать с++20.
Только возник вопрос с сопрограммами. Разве не должна функция start_accept
иметь такое определение:
future_t<void> start_accept(net::io_context& io_service);
http://jackieokay.com/2017/04/13/reflection1.html#the-road-to-standardization-reflexpr-and-operator
но статья ещё апрельская.
Кстати, вроде ещё про транзактное программирование заикались.
Исходники: github.com/ericniebler/range-v3/tree/async_ranges/include/range/v3/experimental/utility
Подтверждаю, что MultiFuture очень крутые и сильно влияют на стиль разработки. Код получается значительно приятнее и читаемее.
Насколько мне известно, в Windows, как раз, проблема была более-менее решена, MSVC просто добавлял конструктор ofstream от wstring, а в GCC/Clang его не было. Короче, слава новым стандартам, теперь можно жить.
Если у меня стоит русская локаль, и я хочу прочитать файл с индийскими буквами, это не сработает, так?Сработает. Более того, всё сработает даже если у вас русская локаль, а файл с индийскими буквами нужно открыть в программе, написанной 20 лет назад и об UTF-8 ничего не знающей.
Насколько мне известно, в Windows, как раз, проблема была более-менее решена, MSVC просто добавлял конструктор ofstream от wstring, а в GCC/Clang его не было.Я бы скорее сказал, что на Windows эта проблема была создана — и теперь успешно разрулилась. В некотором смысла. В MacOS и Linux проблемы никогда и не было, а на Windows расширение всегда было (хоть и небыло стандартом), так что, в общем, неясно — чего мы достигли.
А все стандартные библиотеки поддерживают UTF8 в, скажем, ofstream? Т.е. мне достаточно сделать буфер байтов который начинается с EF BB BF и все будет работать и в linux и в MacOS и в Windows?
А поскольку в MSVC есть ofstream(wstring), все пишут программы с его использованием, и ожидают что это будет работать в других системах. Это и есть проблема.Именно.
А все стандартные библиотеки поддерживают UTF8 в, скажем, ofstream?В корне неверная постановка вопроса. Не «стандартные библиотеки поддерживают UTF8», а «UTF8 поддерживает все стандартные библиотеки» — для того и сделан.
Т.е. мне достаточно сделать буфер байтов который начинается с EF BB BF и все будет работать и в linux и в MacOS и в Windows?Нет. EF BB BF — это ещё одна проблема, созданная Windows.
Для того, чтобы всё работало в MacOS, Windows, Android, ChromeOS — и вообще на подавляющем большинстве систем не нужно делать ни-че-го. Просто используйте функции, которые использовали в C 70х, а в C++ — с 80х — и всё.
А вот если вам нужен ещё и Windows… тогда у вас проблемы…
Резюмируя: да, теперь будет портабельный передачи юникод путя в ofstream, однако на мой взгляд лучше было бы если бы Windows string конструкторе принимала бы UTF8 а не локальную кодировку.
Интересно есть специальный смысл у разделителя, 0x2f "/". Когда он будет в любом байте, т.е. в Linux нельзя будет создать файл ∯ (0x22 0x2f) прочитал это здесь: unix.stackexchange.com/questions/39175/understanding-unix-file-name-encoding. Получается что используя utf8 строку как имя файла/путь можно получить не тот результат что ожидается.
В UTF-8 же символ ∯ кодируется как 0xe2 0x88 0xaf, и никаких проблем с его использованием в имени файла не возникает.
Ну хорошо, но ведь остается проблема что в open() можно отправить строку которая не является валидной UTF8 строкой и в это случае файл будет создан или открыт. Получается что программа которая читает список файлов должна быть готова к тому что имя какого-нибудь из файлов не будет валидной UTF8 строкой. Так?
Получается что программа которая читает список файлов должна быть готова к тому что имя какого-нибудь из файлов не будет валидной UTF8 строкой. Так?Да. Но совершенно точно так же в Windows программа должна быть готова к тому, что имя файла не будет валидной UTF-16 строкой.
В обоих случаях это — проблемы GUI, ядро такие «мелочи» не интересует…
Да, библиотеки для работы с TUI/GUI пришлось переделывать, так как правило «один байт = один символ на экране» перестало исполняться. Однако 99% кода — это не касается. Никакой ASCII vs UNICODE дихотомии, никаких «legacy» и «modern» программ, никаких изменений в ядре — ничего этого не нужно. Даже подстроки в регулярных выражениях можно в UTF-8 искать старыми функциями (если upcase/lowercase конвертация не нужна).
Несколько обидно видеть этот костыль в стандарте в 2020м году — примерно как раз когда можно будет уже наконец со спокойной душой заявлять «наше приложение работает на большинстве распространённых платформ… кроме Windows… и не планируем».
Вот в 2003м, когда реально было неясно чем попытка Microsoft'а «удавить всех» закончится — это было бы здорово. Но в 2020м?
Поскольку UTF-16 не решает ни одной проблемы, а только создаёт новые — использовать его в новых программах глупо, а так как писать новые программы под Windows всё менее нужно — то странно вносить его поддержку в язык в 2020м году.
> то странно вносить его поддержку в язык в 2020м году
я считаю странным. Во-первых поддержку utf16 все же включили в C++17 в конструктор ifstream/ofstream. Во-вторых все же подход в рамках идеологии С++ как я ее вижу был бы обеспечить там где возможно близость к платформе, а не игнорировать существующие реалии с высокой колокольни utf8.
Во-первых поддержку utf16 все же включили в C++17 в конструктор ifstream/ofstream.И, как мне кажется, это уже было ошибкой.
Во-вторых все же подход в рамках идеологии С++ как я ее вижу был бы обеспечить там где возможно близость к платформе, а не игнорировать существующие реалии с высокой колокольни utf8.Странно вы её видите. Не напомните, пожалуйста, в какой операционка функции fopen/fclose реализованы на уровне операционной системы? Или там malloc/free/operator new/operator delete?
Извините, но подход C/C++ — это максимальная переносимость, а никак не «максимальная близость к платформе». Вот если бы они научились ifstream/ofstream использовать WTF-8 на Windows — это был бы нормальный C/C++ подход…
Я бы с этим поспорил, откуда вы это взяли? С++ это один уровень выше поверх ассемблера в системе. Если я пишу под микроконтроллер я не ожидаю переносимость: программа не запустится на другом микроконтроллере, а С++ мне нужен чтобы не думать в ассемблерных инструкциях. Также, если я пишу для Windows мне лучше для путей использовать UTF16, поскольку это без дополнительных преобразований уходит в системные вызовы.
Если нужна максимальная переносимость, то это лучше Java (Write once, run anywhere, хехе) или другой язык где все реализовано через толстые и надежные абстракции.
Вот что как Страуструп определял основные идеи для C++:
• direct mappings of built-in operations and types to hardware to provide efficient memory use and efficient low-level operations, and
• affordable and flexible abstraction mechanisms to provide user-defined types with the same notational support, range of uses, and performance as built-in types.
Разработка переносимого кода в C++ это некоторое подмножество со своими особенностями. Задача разработчиков std:: как раз в том чтобы было больше прикладных задач можно было бы решить в рамках переносимого кода. То что стандартная библиотека позволяет использовать любые пути к файлам — это довольно неплохо на мой взгляд, разработчик при желании может выбрать вариант который обладает наименьшим оверхедом на его платформе, оставаясь в рамках переносимого кода.
Java (Write once, run anywhere, хехе)Именно что хе-хе. У меня сложилось впечатление, что написать реально переносимый код на Java не легче, чем на C++. Я не знаю примеров когда, которые легко написать переносимо на Java и трудно на C++.
Если вы не хотите вовлекать разработчика в процесс — то да, вам нужна виртуальная машина и запрет на использование API.
Если же вы хотите вовлечь разработчика в этот процесс, то у вас появляется CHARS в Форте (а вдруг у вас память нибблами адресуется как на HP 48?) и множество «неопределённых поведений», которых программист должен избегать.
Излишне и говорить что первый процесс порождает гораздо менее переносимые программы — если специально о переносимости не заботится, то и в Java её не будет (ваша программа может захотеть иметь два разных файла «Makefile» и «makefile» в одном каталоге и всё — прощай переносимость).
Я бы с этим поспорил, откуда вы это взяли? С++ это один уровень выше поверх ассемблера в системе.
c++ как язык преследует цель быть тонким слоем высокоуровневых абстракций. Он всегда задумывался быть переносимым и развивается он в направлении переносимого кода (thread/async/filesystem/net/coroutines)
Я бы с этим поспорил, откуда вы это взяли?Вам все источники перечислить? Или достаточно Страуструпа?
С++ был изначально создан на основе C потому что C был flexible, efficient, available и portable. И целью — было не потерять эти качества.
И переносимость красной нитью проходит через весь дизайн языка — снизу доверху.
Если я пишу под микроконтроллер я не ожидаю переносимость: программа не запустится на другом микроконтроллере, а С++ мне нужен чтобы не думать в ассемблерных инструкциях.Как я уже писал раньше: никто не может вам запретить забивать гвозди бензопилой — но вообще-то она не для этого предназанчена.
Огромное количество UB в C/C++, в частности — появились как раз для того, чтобы иметь возможность писать переносимые программы с минимальными потерями производительности.
Если нужна максимальная переносимость, то это лучше Java (Write once, run anywhere, хехе) или другой язык где все реализовано через толстые и надежные абстракции.На этом пути максимальной переносимости, увы и ах, не достичь. Ни Java, ни C# не работают на таком количестве платформ как C/C++ — и это не случайность. Лозунг write once, run anywhere — несомнно хорош, но он возлагает задачу обеспечения переносимости на рантайм, а не на программиста. Результат — ни на микроконтроллерах, ни на суперкомпьютерах Java не испольщуется… а C/C++ — вполне.
Также, если я пишу для Windows мне лучше для путей использовать UTF16, поскольку это без дополнительных преобразований уходит в системные вызовы.Однако это — непереносимо и именно поэтому достаточно грустно видеть эту фичу в C++17. Впрочем это
То что стандартная библиотека позволяет использовать любые пути к файлам — это довольно неплохо на мой взгляд, разработчик при желании может выбрать вариант который обладает наименьшим оверхедом на его платформе, оставаясь в рамках переносимого кода.Посмотрим. Я пока довольно скептически к этому отношусь: всё это немного напоминает историю с fopen, где стандартное поведение было призвано нейтрализовать различие в разделителях строки (\r\n vs \n), но на практике в 99% случаев используется спецификатор «b», потому что альтернатива — это странные глюки…
Теперь у нас Страуструп спорят со Страуструпом. По вашей ссылке я не увидел что С++ ставит переносимость как наивысший приоритет. Когда переносимость делают наивысшим приоритетом в ущебр производительности и доступности, то получится что-то другое. Тогда и получается что-то вроде «UTF8 — самая правильная кодировка, наш язык поддерживает только ее».
С/С++ появляется в системе как системный язык, на котором пишется ядро/драйверы. Именно по этой причине он наиболее доступный среди других языков на большем числе платформ. Если бы С/С++ не позволял работать очень близко к железу и платформе, никто бы не выбрал его как системный язык и нельзя было бы хвастаться что С++ доступен больше чем Java. Переносимость С++ в реальном мире — это больше похоже на некоторый хак, как писать код так чтобы он работал на требуемых платформах.
При использовании только стандартного подмножества с++ программа будет работать одинаково на всех платформах (с поправкой на поддерживаемый функционал, типа std::thread на однопоточных МК). И столько проблем со стандартизацией с++ как раз таки от того, что вся переносимость новшеств должна достигаться не в ущерб производительности.
Вот что действительно является хаком — так это использование сотен разных системных кодировок в win.
При использовании только стандартного подмножества с++ программа будет работать одинаково на всех платформах
В реальности так может быть у mainstream платформ, для многих других будут многочисленные уровни совместимости основанные на макросах или билд-скриптах, собственных реализаций, и других методах (вспомните например ./configure и autotools).
В целом то что вы написали — это примерно то же что и я пытаюсь сказать: «что вся переносимость новшеств должна достигаться не в ущерб производительность», т.е. переносимость тут не наивысший приоритет.Спорить о том что есть высший приоритет, а что низший можно до бесконечности. Важно, что пока нет быстрого и переносимого решания — фича в C++ не появляется, а переностися на следующий релиз.
Microsoft со своими co_await/co_yield на это с размаху вьехал — потому его и послали в C++20 (и не факт что там примут — будет зависеть от того, насколько эффективной реализация на разных платформах окажется).
вспомните например ./configure и autotoolsВспоминаю. Как страшный сон. Слава богу поддержка «кривых» платформ, не поддерживающих стандарты, сегодня не актуальна (за исключением embedded, но и там потихоньку «дело движется»)
std::ofstream(L"Теперь можно открывать файлы с юникодными именами.txt") << u8"Ура!";
это lambda v2.0 (лямбда + 3 служебных метода + компилятор преобразует тело функции в конечный автомат
А можно это как-то проиллюстрировать, или есть развернутое объяснение?
Вполне возможно что поддержку stackful во временем добавят в стандартную библиотеку.
Интересно, можно ли будет stackless корутину сериализовать?
Кроме перечисленных в табличке rotl, rotr, popcount, countl_zero, countl_one, countr_zero и
countr_one, есть еще такая операция как «bit reverse» — разворот бит в слове (то есть первый бит становится последним, второй — предпоследним и т.д.). В x86 ее нет, но в ARM есть операция RBIT.
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489h/Cihjgdid.html
Там же есть REV, REV16 и REVSH, обращающие байты в словах (это для перехода между Big/Little endian, кстати не знаю есть ли в стандарте такое… на ум приходит только htonl, htons, ntohl и ntohs, но насколько они стандартны?). Если делать то делайте сразу все.
Еще для битовых вращений было бы прикольно задействовать <<< и >>>, но это уже украшательства.
А где он будет сохраняться? Если программа записала что-то в регистры процессора и новая сопрограмма туда тоже что-то хочет записать чтобы выполнить вычисление, то как все это будет работать?
В случае сопрограмм через co_await — компилятор знает про co_await и не оставляет ничего в регистрах процессора при переключении.
У co_await есть более сильное ограничение — у таких сопрограмм нет своего стека, свои локальные переменные они хранят в куче.
Переключение контекста у co_await не такое затратное как у stackfull-версий потому что компилятор знает о нем и не держит ничего в этот момент в регистрах, а потому нет необходимости эти самые регистры сохранять. Кроме того, под локальные переменные нужно намного меньше места чем под еще один стек.
У stackfull-сопрограмм переключение контекста не такое затратное как у системных потоков потому что они остаются в режиме пользователя, обходясь без перехода в режим ядра. Также возможность управлять переключением контекста когда-то давала возможность серьезно экономить на синхронизации потоков. Сейчас эта возможность уже не так актуальна из-за широкого распространения многоядерных процессоров, но ее все еще можно использовать в некоторых случаях.
Тогда вместо асинхронного нечитабельного кода на +100 строк можно получить то же самое, но на 40 строк
Уверен, что 100 строк C-ного кода будут понятее, чем приведённый пример…
Т.е. претензия именно к тому, что язык всё сложнее и сложнее понять без специальной подготовки…
Оператор co_await в первом приближении можно рассматривать как оператор который "распаковывает" future: future<T>
превращается в T
.
Человеку, который не знаком с такими конструкциями, очень тяжело по коду понять, что он делает. У меня большой опыт программирования на разных языках, в том числе и на C/C++, но я долго втыкал в код, чтобы понять, что он делает…
Простите, но это уже ваша проблема, а не проблема языка. Асинхронное программирование используется уже в большом наборе мейнстримных языков: C#, Python, JavaScript. Теперь и в С++ завезли.
Если вам требуются усилия для его понимания — поздравляю, ваши знания немного устарели. Просто потратьте немного времени на самообучение.
Я на C/C++ программирую с 1997 года, т.е. уже около 20 лет… И за 25 лет опыта программирования вообще, программировал на десятке-другом разных языков… Так что не «пробовал китайский», я очень неплохо знаю китайский, но некоторые его диалекты вымораживают меня…
Так я тоже по аналогии проакцентировал на своем мнении
Не похоже: «я уверен что китайский язык сложный».
Опыт в данном случае никакой роли не играет, но я бы на вашем месте вычеркнул у себя из списка С++, вы очевидно его вообще не понимаете
Доказательства? Возможно для вас это так, мне же знание разных языков помогает. Например, когда-то я программировал на прологе, что помогло в понимании эрланга и хаскеля.
Вы переходите на личности и судите меня не будучи со мной даже знакомы…
Возможно вы им пользуетесь как С, поэтому логика применения колбеков что С что С++ для вас легче.
Ну да, в статье идёт речь о будущем стандарте, в текущем описанное пока не поддерживается…
К чему тогда ваше личное мнение к тому что вам трудно понимать современный С++? Какие пути решения этой ситуации вы предлагаете?
Вам, любителю аналогий: «Погода хреновая. К чему это ваше личное мнение? Какие пути решения этой ситуации вы предлагаете?»…
Я предлагаю другое решение, вы возьмете книжку по С++ и освоите те моменты которые вам не понятны.
Умерь пыл, мальчик…
Языки забываешь, но концепции где-то сидят в голове и всплывают, когда видишь что-то подобное… Помниться пролог мне взорвал мозг не меньше ассемблера :)
В вашем же случае идет голословное утверждение.
Вот обработка соедниений в nginx: github.com/nginx/nginx/blob/master/src/core/ngx_connection.c, куча понятного кода…
Вы понимаете разницу между мнением и утверждением?Я не понимаю зачем нужно высказывать мнение, если даже автор не может его подтвердить.
Вот обработка соедниений в nginx:Ну а пример из статьи с этой самой красотой как будет выглядеть? И вообще-то такие потроха нужно сравнивать с потрохами Asio, а не с кодом, который Asio использует.
Я не понимаю зачем нужно высказывать мнение, если даже автор не может его подтвердить.
Давайте аналогию проведу. Я могу выссказать мнение «кофе — обалденный напиток», а вы его не любите и напишите «кофе горький и невкусный». Мы оба выссказали мнение об одном и том же, в обеих случаях это мнение, которое не требует доказательств, это наше личное восприятие… Как можно подтвердить, что один прав, а другой нет? Точно так же и здесь я выссказал своё мнение…
Но давайте попробуем на аналогии: в статье говорится, что варить кофе эспрессо быстрее и удобнее, чем варить в турке. И приводятся примеры и того, и другого. А потом приходите вы и говорите: «что-то я не понимаю, как варить кофе эспрессо, по-моему, если заваривать кофе в чайничке, то будет еще проще и быстрее». И когда вас просят продемонстрировать, как вы будете это делать, вы говорите что «это мое мнение, а вообще вон за углом в знаменитой кофейне так варят и нормально получается».
Это не верная аналогия. Да и вообще, сам факт того, что приходится прибегать к аналогиям уже показателен.
Почему неверная? Зачем вы выссказываете мнение, если не можете его подтвердить?
Но давайте попробуем на аналогии: в статье говорится, что варить кофе эспрессо быстрее и удобнее, чем варить в турке. И приводятся примеры и того, и другого. А потом приходите вы и говорите: «что-то я не понимаю, как варить кофе эспрессо, по-моему, если заваривать кофе в чайничке, то будет еще проще и быстрее». И когда вас просят продемонстрировать, как вы будете это делать, вы говорите что «это мое мнение, а вообще вон за углом в знаменитой кофейне так варят и нормально получается».
Эспрессо — это сорт кофе, а не способ приготовления… )))
Предлагаю заканчивать этот диспут. Я не хочу доказывать, что мнение не требует доказательств, мне это кажется очевидным…
Эспрессо — это сорт кофе, а не способ приготовления…А эспрессо-машины придумали для приготовления кофе только этого сорта.
Я не хочу доказывать, что мнение не требует доказательств, мне это кажется очевидным…Очевидным является то, что бездоказательное мнение не имеет никакого веса и смысла его высказывать не было.
Я привык к ровно противоположному определению: очевидно, это не то, что невозможно доказать, очевидно — это то, что тривиально можно доказать.
Действительно, я был неправ…
Очевидным является то, что бездоказательное мнение не имеет никакого веса и смысла его высказывать не было.
Очевидным? Где доказательства? Без доказательства вы сами себе противоречите…
Очевидным? Где доказательства? Без доказательства вы сами себе противоречите…Ну если у вас с логикой всё настолько плохо…
Споры на Хабре ведутся не с целью потратить ресурсы для пересылки бессмысленных сообщений туда-сюда, а для того, чтобы дать шанс разным людям выработать некоторое общее мнение. Классическое «в споре рождается истина». Приведение каких-то доводов и аргументов позволяет оппоненту на них как-то отреагировать и парировать, либо изменить свою точку зрения.
Приведение же ничем не обоснованного мнения не позволяет сделать ни того, ни другого: как-то маркировать «довод» «это ни из каких разумных посылок не следует, но я „попом чую“ — тут что-то не так» невозможно — откуда оппоненту знать, насколько у вас качественная попа? Соответственно конструктивная дискуссия после этого «довода» кончается и начинается бред.
Эспрессо — это сорт кофе, а не способ приготовления… )))Ну то есть о кофе у вас примерно столько же знаний, сколько о C++. Хоть бы Википедию открыли, блин: Эспре́ссо (от итал. espresso[1] ) — метод приготовления кофе путём прохождения горячей воды (около 90 °C) под давлением 8-10 бар через фильтр с молотым кофе
Я не хочу доказывать, что мнение не требует доказательств, мне это кажется очевидным…Я привык к ровно противоположному определению: очевидно, это не то, что невозможно доказать, очевидно — это то, что тривиально можно доказать.
Какой смысл высказывать мнение, которое вы ничем не можете обосновать? Ничего, кроме бесполезной траты времени дискуссия без обоснований, увы, породить не может…
Очевидным является то, что бездоказательное мнение не имеет никакого веса и смысла его высказывать не было.
Вот вам, любителю википедии: ru.wikipedia.org/wiki/%D0%9C%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5
то есть у Вас есть мнение, но нет аргументов? Так и пишите тогда: «вот моё мнение, основанное чисто на эмоциях».
Т.е. вы всегда, когда не предоставляете аргументы, добавляете «основанное чисто на эмоциях», так? Интересный вы человек ))))
Как вы определяете, что место для выссказывания мнения является подходящим?
Как вы определяете, до выссказывания мнения, что время будет потеряно?
Зачем вы со мной переписываетесь, ведь мы же, объективно, теряем время?
Предлагаю закончить диспут, а то мы тут увязнем до конца дня, а мне ещё надо поработать на C/C++. Мне было приятно с вами пообщаться ;)
Как вы определяете, что ваших аргументов будет достаточноЭто субъективно. Если мне кажется, что аргументы весомы можно их высказать, но если мне и самому понятно, что они недостаточно убедительны — возможно лучше воздержаться.
В том числе, ничего криминального нет в стиле использования C++ как «C с классами ООП, использованием стандартных библиотек и вставок на ассемблере». И это может выглядеть и работать прекрасно в опытных руках.
Интересоваться новинками языка и разобраться в них определённо следует, но кидаться их использовать на следующий день после официального выхода весьма опрометчивое ребячество. Имхо.
Что касается развиваться и экспериментировать — это можно только поддерживать. Но есть и разная прикладная реальность.
Почему неверная? Зачем вы выссказываете мнение, если не можете его подтвердить?Доказывать уместность аналогии должен тот, кто её привёл, а не наоборот. Если это затруднительно — нужно отказаться от аналогии.
Доказывать уместность аналогии должен тот, кто её привёл, а не наоборот. Если это затруднительно — нужно отказаться от аналогии.
Давайте подиспутируем… Вы будете что-то доказывать, а я вам, в качестве обратной связи буду говорить одно слово «Ложь». Вы будете продолжать доказывать или плюнете? Вот и тут на мою аналогию ответили только «Неверная», не написав почему…
Вы будете что-то доказывать, а я вам, в качестве обратной связи буду говорить одно слово «Ложь»Если я приведу аргументы, а Вы ответите просто «ложь», то в глазах рационального наблюдателя, моя позиция выиграет в диспуте.
Вот и тут на мою аналогию ответили только «Неверная», не написав почемуАналогия — это не аргумент. Аналогия может стать аргументом только в том случае, если обе стороны признают аналогию справедливой. Если оппонент не согласен с тем, что аналогия уместна — от аналогии необходимо либо отказаться, либо сперва убедить оппонента в её уместности. Это сложно, так как аналогию вынужден защищать тот, кто её привёл.
Если я приведу аргументы, а Вы ответите просто «ложь», то в глазах рационального наблюдателя, моя позиция выиграет в диспуте.
Как можно в диспуте «Погода хорошая» и «Погода плохая» выиграть? Можно только сказать, собрав статистику, что такой-то процент считает погоду хорошей, а такой-то плохой, но истины тут быть не может, потому как это мнение, а не факт.
Аналогия — это не аргумент. Аналогия может стать аргументом только в том случае, если обе стороны признают аналогию справедливой. Если оппонент не согласен с тем, что аналогия уместна — от аналогии необходимо либо отказаться, либо сперва убедить оппонента в её уместности. Это сложно, так как аналогию вынужден защищать тот, кто её привёл.
Согласен, аналогия — это попытка донести информацию иносказательно. Если собеседник выражает несогласие одним лишь «несогласен», как можно вести какой-либо диспут? Я вам об этом и написал, если я каждый раз буду говорить «ложь», диспут зайдёт в тупик…
Как можно в диспуте «Погода хорошая» и «Погода плохая» выиграть?Вообще можно, если вы на форуме метеорологов. По сути же, если бы Вы написали, что Ваше мнение о корутинах построено чисто на интуиции — его бы проигнорировали. Максимум можно было бы по голосам за и против к этому комментарию судить о том, сколько людей разделяют вашу оценку.
Если собеседник выражает несогласие одним лишь «несогласен», как можно вести какой-либо диспут?Если несогласие направлено на аналогию, то диспут нужно продолжать отказавшись от аналогии или привести исчерпывающее доказательство её уместности. Это такая особенность аргументов по аналогии.
Вообще можно, если вы на форуме метеорологов. По сути же, если бы Вы написали, что Ваше мнение о корутинах построено чисто на интуиции — его бы проигнорировали. Максимум можно было бы по голосам за и против к этому комментарию судить о том, сколько людей разделяют вашу оценку.
Идёт дождь. Мне нужен дождь, чтобы земля увлажнилась, чтобы картошка лучше росла, я утверждаю «погода прекрасная». Вы хотели позагорать на солнышке и утверждаете «погода отвратительная». Кто прав? Мнения противоположные на одно и то же.
По сути же, если бы Вы написали, что Ваше мнение о корутинах построено чисто на интуиции — его бы проигнорировали. Максимум можно было бы по голосам за и против к этому комментарию судить о том, сколько людей разделяют вашу оценку.
Моё мнение было о читабельности современного C++ кода. К корутинам вообще отношусь прекрасно, очень интересное решение. И не думаю, что проигнорировали бы, выссказывание было весьма холиварное и молодые любители C++ наверняка его не проигнорировали бы.
Вот обработка соедниений в nginx: github.com/nginx/nginx/blob/master/src/core/ngx_connection.c, куча понятного кода…
У вас куда-то пропала частица "не".
В приведенном вами файле:
- куча директив условной компиляции;
- местами используется вложенность управляющих конструкций в 4 и более слоев;
- несколько функций не влезают в экран по высоте.
Сами по себе эти проблемы ничего плохого не означают — но вот вместе они приводят к тому, что в этом коде очень тяжело разобраться случайному прохожему.
- Да, но как иначе вы определите, можете вы использовать некоторую функцию или нет? Сушествует ли опция сокета в этой версии ядра или нет? В плюсах точно так же придётся использовать директивы.
- Разве в C++ как-то избавились от этого? С++ в чём-то тут отличается от C?
- И? В С++ такого не бывает?
Это не проблемы языка, это особенности стиля программирования конкретного человека и C++ от этого тоже не застрахован.
Субъективно, не тяжело, а очень просто. Когда я писал свой первый большой проект на C, лет 10 назад, nginx был моим reference manual по сетевому программированию, код Игоря очень легко читается.
Да, я знаю про вкус, цвет и фломастеры. Тем не менее, мне очень хочется знать, каким образом вы определяете какой именно блок закрывает вот эта скобка:
Вот в том-то и проблема, что такой код нельзя прочесть с конца.
Более аккуратный код хорошо читается в любом направлении.
Одно условие — несколько строк, один вызов функции — несколько строк. Логично же, что для улучшения читаемости они должны быть разделены пустыми строками.
Ну и вертикально ориентированный монитор для чтения такого кода будет очень удобен.
Уверен, что 100 строк C-ного кода будут понятее, чем приведённый пример…
На си это будет не 100 строк, а за 400. Их и читать будет сложнее, просто в силу объема, и количество ошибок будет квадратнопропорционально выше
вы сравниваете код на си, который не развивается, с кодом на с++, который развивается. Ваши знания с++ устаревают, и виноваты в этом вы.
Вы оправдываете читабельнность кода C++ его развитием? Развитие, в моём понимании, должно улучшать, а не ухудшать читабельность кода… Может C и так хорош, потому и не надо его куда-либо дальше развивать.
Вот go динамично развивается, но у меня нет проблем с пониманием кода. Т.е. я не читал reference, но я понимаю чужой код на go. Вот это для меня признак хорошего языка.
На си это будет не 100 строк, а за 400. Их и читать будет сложнее, просто в силу объема, и количество ошибок будет квадратнопропорционально выше
Я так понимаю, у вас богатый опыт использования C и C++, коль вы так уверенно утверждаете без доказательств, которые тут от меня, чуть ли не в каждом комментарии требуют?
Вот набросал примерно то же самое на C, обработка HTTP-запросов:
#include <ev.h>
#include "cor_http.h"
void
cor_http_server_cb(cor_http_request_t *r, void *arg)
{
cor_http_response_t res;
cor_http_response_init(&res, r);
cor_http_response_set_code(&res, 200);
cor_http_response_set_body(&res, "answer", 6);
cor_http_response_send(&res);
}
int main() {
struct ev_loop *loop = EV_DEFAULT;
cor_http_t *http = cor_http_new(loop, "127.0.0.1", 8000, NULL, NULL);
if (!http) {
return 1;
}
if (cor_http_start(http, cor_http_server_cb, http) != cor_ok) {
cor_http_delete(http);
return 1;
}
ev_run(loop, 0);
cor_http_delete(http);
}
Что тут сложного? Где тут 400 строк и «квадратнопропорционально выше», простите, не понял, что вы этим хотели сказать…
Но здесь приведен тривиальный алгоритм, веселье начинается когда нужно, к примеру, в процессе обработки http-запроса сделать запрос к СУБД. Или, еще веселее, пачку запросов в цикле (да, я знаю, делать запросы к СУБД в цикле — дурной тон, но иногда надо).
И когда вам надо написать быстрое приложение, вы начинаете отказываться от STL, лямбд и другого сахара…Н-да… Поинтересуйтесь что говорят люди, которым приходится писать быстрые приложения на C++.
Ну правда, сколько уже можно эти байки из начала 90-х годов распространять?
Про какие вы байки?
Байки про отказ от STL и лямбд. Там подробно рассказывается как STL и лямбды замечательно работают в задачах HFT.
А что там говорят? По большей части банальные вещиТ.е. вы не просмотрели слайдов доклада, но говорите про «банальные вещи, применимые к языку программирования»? В том числе и про использование C++ных шаблонов для получения быстрого кода?
Про какие вы байки?Про то, что необходимо отказываться от STL для получения быстрого кода. Про лямбды — это вообще шедеврально.
Вы, часом, лямбды с std::function не путаете?
Т.е. вы не просмотрели слайдов доклада, но говорите про «банальные вещи, применимые к языку программирования»? В том числе и про использование C++ных шаблонов для получения быстрого кода?
Вы у меня за спиной стояли и смотрели, что я делал? Зачем вы мне приписывает то, чего не знаете? Я просмотрел слайды. Но я давно программирую на C и увлекаюсь низкоуровневой оптимизацией, в том числе и на уровне ассемблера, потому для меня то, что там написано — это банально, это самое начало…
Про то, что необходимо отказываться от STL для получения быстрого кода. Про лямбды — это вообще шедеврально.
Вы меня верно поняли, я про STL писал, а не про шаблоны…
Зачем вы мне приписывает то, чего не знаете?Я не приписываю, я интересуюсь. У меня вопросики стоят в надежде, что вы проясните ситуацию.
Вы меня верно поняли, я про STL писалSTL уже перестал быть Standard Template Library? Как давно?
Я не приписываю, я интересуюсь. У меня вопросики стоят в надежде, что вы проясните ситуацию.
Ну значит вы неточно выразились, потому как в вашем предложении «Т.е. вы не просмотрели слайдов доклада» является утверждением, после чего идят вопрос…
STL — это контейнеры, итераторы, алгоритмы и т.п., реализованные посредством шаблонов. Т.е. я могу использовать шаблоны, но не использовать элементы STL, скажем, std::vector, std::sort, std::map и т.п.
Обратите внимание: там на одном из слайдов используется класс std::unique_ptr<>
. Этот класс, вообще-то, входит в STL.
Значит, даже в HFT от STL не отказываются.
"Don't be afraid to use exceptions." И дальше можно не читать. Именно из-за exceptions не возможно нормально использовать STL при разработке дров и проч. И везде нужно писать свой "очень нужный"vector/map и проч или еще хуже писать на C и обмазываться макросней.
Отказ от STL не означает отказа от шаблонов (template). Отказ от лямбд и вовсе не имеет смысла — они при правильном приготовлении столь же быстрые как и обычные функции.
И почему вы противопоставляете языковую поддержку и творчество?
Не противопоставляю, я говорю о том, что язык не сделает за вас красиво, какой бы сахар там не был…
Шаблоны — это сахар. STL — это конкретная библиотека вокруг языкового сахара.
Почему про шаблоны вы говорите что с удовольствием бы использовали их в Си — а про co_await говорите что оно непонятное, сложное и языковой поддержки на все случаи не хватит? co_await — это такой же сахар как и шаблоны.
Я понял, что такое co_await и я примерно понимаю, как это должно быть устроено внутри. Но когда читал код, мне нужно было время, чтобы понять, что этот код делает, а это, субъективно, характеризует сложность языка…
Не знаю как конкретно реализован co_await, но подозреваю, что сохраняются все локальны переменные, идёт выход из функции, а потом возврат в функцию с восстановлением всех переменных. А это может создавать реальный оверхед…
А это может создавать реальный оверхедИ все же, корутины — это negative overhead abstraction. Они как минимум не хуже, чем все, что можно реализовать руками, а иногда лучше.
Локальные переменные сразу создаются в динамической памяти. Тут оверхед будет скорее от того что один из регистров окажется постоянно занят под указатель на блок этих переменных.
С другой стороны, посмотрите какая красота получается: любая реализация FSM так или иначе сводится или к виртуальным вызовам, или к большому switch. А тут компилятор может себе позволить использовать в качестве состояния непосредственно указатель на ту инструкцию с которой следует продолжать!
Руками такое можно реализовать только выключив в компиляторе всю оптимизацию.
Локальные переменные сразу создаются в динамической памяти. Тут оверхед будет скорее от того что один из регистров окажется постоянно занят под указатель на блок этих переменных.
Т.е. потенциально это будет медленнее, чем выделение памяти на стеке…
С другой стороны, посмотрите какая красота получается: любая реализация FSM так или иначе сводится или к виртуальным вызовам, или к большому switch. А тут компилятор может себе позволить использовать в качестве состояния непосредственно указатель на ту инструкцию с которой следует продолжать!
Да, красиво, но на сколько это будет эффективно… Надо мерять…
Т.е. потенциально это будет медленнее, чем выделение памяти на стеке…
Если сравнивать с синхронным кодом — то да. Но если ваша задача требует асинхронность — то блок в динамической памяти у вас появится в любом случае, надо же где-то хранить данные.
Тем более что там можно свой аллокатор добавить.
Т.е. потенциально это будет медленнее, чем выделение памяти на стеке…
Нет. Компилятору разрешено оптимизировать это место и удалять динамическую аллокацию в пользу аллокации на стеке, даже если подставлен пользовательский аллокатор.
Другими словами — если компилятор вставит вызов аллокатора, то написать без динамической аллокации руками у вас не получится, без серьёзной переработки логики приложения. См изначальный пример с Boost.Asio. Там есть «new tcp_connection», так вот, этот new в примере с корутинами фактически «переехал» внутрь имплементации корутины.
когда вам надо написать быстрое приложение, вы начинаете отказываться от STL, лямбд и другого сахараКонкретно корутины — это negative overhead abstraction
И когда вам надо написать быстрое приложение, вы начинаете отказываться от STL, лямбд и другого сахара
Ох лол, работаю с графикой (рендеры, шейдеры, генераторы волос, рейтрейсинг и прочее для 3D мультфильмов) с появлением лямбд производительность, и скорость написания кода очень сильно выросла, а главное код стало намного легче читать. А stl как использовался так и используется ещё ни разу stl не была причиной проседания производительности, а с приходом мува код стало ещё проще писать без потерь в производительности. Вы ничего не знаете про использование новых стандартов, они сильно повышают читабильность и скорость написания кода, при этом ещё умудряются повысить производительность. Вы либо не удосужились потратить время и разобраться либо вы слепо боготворите С
#include <iostream>
#include <restinio/all.hpp>
int main()
{
restinio::http_server_t<> http_server{
restinio::create_child_io_context(1),
[](auto & settings) {
settings.port(8080).address("localhost")
.request_handler([](auto req) {
req->create_response().set_body("answer").done();
return restinio::request_accepted();
});
}};
http_server.open();
std::cin.ignore();
http_server.close();
return 0;
}
Как-то даже на таком тривиальном примере видно, что C-шный код внимания к себе требует побольше.
Вот у вас, например, недостаточно просто объявить cor_http_reponse_t и затем вызвать cor_http_response_set_body. Нужно еще предварительно вызвать cor_http_response_init.
Нельзя просто после ev_run сделать return, нужно еще предварительно вызывать cor_http_delete.
Нужно объявлять переменную loop со специальным значением.
Это все детали, которые легко упустить. А потом искать проблемы. Именно поэтому «простой» код на C получается объемнее и требует к себе большего внимания.
Именно поэтому вас и просили написать таки те самые 100 строк кода на C. Дабы вы сами в этом смогли убедиться.
Переменную loop можно внести и внутрь сервера, просто я взял кусок из проекта и запостил сюда, один loop используется во множестве мест, а не только в http-сервере, в C++ так же пришлось бы его выносить в отдельную переменную…
Разве в C++ меньше вероятность что-то упустить? Мне кажется, в плюсах гораздо больше способов прострелить себе ногу )))
Но нет большой разницы между cor_http_reponse_t *res = cor_http_reponse_new(); и CorHTTPRespose res; Просто конструктор вызывается явно.Вообще-то разница прям таки принципиальная. И заключается она не столько в конструкторах, сколько в деструкторах.
То, что вам не надо заморачиваться удаление данных?Да.
По моему опыту, в этом нет ни каких проблем…Ну что уж тут поделать. У вас нет. А вот у огромного количества разработчиков есть.
Ну что уж тут поделать. У вас нет. А вот у огромного количества разработчиков есть.
Видать я избранный, счастливчик )))))))
Да, лямбду я бы оформил в виде отдельной функции. Принципы SOLID и всё такое.
std::sort(std::begin(cnt), std::end(cnt),
[](const auto & a, const auto & b) {
return std::tie(a.a, a.b, a.c) < std::tie(b.a, b.b, b.c);
});
вы бы лямбду в отдельную функцию вынесли бы? Ну SOLID и все такое…В приведённом примере ошибиться очень легко, просто перепутав имена полей. Но приведённый пример легко протестировать, не вынося лямбду в отдельную функцию: она легко проверяется сортировкой массива из 2 элементов.
А вот протестировать веб-сервер с лямбдой будет проблематично.
std::sort(std::begin(cnt), std::end(cnt),
[](const auto & a, const auto & b) {
const auto tie = [](const auto & x) { return std::tie(x.a, x.b, x.c); };
return tie(a) < tie(b);
});
Ну а сам предикат для sort-а выносить в отдельную функцию имеет смысл если он повторяется в коде несколько раз (если два раза, то это уже сигнал к рефакторингу). Но тогда еще нужно посмотреть, может в отдельную функцию следует вынести не один предикат, а весь sort с этим предикатом.
Однако, поинт вообще был в другом.
Выигрыш ли это? А вот не факт. Если у вас вызовов std::sort с кастомными компараторами множество, то дополнительный comparingBy может себя оправдать. Если же std::sort вызывается всего один раз, то делать еще и comparingBy — это уже поклонение паттернам вместо здравого смысла.
Ну и повторюсь, поинт был вообще не в том. Но, видимо, это непонятно, поэтому начинается соревнование в утонченности на ровном месте.
Терпеть не могу K&R стиль расстановки скобок, уж простите. С ним тяжело глазами выцеплять логически разные блоки. Вот так мне сильно более приятно читать код:
#include <iostream>
#include <restinio/all.hpp>
int main()
{
restinio::http_server_t<> http_server
{
restinio::create_child_io_context(1),
[](auto & settings)
{
settings
.port(8080)
.address("localhost")
.request_handler([](auto req)
{
req->create_response().set_body("answer").done();
return restinio::request_accepted();
});
}
};
http_server.open();
std::cin.ignore();
http_server.close();
return 0;
}
Тем не менее, это лишь доказывает мою точку зрения:
На си это будет не 100 строк, а за 400. Их и читать будет сложнее, просто в силу объема, и количество ошибок будет квадратнопропорционально выше
Объем приведенного вами кода мал. А вот объем кода библиотеки, которую вы использовали, переваливает за 1000 строк (и это без заголовочника). В то время как в статье речь идет о функционале «из коробки». А внешняя зависимость влечет усложнение сборки/деплоя и усложняет проверку на корректность
Объем приведенного вами кода мал. А вот объем кода библиотеки, которую вы использовали, переваливает за 1000 строк (и это без заголовочника). В то время как в статье речь идет о функционале «из коробки». А внешняя зависимость влечет усложнение сборки/деплоя и усложняет проверку на корректность
В обеих случаях используются библиотеки. Практически в любом реальном проекте, за редким исключением, используются подключаемые библиотеки, редко язык может покрыть все потребности, а покрыть качественно, тем паче…
В C++ давным давно есть std:map, но существует десятки алтернатив, которые придётся подключать, если вам надо, чтобы код работал быстро…
В серьёзном проекте вы не обойдётесь без внешних зависимостей, так что аргумент весьма сомнительный… Да, nginx без внешних зависимостей, но единственный пример, который я знаю, Сысоев маньяк, в хорошом смысле этого слова ))
const auto log_and_return_null = [&](const char * msg) {
cor_log_error(log, msg);
return nullptr;
};
...
if(!ctx)
return log_error_and_return_null("can't malloc");
...
if(!ctx->buf_pool)
return log_error_and_return_null("can't cor_buf_pool_new");
...
if(!ctx->requests)
return log_error_and_return_null("can't cor_list_new");
...
И писать нужно меньше, и вероятность допустить ошибку ниже, и даже думать больше не нужно. Нужно только перестать смотреть на C++ как на «C с чем-то».
По поводу log_error_and_return_null, можно точно так же написать функцию на C, но выгода от этого сомнительна, в том же исходнике всего 5 мест, где используется вывод ошибки и возврат NULL. А если вам нужно warn или info вывалить, писать для них функции? А то, что cor_log_error работает с переменным набором параметров означает, что во всех этих обёртках придётся его поддерживать… Весьма сомнительно, что будет реальный выигрыш… Плюс программисту надо помнить, что у него есть набор подобных «обёрток»…
И вот такую «простоту» вы пытаетесь противопоставить растущей сложности C++.
Не язык делает код трудночитаемым, а программист…
Простота кода зависит не от языка, а от программиста.Программы на brainfuck-е смотрят на вас с недоумением.
Тот же, неоднократно упоминаемы мною nginx, прекрасно читается.Это может быть следствием профдеформации.
С++ позволяет стоить что угодно, хоть нормальный код, хоть ненормальный. Тогда как на C вы вынуждены педалить простыни однотипного кода, повторяя одно и тоже снова и снова. И оставляя контроль за всем (например, за временами жизни и очистку ресурсов) исключительно на разработчике.
Так что C++ определенно не самый удобный язык программирования, но противопоставлять ему C, на котором облегчить себе работу гораздо сложнее… Ну пусть каждый выбирает для себя.
Программы на brainfuck-е смотрят на вас с недоумением.
Вы используете brainfuck в работе?
Это может быть следствием профдеформации.
Можете пояснить, что конкретно вы имеете в виду?
С++ позволяет стоить что угодно, хоть нормальный код, хоть ненормальный. Тогда как на C вы вынуждены педалить простыни однотипного кода
У васт большой опыт написания программ на C, да? В моём коде вы предложили сократитить 2 повтора по 2 строки лямбдой в 5 строк и плюс по одной стоке на каждый повтор вместо двух. Ваш код стал менее читаемым, длинее и дольше в написании… Вы повторяете стереотипы по поводу C, которые к реальности не имеют ни какого отношения…
Ну пусть каждый выбирает для себя.
Вот именно. Я изначально выссказал своё мнение, своё отношение и неоднократно акцентировал на этом внимание…
Вы используете brainfuck в работе?Для демонстрации ошибочности вашего утверждения этого не требуется. Ну если хотите примеров того, что люди используют в работе, то посмотрите на какой-нибудь Q или J.
Можете пояснить, что конкретно вы имеете в виду?Если вы вынуждены плотно заниматься сетевым программированием на чистом C и привыкли это делать так, как разработчики nginx, то у вас может просто «глаз замылиться». Ну и парадокс Блаб-программиста так же может иметь место быть.
У васт большой опыт написания программ на C, да?Возможно, опыт написания программ у меня просто больше, чем у вас. Если вы говорили про 1997-ой год, то точно больше. Ну и C был одним из тех языков, с которых приходилось начинать. И ушел я с него при первой же возможности, т.к. даже в начале 90-х было понятно, что использовать C по собственной воле могут не только лишь все.
В моём коде вы предложили сократитить 2 повтора по 2 строки лямбдой в 5 строк и плюс по одной стоке на каждый повтор вместо двух.Вообще-то три повтора. В двух из которых еще и нужно было функцию очистки явным образом вызывать. И если вы в лямбде насчитали пять строк, то у вас еще и проблемы со зрением.
Ваш код стал менее читаемым, длинее и дольше в написании…Ну тогда понятно. Вопросов больше не имею.
Для демонстрации ошибочности вашего утверждения этого не требуется. Ну если хотите примеров того, что люди используют в работе, то посмотрите на какой-нибудь Q или J.
В чём оно ошибочное? Сложность синтаксиса языка не отрицает того, что сложная для понимания программа или простая — это результат работы программиста.
Если вы вынуждены плотно заниматься сетевым программированием на чистом C и привыкли это делать так, как разработчики nginx, то у вас может просто «глаз замылиться». Ну и парадокс Блаб-программиста так же может иметь место быть.
Игорь не был вынужден, это было его хобби и он был волен в выборе языка. Фразы «глаз замылиться» и «парадокс Блаб-программиста» не делают понятнее то, что вы хотите сказать…
Возможно, опыт написания программ у меня просто больше, чем у вас. Если вы говорили про 1997-ой год, то точно больше. Ну и C был одним из тех языков, с которых приходилось начинать. И ушел я с него при первой же возможности, т.к. даже в начале 90-х было понятно, что использовать C по собственной воле могут не только лишь все.
На C с 97-го, а так с 92-го, бейсик, ассемблер, паскаль, пролог… Тут, видимо, кому что, я неоднократно пытался перейти на C++ в своих проекта, так как у него объективно есть плюсы, но не лежит душа… На вкус и цвет…
Вообще-то три повтора. В двух из которых еще и нужно было функцию очистки явным образом вызывать.
Ок, я был невнимателен, 8 строк заменяется на лямбду из 5 строк плюс три вызова лямбды, тоже 8 строк, где выигрыш?
И если вы в лямбде насчитали пять строк, то у вас еще и проблемы со зрением.
Давайте посчитаем вместе:
const auto log_and_return_null = [&](const char * msg)
{
cor_log_error(log, msg);
return nullptr;
};
Я насчитал 5 строк. У вас меньше?
Ну тогда понятно. Вопросов больше не имею
Действительно, теперь всё понятно ))
В чём оно ошибочное?В основе своей.
Сложность синтаксиса языка не отрицает того, что сложная для понимания программа или простая — это результат работы программиста.Посмотрим на исходный тезис:
Простота кода зависит не от языка, а от программиста.
И смотрим на пример программы на J:
quicksort=: (($:@(<#[), (=#[), $:@(>#[)) ({~ ?@#)) ^: (1<#)
Вряд ли в этой программе есть что-то сложное, обычный такой quick sort. Сложным для восприятие ее сделал не программист, а синтаксис языка программирования.
Фразы «глаз замылиться» и «парадокс Блаб-программиста» не делают понятнее то, что вы хотите сказать…Ну извините, разжевывать еще подробнее нет желания.
По поводу количества строк. Скобки нет смысла считать. Так что там всего три строки. Но если вы хотите подсчитать скобки, то будет либо 4, либо 5, в зависимости от расположения открывающей. Но подобный подсчет выглядит еще более тупым занятием, чем попытки сравнить простоту и выразительность C-шного и C++ного кода.
Вряд ли в этой программе есть что-то сложное, обычный такой quick sort. Сложным для восприятие ее сделал не программист, а синтаксис языка программирования.
Солжным для программиста на этом языке или для программиста на другом языке, не знакомым с этим? Ну так для дворника все языки являются сложными…
Ну извините, разжевывать еще подробнее нет желания.
«Если вы ученый, квантовый физик и не можете в двух словах объяснить пятилетнему ребенку, чем вы занимаетесь, — вы шарлатан» Ричард Фейнман.
По поводу количества строк. Скобки нет смысла считать. Так что там всего три строки. Но если вы хотите подсчитать скобки, то будет либо 4, либо 5, в зависимости от расположения открывающей. Но подобный подсчет выглядит еще более тупым занятием, чем попытки сравнить простоту и выразительность C-шного и C++ного кода.
Ну т.е. я не слепой, а вы меня оклеветали, так? )))
Не вижу смысла дальше продолжать эту дискуссию…
В серьёзном проекте вы не обойдётесь без внешних зависимостей
Да. Но желательно обходиться минимумом. Потому что каждая внешняя библиотека это потенциальные проблемы:
1. усложнение процесса сборки (писал)
2. усложнение процесса деплоя (писал)
3. в библиотеках от версии к версии может меняться API
4. библиотеки могут устаревать (например, прекращается поддержка и их уже не собрать новым компилятором)
5. библиотеки могут распространяться не под все поддерживаемые платформы
6. в них могут появляться и исправляться ошибки.
7. даже если ошибок в самих библиотеках нет, их еще надо корректно использовать
Есть проект, у которого коллеге пришлось убирать зависимость от Qt. Если вы не пробовали — просто скажу, что это боль.
У нас используется сервер сборки. Каждая доп. зависимость должна быть отражена еще и там. С этим тоже бывают проблемы.
Бывают проблемы вида «старая версия библиотеки не собирается более новым компилятором, а в новой что-то не работает». Такое было недавно с boost
Так что из личного опыта: число зависимостей надо сводить к аскетичному минимуму.
В этом случае, расширение стандартной библиотеки, позволит многим проектам реже пользоваться сторонними библиотеками.
вы написали функцию inline/force_inline, 100 раз её использовали, а компилятор решил её не встраивать — линкер выкинет 99 скомпилированных тел вашей функции и оставит одно
кмк, если компилятор решит не встраивать, он и оставит одно тело
Т.е. Четко формулировать где когда какой результат или где выдавать ошибку.
В C# и Java принят другой подход — ну ради бога. Но зачем его тащить в C/C++?
потому что множество разбросанных граблей не делает язык хорошим.Это смотря какая у вас цель. Если у вас цель — безопасный язык, то вам не сюда: ни один язык с
free
/delete
не может быть безопасным. А если вам нужны переносимые программы — так UB это первейший помощник: они заставляют программиста писать программы так, что их легко потом скомпилировать.И для стат анализа это гемор.Смотря для какого. Для компиляторного — как раз наоборот: чем больше UB — тем лучше. Так как он на UB (вернее на отсутствие UB — не забываем, чья обязанность с ними бороться, да?) полагается в своём анализе очень сильно.
Для понимания внутренних механизмов — очень много. А вот пользоваться библиотеками на их основе можно будет как только эти библиотеки появятся.
А вот устройство библиотек для работы с ними действительно сложно, т.к. туда надо впихнуть и планировщик, и многопоточную синхронизацию.
P0458R0 — функция contains(key) member для классов [unordered_]map/set/multimap/multiset.А чем count() не угодил?
if (some_set.find(element) != some_set.end()) {
// ...
}
что несколько притянуто за уши, т.к. есть count()И как вы это в
if constexpr
вложите, я извиняюсь?if constexpr
выглядит примерно как метапрограммирование в C++98: всё, в принципе, делается — но жутко неудобно и «через одно место». В частности проблему выбора типов можно решить если вернуть указатель на нужный, а потом к соответствующей функции decltype
применить, но если вы после этого кроме AVX/NEON/SSE захотите ещё и «чисто скалярную версию» поддержать — то вам нужно будет весьма кривые трюки применять.Впрочем по-хорошему тут нужно делать отдельные модули и выбирать один из них во время компиляции. Интересно — как с этим всё будет обстоять…
Я вообще не понимаю регулярно вылезающих мелких косяков в стандартеНикто не хочет повторения истории с
export template
— когда в стандарте написали, а реализовать потом толком не смогли.Вот кто мешает разрешить делать if constexpr вне функций, например?Ну мало ли какие у разработчиков разных компиляторов заморочки. Просто не подумали, скорее всего. Да и вот это вот тоже как костыль выглядит:
template<class T> struct dependent_false : std::false_type {};
template <typename T>
void f() {
if constexpr (std::is_arithmetic_v<T>)
// ...
else
static_assert(dependent_false<T>::value, "Must be arithmetic"); // ok
}
Или вот вышел C++11 с using-алиасами, а всякие std::enable_if_t появились только в C++14. Или вот вышел C++14 с variable templates, а всякие std::is_same_v появились только в С++17.Ну это как раз понятно: выходит сначала базовый функционал, потом вспомогательный. Чтобы не было перерывов в полтора десятилетия как между C++98 и C++11
Практически механическое добавление алиасов вряд ли что задержало бы, на мой взгляд.Как раз подобные вещи сжируют кучу времени на совещаниях. Потому что если GCC реализовал одно, MSVC реализовал другое, а clang — третье, то это вот всё нужно как-то согласовывать.
Сложные же вещи зачастую имеют только одну реализацию, которую можно принять или не принять — и всё. Закон тривиальности в действии.
Для использования модуля его все равно придётся сначала скомпилировать из исходников, тут-то условная компиляция и вылезет.
Конкретно про null dereference и владение памятью — пункты I.11, I.12, F.7, F.26, F.27 и некоторые другие. Про unsafe — пункт I.30.
А алгебраические типы данных, насколько я понимаю, тот же самый std::variant, вид в профиль.
Можно будет инициализировать поля структур, прям как в чистом C:
struct foo { int a; int b; int c; };
foo b{.a = 1, .b = 2};
Господи, дождались!
Другое дело, что использовать нестандартные расширения языка, на мой взгляд, не лучшее решение. Я обычно просто запихивал такие инициализации в файлы.с.
Странно, только что проверил gcc 6.3 — собирает.Мы ушли с GCC после GCC 4.6. А оказывается GCC 4.7 тоже так умеет.
Они хотя бы в release notes это упомянули бы, что ли…
Другое дело, что использовать нестандартные расширения языка, на мой взгляд, не лучшее решение.Теперь можно с чистой совестью говорить, что вы просто используете стандартную фичу из C++20
А вы попробуйте немного другой код:
foo b{.c = 2};
и опцию -pedantic
. И Gcc такое уже не собирает: "sorry, unimplemented: non-trivial designated initializers not supported".
Правда все компиляторы таки ругаются на такое использованием: https://wandbox.org/permlink/D75xMWgpGfXi1PMU
ЗЫ по ссылке пример использования данной фичи как альтернативы именованных аргументов функции.
Шланг собирает. Gcc — нет. Без -pedantic
даже не ругается, а с ним — как по ссылке у меня.
-pedantic
, как бы, и должен ругаться на использование нестандартных фич, так что это нормально.Хуже, что он не позволяет опускать поля и указывать только подмножество (как в предложении для C++20). Без этого, как бы, смысл совсем теряется…
Я к тому, что сейчас, так или иначе, это реализовано (если реализовано, привет Gcc!) не как часть предложения для C++20, а как часть функционала C99, эдакое расширение.
Хуже, что он не позволяет опускать поля и указывать только подмножество (как в предложении для C++20). Без этого, как бы, смысл совсем теряется…
только Gcc. Шланг может и без -pedantic
вполне можно пощупать, как оно будет. Другое дело, что оно реализовано не в соответствии с предложением для C++20, а как в C99.
Подскажите пожалуйста кто в курсе по поводу двух особенностей модулей:
Своя нотация именования
// <root>/src/Module1.cpp
module foo.bar;
// ...
// <root>/src/dir1/Module2.cpp
module foo;
import foo.bar;
Вместо того, чтобы сделать простой и понятный импорт по относительным путям
// <root>/src/Module1.cpp
import "Dir1/Module2"; // See below
// some code goes here
// ...
// <root>/src/Dir1/Module2.cpp
import "std/vector";
// some code with exports goes here
Это КМК решило бы сразу несколько проблем — не нужно явно указывать имя модуля, пути импорта понятны из кода, плюс естественная возможность приватных модулей. Если вы скажете "а как же многофайловые модули?" — а зачем они вообще нужны? Откровенно выглядит как костыль. Генерацию интерфейсной части из общего текста модуля можно как раз оставить на совести компилятора.
Глобальный модуль
В черновике упомянут некий "глобальный модуль", в который входит весь код до декларации module
. Этот двойной пассаж мне честно говоря не понятен. Единственное подозрение — необходимость куда-то деть содержимое файла до декларации модуля.
Я попытался спросить в общей гугл группе https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-proposals/SdgWgJCR35s, но мне к сожалению так никто и не ответил.
Потому что это удобно. Неприятно работать с одним файлом на 10к строчек.
Потому что это удобно. Неприятно работать с одним файлом на 10к строчек.
Эмм, просто поделить на несколько вложенных модулей? С указанным подходом это будет тривиально.
Да, придётся добавить к таким функциям export. Зато будет понятно, где что лежит. При описанном вами сценарии с модулем в 10К строчек, делённым на несколько файлов, придётся ещё выяснять где лежит каждый кусок — т.к. логическое имя модуля вообще никак не связано с его местоположением в исходниках.
Тогда я не понимаю, какой уровень призваны организовать модули. Если уровень юнитов трансляции — то где, действительно, возможность организовать приватные детали реализации, и зачем такие с мультифайловостью и отдельным именованием? Если же уровень единиц сборки — зачем отдельно декларировать везде имя модуля, и что будет, если несколько модулей лягут в один бинарь?
Уважаемый antoshkka, у вас случайно нет "человекочитаемого" описания текущего состояния по модулям — на каком уровне они существуют (грубо — единица трансляции или линковки), как предполагается существующий код с инклюдами приводить к модульному виду и т.п. Судя по https://habrahabr.ru/company/yandex/blog/336264/#comment_10449246, понимания, как оно должно быть, нет как минимум у меня.
fmt::format("The answer is {}", 42);
??? Вспоминается вот этот пост: https://news.ycombinator.com/item?id=13528599. Особенно вот этот коммент:
Makes you wonder why HN won't budge with those<table>
tags in the source code. One day I hope to see "Table layouts are back with<table>
tags" and the HN programmers would have saved themselves so much work.
Поняли, да? Кучу лет верстали тегами table, потом выяснилось, что table layout — это плохо. Стали на дивах. Теперь вдруг table layout is back. Что дальше? Table layout на тегах table?
Так и с вашим fmt::format. В си был printf/sprintf/snprintf. Потом пришёл C++ и оказалось, что printf — это плохо и надо cout. Теперь вдруг fmt::format. Что дальше? Снова на printf?
На шаг ближе к С++20. Итоги встречи в Торонто