Каждый день, везде, в том числе в технических статьях, появляется мифический С/С++, честно говоря я просто устал это видеть и хочу в этой короткой статье донести простейшую мысль - С и С++ это два совершенно разных языка с разными требованиями и подходами к разработке.
Языки несовместимы и уже давно. Они развиваются параллельно(хотя про развитие стандарта С не знает ничего даже гугл, но он есть, добавили дженерики и хотят лямбды...)
-
Громадная часть С кода хоть и компилируется в С++, но является undefined behavior. В первую очередь из-за того, что все типы в С являются тупым набором байт и никакая эмуляция полиморфизма здесь не поможет. Язык позволяет относится со всем как с байтами, тогда как в С++ все типы это объекты инкапсулирующие свою логику через конструкторы и деструкторы, есть понятие лайфтайма, относится как к байтам можно только к тривиально копируемым объектам и то не всегда и не везде, только в С++20 валидно было бы реинтерпретировать байты как int, а в С коде это происходит повсеместно.
Согласно стандарту С++ программа с undefined behavior не является программой на С++, а значит как только вы скомпилировали сишный файлик скорее всего ваша программа перестала быть С++ кодом.
Самое главное - языки требуют абсолютно разных умений и подходов к разработке.
С - фактически императивный язык структурного программирования(да и с этим можно поспорить из-за обилия goto в некоторых проектах). Он не предполагает никакой декларативности, полиморфизма, инкапсуляции, иммутабельности и прочих модных слов. Его сфера использования это места, где нет С++ компилятора(глубокий наколеночный эмбед)
Современный С++ в свою очередь это во многом декларативный и функциональный - шаблоны, алгоритмы, итераторы и ренжи, RAII - язык с элементами ООП в виде инкапсуляции логики в объекты(типа вектора) или наследования для реализации чего-то(но почти никогда не так, чтобы пользователь абстракции знал что она реализована через наследование). Писать что-то в императивном стиле в современном С++ просто странно.
Эти подходы(декларативный и императивный) фактически противоположны, поэтому разработчик на С++ будет с трудом писать на С, а разработчик на С, как показывает практика, вообще не сможет писать в стиле С++
И это не абстрактные рассуждения в вакууме, если компания ищет С/С++ разработчиков, то она не знает, чего хочет и найдёт посредственных специалистов или команду абсолютно несовместимых людей, у которых не получится вместе что-то разрабатывать.
Давайте уже одумаемся.
Комментарии (307)
nanev1976
13.07.2022 19:55+13Не-а. C это подмножество C++, ровно как и подмножество Objective-C. Компании ищут "C/C++" так как программисту часто (всегда) будет нужно работать с "тупыми наборами байт" и что еще хуже, взаимодействовать с железом (даже если посредством толстого фреймворка типа CUDA Toolkit). Если этого нет, то писать на C/C++ не имеет никакого смысла, для бизнес логики есть всякие сишарпы с джавами.
Kelbon Автор
13.07.2022 20:06+21только часть С это подмножество С++
А с точки зрения хорошего С++ кода практически ничего из С использовать не нужноChaos_Optima
13.07.2022 20:34+34Видимо геймдев не в курсе. А то там постоянно парятся над выравниванием байтиков, алокациями и прочим менеджментом памяти.
Кстати ООП также преимущественно является императивным стилем, как и функциональное программирование. Я честно говоря ещё ни разу за 12 лет не видел в С++ декларативного программирования, или для вас использование готовых библиотек является декларативным программированием?
Kelbon Автор
13.07.2022 20:38-6А как связаны аллокации и С?
Chaos_Optima
13.07.2022 21:22+10Я имею ввиду написание своих аллокаторов, что по моему мнению напрямую схоже с С way
domix32
13.07.2022 22:31+4И это не считая что существует куча именно Сишных зависимостей, типа того же curl или openal. Да те же pthreads/epoll и все что их окружает. И в мире С++ они появляются заметно чаще, чем хотелось бы. Потому оно и С/С++.
lorc
13.07.2022 23:07+2Я вообще не уверен что в C++ можно открыть сокет и послать пакет без reinterpret_cast или там C-style cast. На этапе
connect
уже возникнут проблемы.domix32
14.07.2022 00:29+4Как-то так до сих пор и нет std::network в библиотеке. Всё остальное из cstd подключают.
Kelbon Автор
14.07.2022 06:59-1подключают boost::asio, а не сишные сокеты
DabjeilQutwyngo
14.07.2022 09:19+5Открыл исходник v69 по пути
"detail/impl/socket_ops.ipp"
:... #include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> #include <cerrno> #include <new> #include <boost/asio/detail/assert.hpp> #include <boost/asio/detail/socket_ops.hpp> #include <boost/asio/error.hpp> ... template <typename SockLenType> inline socket_type call_accept(SockLenType msghdr::*, socket_type s, socket_addr_type* addr, std::size_t* addrlen) { SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0); if (addrlen) *addrlen = (std::size_t)tmp_addrlen; return result; }
::accept
-- тот самыйaccept
для сишного сокета.Kelbon Автор
14.07.2022 09:23-5это не сишный сокет, а платформозависимый, вы бы ещё сказали "открыл .exe, там машинный код"
Kelbon Автор
14.07.2022 08:30-5Аллокатор в С и аллокатор в С++ это совершенно разные вещи.
С++ аллокатор это абстракция, легковесный объект предоставляющий интерфейс для получения памяти.
Chaos_Optima
14.07.2022 12:53Ну ок чем отличается по вашему
//C++ auto data = Allocate<Data>(); // от С struct DataHandle* data = ALLOCATE(Data);
Внутри ведь они могут будут устроены практически одинаково.
Kelbon Автор
14.07.2022 13:09-6Вы понятия не имеете как выглядит аллокатор в С++
Спойлер
ВОТ ТАК
template<typename T, typename Alloc = std::allocator<T>>
struct vector { ... };Chaos_Optima
14.07.2022 13:58facepalm Я говорю про реализацию аллокаторов а не про использование их в дальнейшем, видно вы никогда не писали свои аллокаторы. Ладно хотите пример применения на подобие С++ ок
struct vector = create_vector(ALLOCATOR_FOR(Data)); ... struct Data item = at(vector, i);
Kelbon Автор
14.07.2022 14:00Это уже рантайм передача аллокатора, ваш код значительно менее эффективен чем аналогичный на С++, выглядит хуже, использует макросы
Ассемблер ужасен, да и всё равно не вы сможете реализовать полноценный вектор. Просто попробуйте и поймете, что С не даст вам этого сделать
Chaos_Optima
14.07.2022 15:27Это уже рантайм передача аллокатора, ваш код значительно менее эффективен чем аналогичный на С++, выглядит хуже, использует макросы
Простите, но указанный аллокатор в векторе создаётся разве не в рантайме? Точно также создаётся в рантайме, а ещё его можно передать конструктору вектора. Но самое главное, речь была не про использование аллокатора, а про устройство аллокатора, как он внутри устроен, вы упорно это игнорируете и пытаетесь свинтить в другую тему.
Kelbon Автор
14.07.2022 15:38+1Нет не в рантайме. Стандартный аллокатор не занимает там места вообще, кастомный с состоянием может занимать состояние и подставляться на рантайме
Chaos_Optima
14.07.2022 15:47Ну так мы же говорим о кастомных аллокаторах, они также создаются в рантайме. В С тоже если не использовать кастомный аллокатор он не будет занимать места.
0xd34df00d
14.07.2022 16:23Если вы говорите про реализацию аллокатора, то на чистом стандартном C написать аллокатор (и вообще
malloc
) умнее, чем «выделить из статического массива» нельзя. Что это говорит о связи C и, не знаю, ассемблера?Chaos_Optima
14.07.2022 16:29Почему нельзя? У гуглов и интелов получилось вроде. Правда не до конца понимаю что вы имели ввиду.
lorc
14.07.2022 16:40Ну так с ядра все начинается и им же все заканчивается. Какая разница, прочитает - ядерный загрузчик размер секции .bss из ELF для того чтобы выделить кусок памяти или само приложение вызовет mmap(ANONYMOUS) чтобы получить себе памяти в свое адресное пространство?
Рантайм любого языка будет дергать ядерные вызовы типа mmap или sbrk(не к ночи будь упомянут) чтобы получить память от ядра для своего внутреннего аллокатора.
0xd34df00d
14.07.2022 16:44А внутри
mmap
илиsbrk
что происходит? В итоге написанный на ассемблере код вызывается, не так ли?
lorc
14.07.2022 17:01Вы удивитесь, но скорее всего нет. Если продраться через абстракции, то все сводится к записи в таблицу трансляции MMU. Прямо из сишного кода. Что-то типа
page_table[virtual_addr] = physical_addr
.(По пути вызовов конечно придется пройти через ассемблерный код который переключает контекст из приложения в ядро, но это неизбежно для любого системного вызова)
flx0
14.07.2022 17:11А как по-вашему ядро знает какие у него физические страницы заняты, а какие свободны?
lorc
14.07.2022 17:17Ну как.... У него есть свой аллокатор страниц. Который следит за тем, какие страницы кому были выданы.
0xd34df00d
14.07.2022 17:14+4- Откуда берётся физический адрес?
- Какое место в стандарте C гарантирует, что оттуда можно читать и туда можно писать?
- Как это всё работает с оверкоммитом и выделением страниц по мере обращения к ним? Какие сишные конструкции или функции из стандарта в итоге это позволяют?
lorc
14.07.2022 17:45Откуда берётся физический адрес?
Из аллокатора страниц. Аллокатор знает где вообще расположена память потому что ему об этом сказал условный BIOS.
Какое место в стандарте C гарантирует, что оттуда можно читать и туда можно писать?
А это самая банальная память. Туда можно писать и оттуда можно читать как угодно.
Вы наверное спрашиваете про Memory Mapped IO, которые хоть и выглядят как память, но ею не является. Поэтому ядро кстати туда на пишет через
a = 17
. Есть специальные функции обертки:writel(a, 17)
. Которые с одной стороны обламывают шибко умный компилятор, и с другой - обеспечивают совместимость со стандартом. (А вот всякий код на arduinо пишет в лоб. Стыд и позор им.)Как это всё работает с оверкоммитом и выделением страниц по мере обращения к ним? Какие сишные конструкции или функции из стандарта в итоге это позволяют?
Ну там на самом деле сложнее конечно. Просто если я еще буду рассказывать про аппаратные исключения от MMU и вот это все, то это будет очередной пост "как написать свою ОС".
А насчет стандарта - для приложения стандарт заканчивается в тот момент, когда оно дергает условный
mmap
или другой системный вызов. Потом происходит магия за пределами абстрактной машины описанной стандартом и мы получаем свой валидный указатель на void.Ядро же - это отдельная сущность. Отдельная абстрактная машина с точки зрения стандарта. Для ядра - операции над page tables приложения выглядят как просто работа с массивом структур. Стандарт это позволяет, надеюсь :)
0xd34df00d
14.07.2022 18:16+2Аллокатор знает где вообще расположена память потому что ему об этом сказал условный BIOS.
Как сказал? Какая сишная функция отвечает за взаимодействие с BIOS?
А это самая банальная память. Туда можно писать и оттуда можно читать как угодно.
Вы утверждаете, что я могу написать
int *ptr = (int*) 0xd34df00d; printf("%d\n", *ptr);
и это будет валидный сишный код?
Я утверждаю, что нет. Более того, я утверждаю, что это не факт что корректный C-код, даже если я знаю, что эта область памяти была раньше выделена каким-нибудь
malloc
, потому что pointer provenance.А насчет стандарта — для приложения стандарт заканчивается в тот момент, когда оно дергает условный mmap или другой системный вызов. Потом происходит магия за пределами абстрактной машины описанной стандартом и мы получаем свой валидный указатель на void.
И я утверждаю, что эту магию невозможно реализовать на стандартном C.
lorc
14.07.2022 21:06Как сказал? Какая сишная функция отвечает за взаимодействие с BIOS?
А какая сишная функция отвечает за обмен данными по сети? Или если в стандарте не описаны
socket
/connect
/send
/recv
то валидная сишная программа не может обмениваться данными через сокеты?Я утверждаю, что нет.
Тем не менее, это будет валидный код:
An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
потому что pointer provenance.
Вообще, в стандарте C нет memory model. В отличии от стандарта C++.
И я утверждаю, что эту магию невозможно реализовать на стандартном C.
Таки можно. Это будет implementation-defined (еще бы, в windows вообще нет mmap), то стандарту это не противоречит.
Вообще, стандарт например упоминает такие штуки как memory-mapped input/output address или тот факт что hardware может изменять volatile переменные.
0xd34df00d
14.07.2022 22:34Или если в стандарте не описаны socket/ connect / send / recv то валидная сишная программа не может обмениваться данными через сокеты?
Ну, я бы сформулировал корректнее: невозможно на одном лишь стандартном С реализовать весь сетевой стек.
Тем не менее, это будет валидный код:
Нет, не будет.
An integer may be converted to any pointer type.
Я там его не только преобразую, но и разыменовываю.
Вообще, в стандарте C нет memory model. В отличии от стандарта C++.
Ну, я думаю, что с C11 таки есть, потому что в C11 тоже завезли многопоточность, а завозить её без memory model довольно тяжело.
Но это неважно: здесь речь идёт, во-первых, о лайфтаймах (6.2.4 в N1256, например).
Во-вторых, pointer provenance — это вообще какая-то наркомания, на которую у меня нет хорошего источника, и которую я вам предлагаю погуглить самостоятельно.Это будет implementation-defined (еще бы, в windows вообще нет mmap), то стандарту это не противоречит.
Что именно? Куски магии? Нет, не будет implementation-defined.
Implementation-defined будет само наличие этой магии, но она действительно стоит за пределами стандарта.
Вообще, стандарт например упоминает такие штуки как memory-mapped input/output address или тот факт что hardware может изменять volatile переменные.
Да, единственное место — 6.7.3/5 и /6. Но эти пункты лишь описывают частные случаи undefined behaviour. У вас с ними не получится реализовать свой malloc или даже считать что-нибудь из биоса.
Из упоминания «UB даже неправильное по волатильности обращение к memory-mapped I/O address» не следует, что их можно иначе считывать.
lorc
14.07.2022 23:05Ну, я бы сформулировал корректнее: невозможно на одном лишь стандартном С реализовать весь сетевой стек.
Почему? Стандарт даже прерывания упоминает. И memory IO вроде как не запрещает. Так что можно даже драйвер сетевой карты написать. Ну а дальше - пошло-поехало.
Я там его не только преобразую, но и разыменовываю.
Разыменовывать нельзя только невалидные указатели. Учитывая что валидность такого сконструированного указателя - implementation defined - то и правомерность его разыменования - тоже выходит implementation defined.
Короче да, это будет непереносимый код. Но стандартом явно не запрещенный же.
Ну, я думаю, что с C11 таки есть, потому что в C11 тоже завезли многопоточность, а завозить её без memory model довольно тяжело.
5.1.2.4 в N1548. Упоминается конфликт при доступе к memory location, а не к object. Объекты упоминаются только в контексте atomic object.
И, кстати, этот стандарт упоминает ‘‘volatile as device register’’ semantics. Что опять же намекает на возможность обращаться к железу прямо из сишной программы. Но вообще конечно могли бы написать прямо, что ли...
У вас с ними не получится реализовать свой malloc или даже считать что-нибудь из биоса.
Переносимым образом - да, очевидно не получится.
Из упоминания «UB даже неправильное по волатильности обращение к memory-mapped I/O address» не следует, что их можно иначе считывать.
Но и не следует что считывать их нельзя.
На этом невысказанном предположении и держится весь код операционных систем, гипервизоров, драйверов, прошивок и т.д... Кстати, стандарт явно описывает freestanding environment, т.е. возможность запускать программу без ОС.
0xd34df00d
14.07.2022 23:33Так, пажжите. Что для вас «стандартный C»? «Код, для которого существует хотя бы один соответствующий стандарту компилятор, который его сожрёт и сделает то, что я подразумеваю»? Или «Код, для которого любой соответствующий стандарту компилятор его сожрёт и сделает то, что я подразумеваю»?
lorc
14.07.2022 23:49Ну так стандарт описывает оба случая. Они там называются conforming program и strictly conforming program соответственно.
Лично я выступаю за strictly conforming программы тогда, когда это возможно. Учитывая, что мы тут говорим все же про embedded и всякое такое, которое в принципе не может быть strictly conforming, остается довольствоваться просто conforming.
0xd34df00d
14.07.2022 23:58+1Просто в такой формулировке и UB соответствует стандарту, а это не имеет смысла.
mpa4b
14.07.2022 17:20mmap и sbrk подключают в адресное пространство процесса дополнительные mmu-странички с памятью. И всё. Как потом эту память нарезать на куски, выдаваемые в программу libc-шным malloc'ом или ещё каким аллокатором -- проблема исключительно этого malloc'а и прочего кода, выполняемого в юзерспейсе.
Stariy2003
15.07.2022 20:31А вы сможете по машинному коду определить, на каком языке он написан? С++ от Асма отличите в ехе-шнике?
0xd34df00d
15.07.2022 22:19Когда как. Есть местами характерные паттерны, которые использует привычный компилятор плюсов. И обратно, есть инструкции, которые компилятор не выплюнет практически никогда, поэтому их наличие — хороший признак, что ассемблер либо писали руками, либо скомпилировали что-то ещё.
А вот месиво с замыканиями в ассемблере, получающемся из хаскеля, я прекрасно отличу от любого другого языка.
Lazerate
13.07.2022 22:49+15Вы, наверно, не в курсе, что запаривание алайнментом, аллокациями и т.д. ещё не значит, что это вообще как-то связано с Си. И то, что в С11, равно как и в С++11, вошёл тот же alignas, ещё не делает написание его или размышление над подобными вещами сишным.
Да и написание своих аллокаторов, вот просто как вообще можно подвязать к Си? Особенно учитывая, что в большинстве случаев такие вещи как аллокаторы выглядят абсолютно иначе, чем как если бы их писали на Си. Тем более что в С++ появляется куча вопросов, которые не так ярко выражены, либо которых вообще нет в Си, таких как пропагировать ли аллокатор на копировании, на перемещении и т.д.
Честно, меня искренне удивляет, когда говоря о низкоуровневых манипуляциях с памятью, люди в большинстве случаев обязательно имеют в виду Си, хотя С++ точно так же имеет кучу средств для этого и, откровенно говоря, выразительнее в этом плане. Даже на собеседовании однажды пришлось писать на Си, под предлогом "ну нам же надо увидеть как вы умеете манипулировать памятью". И честно скажу, с непривычки и из-за необходимости думать о вещах, о которых я обычно не думаю, я допустил ошибку в том коде.
Chaos_Optima
14.07.2022 05:47+3Под запариванием с выравниванием я подразумевал работу с теми самыми пресловутыми байтами, понятное дело что оно может быть сделано на более высоком уровне, тем не менее это всё тоже ковыряние с байтами, например DX константные буфера (да и все остальные ресурсы) мапятся в обычный кусок памяти и функция возвращает void* а дальше работай с этим куском памяти как заблагорассудится тут например и нужно запариваться с выравниванием. То что С++ имеет огромное количество инструментов для эффективного управления памятью не делает его менее С way это просто обёртка, которую без проблем можно заменить на свою.
Lazerate
14.07.2022 11:48+1Гм, ну, так любую работу с байтами на любом языке можно назвать абстракцикй над Си. А там и над ассемблером. Но банально вот до С++20 множество способов, которыми ковыряли и ковыряют байты в Си были попросту запрещены и приводили к неопределённому поведению. Ну а то, что всюду и везде Си апи это да. Но так, если докопаться, везде устроено – что-то в конечном итоге да будет дёргать код, написанный на Си. Это ведь всё ещё не значит, что любая работа с байтами в любом языке Си-way...
Chaos_Optima
14.07.2022 12:08Я не говорю про абстракции на Си я говорю что любое ковыряние с байтами, будь то выравнивание или мапинг, это С way даже если это ковыряние байтов в питоне или шарпах, потому что в данном случае необходимо знать что такое выравнивание и как всё в принципе работает на низком уровне. Просто в шарпах или том же питоне это практически никогда не встречается а в С++ постоянно, по крайней мере в моей сфере.
eao197
14.07.2022 12:22Я не говорю про абстракции на Си я говорю что любое ковыряние с байтами, будь то выравнивание или мапинг, это С way
Теперь бы еще понять почему.
Ковыряние байтов в Ada -- это тоже C way? А в Modula-2? А в Lisp, а в Algol-68?
Chaos_Optima
14.07.2022 12:58В рамках данной статьи я считаю да, потому что идёт сравнение с С. Ясно дело что можно всё свести к тому что языки перебирающие байтики были и до С и по сути С way это ada way и так далее вплоть до Тьюринга.
eao197
14.07.2022 13:02Смысла в ваших словах не видно. Но мысль я понял: если вам удобно называть операции с байтами "C way", значит вы будете называть это "C way" не смотря на то, что байтиками манипулировали и до Си, и после Си в языках, которые к Си никакого отношения не имели.
Удобно, чё.
shybovycha
14.07.2022 14:22+2Если я правильно понимаю, то арифметика на указателях это далеко от C++ way. И если мне не изменяет память, все те выразительные инструменты (в т.ч. для работы с памятью) которые предлагает С++ не особо хорошо натягиваются на некоторые задачи (программирование микроконтроллеров, например).
То есть в моем понимании, если надо дикий перформанс и/или присутствуют жесткие ограничения по железу (ATtiny85 - 8KB flash, 512B RAM, 20MHz), то те же итераторы и умные указатели (насколько я помню, C++ Core Guidelines говорят о ручных выделениях и освобождениях памяти как о анти-паттерне) - не вариант. А иначе - это уже не каноничный С++.
Kelbon Автор
14.07.2022 14:31-3Итераторы это абстракция с отрицательной стоимостью, они не делают ваш код медленнее, наоборот ускоряет. unique_ptr только в некоторых случаях на некоторых платформах и АБИ будет иметь маленький оверхед. shared_ptr просто невозможен в С, замучаетесь.
И да, указатели на умные, а владеющие. Это не замена T*
PkXwmpgN
14.07.2022 17:29unique_ptr только в некоторых случаях на некоторых платформах и АБИ будет иметь маленький оверхед
sim31r
14.07.2022 19:55ATtiny85 даже среди микроконтроллеров 90х годов запредельный аскетизм, его можно на ассемблере программировать. Современные микроконтроллеры это типично STM32F4, 240МГц, мегабайты флеша, сотни килобайт памяти, он сравним с первыми Пентиумами по производительности.
iamkisly
15.07.2022 22:06Не согласен с самой концепцией которую вы вкладываете в "современные микроконтроллеры". У каждого производителя есть несколько линеек микроконтроллеров, каждая из которых имеет свое функциональное позиционирование, где она будет наиболее дешева и востребована. И из этой функциональной иерархии никуда не исчезли сверхдешевые 8-битники, или не производительные и емкие, но очень экономичные Cortex-M0/-M1.
Другое дело, что в некоторых случаях удобнее брать самый жирный чип "для всего", и потом уже думать над удешевлением. Но это экономия на инженерах.
storoj
13.07.2022 23:10деструктор в некоторой степени можно назвать примером декларативного программирования
0xd34df00d
14.07.2022 05:29+5Я в трейдинге тоже парился над байтиками, выравниванием, аллокациями, и так далее, но делал это в очень плюсовом стиле.
Кстати, а как функциональное программирование является императивным стилем? Какой смысл вы вообще вкладываете в эти слова?
Chaos_Optima
14.07.2022 05:56-2Кстати, а как функциональное программирование является императивным стилем? Какой смысл вы вообще вкладываете в эти слова?
Что подразумевается под императивным стилем? Это последовательное выполнение инструкций и переиспользование результатов этих инструкций. Чем функциональное программирование в данном случае отличается, я имею ввиду реальные функциональные яп, в идеале функциональные языки являются декларативными, но по факту получается что мы всё также описываем последовательность инструкций которые должны последовательно выполнятся (я не беру в расчёт возможность перестановки или кеширования результатов компилятором\интерпретатором т.к. это есть и в С и в С++). Возможно моё понимание декларативного стиля не соответствует с обще принятым, но для меня всё это выглядит как просто вызов функций которые внешне декларативные но внутри всё тажа императивщина
0xd34df00d
14.07.2022 06:27+3в идеале функциональные языки являются декларативными
Ну вот хаскель — функциональный?
Я там тоже могу написать набор байндингов, который как-то вычислится в нормальную форму и выведется мне на экран. При этом я, в принципе, могу более-менее рассуждать о порядке вычисления того, что получится, и делать это не сильно хуже, чем я рассуждаю о порядке выполнения операций в плюсовом коде (особенно с учётом того, что оптимизатор может некоторые вещи переупорядочивать и выкидывать). И, собственно, я не могу уловить принципиальную, качественную разницу стратегии вычисления.
Разница между ленивым (вроде хаскеля) и энергичным (вроде идриса или хаскеля с
-XStrict
) функциональным языком тут будет сильно больше, чем разница между идрисом и плюсами.
Kelbon Автор
14.07.2022 07:02-3По такой логике под любым языком машинный код, значит всё императивность. Но это очевидный бред, т.к. декларативность в том как ты пишешь код, а не как он исполняется.
К тому же если взглянуть на QT или любую другую event based, ну или корутины С++20(или в целом асинхронное программирование), то там нет никакой последовательности, есть события и объекты
Fell-x27
14.07.2022 09:20+12Вы, похоже путаете императивность с "код движется строго сверху вниз", а декларативность с "код может перепрыгивать в функции/методы, которые, формально, выше исполняемого кода". Меня еще смутило, что вы признаком декларативности назвали goto...
И Си и Си++ оба императивные языки. Как и подавляющее большинство. В императивном языке вы описываете логику получения результата. По шагам. Процедуры, ооп и тд это лишь механизмы для повышения удобства разработчика. Код декларативным не становится от этого.
Декларативные языки - это в которых разработчик описывает результат, а то, как его достичь, уже проблема программы. Яркий пример - SQL. Любой диалект.
Kelbon Автор
14.07.2022 09:25декларативность с "код может перепрыгивать в функции/методы, которые, формально, выше исполняемого кода". Меня еще смутило, что вы признаком декларативности назвали goto...
Откуда вы это взяли вообще? Я такого даже близко не говорил
shybovycha
15.07.2022 00:59Я так понял автор считает любую программу которая описывает последовательность действий для получения результата императивной. В таком случае декларативный только какой-нибудь Пролог (если я правильно понял автора).
AllexIn
14.07.2022 07:52+8Я в геймдеве как core программист работаю уже больше 15 лет.
И полностью согласен с автором: при использовании С++ практически ничего из С не используется.
При этом С я тоже знаю, писал для смарт часов софт на чистом С. И это адище то еще. Совершенно другой подход, сильно раздражающий.
Но вернемся к геймдеву:
Даже когда на плюсах пишеш низкоуровневую логику, всё равно придерживаешься С++ подходов. Даже условный аллокатор... Где там нужен С?
Тем более современный геймдев, там вообще во всю шаблоны. И если 15 лет назад это воспринималось как "Эпики вообще больные, пишут движок на шаблонах. Это же будет тормозить!". То сейчас это "Эпики сделали стандарт индустрии".Chaos_Optima
14.07.2022 13:06+1Очень интересно, можете рассказать как вы мапите те же Constant Buffers или заливаете ресурсы? А можно пример аллокатора без использования адресной арифметики? Конечно если вы используете готовые библиотеки или движки для этого, то вы и не столкнётесь с Си, но если пишете с нуля сишные ноги будут торчать из всех щелей.
AllexIn
14.07.2022 13:37+2С чего вдруг адресная арифметика стала С спецификой?
Вы еще скажите, что использовать операцию сложения это С way, потому что сложение было в С.Chaos_Optima
14.07.2022 14:07Наверное я не правильно выразился и не смог донести свою точку зрения, попробую иначе.
void* allocate(size_type size) { void* out = hip_pos; hip_pos = hip_pos + size; return out; }
Это код на С++ или на С? Я веду к тому что когда пишутся низкоуровневые вещи без использования дополнительных библиотек грань различия С и С++ практически стирается.
eao197
14.07.2022 14:23Разница обнаружится сразу же по выходу из allocate, т.к. в C++ вы не можете с
void*
обращаться так же вольно, как в Си. Начиная от того, что в C++ нет автоматического неявного каста изvoid*
во что-то другое. И заканчивая тем, что нельзя просто так делатьreinterpret_cast<T*>(some_void_p)
(может потребоваться применятьstd::launder
).Chaos_Optima
14.07.2022 15:33+1Ну на выходе да, но речь то про внутренности. Взять тот же std::string снаружи всё красиво а внутри сишная карусель с указателями и байтами если есть sso
mxr
13.07.2022 21:20+6Как минимум kernel в том же CUDA Вы пишите именно на C.
tabtre
13.07.2022 23:34И на С++ можно
К примеру из недавно встретившегося
github.com/NVIDIA/nvcomp/blob/branch-2.2/src/RunLengthEncodeGPU.cu#L162
Akon32
14.07.2022 13:17+1CUDA ядра очень давно поддерживают фичи из С++, такие как классы, лет 10 минимум.
RH215
14.07.2022 15:01>А с точки зрения хорошего С++ кода практически ничего из С использовать не нужно
...но линуксовые API всё ещё написаны на C. Да и в целом, если необходимая тебе библиотека написана на С, то С рано или поздно в твоём cpp-коде окажется.
Собственно, несмотря на то, что C и C++ - разные языки, у них есть общее подмножество, которое почти полностью совпадает с С.
vconst
15.07.2022 16:13Я помню оранжевую книжку, перевод Страуструпа
(без ката, пусть кто-то всплакнет)
Но еще, помню основы систематизации беспозвоночных из Догеля, которые, с 90х годов, уже несколько раз успели поменяться.
Систематизация — постоянно развивается. То, что раньше считалось отрядом — теперь считается подклассом. И наоборот. Можно продолжать хвататься за старое, но современные специалисты вам просто не поймут
flx0
13.07.2022 20:12+14Уже давно не подмножество. В плюсах нет как минимум restrict и _Generic (действительно, зачем он когда есть шаблоны?), да и код без них не всегда может быть скомпилирован обоими компиляторами из-за разных ограничений на приведение типов (тут я конкретный пример, впрочем, не вспомню).
Cheater
13.07.2022 20:33+23C это подмножество C++
Псст, хотите немного уличной магии!
1) Преобразование int к enum
enum { e0 = 0 } e = 0; // валидно в C, но не в C++
2) Неявное преобразование void*
void* pv = 0;
int* pi = pv; // валидно в C, но не в C++
iago
13.07.2022 20:42+19Какое популярное заблуждение, я думал его уже давно нет. С - это подмножество Ojbc, которому я отдал 8 лет с 2009 по 2017 - это утверждение верно. Потому что код обжа сначала транслируется в чистый С, а потом компилится уже компилятором С. И в iOS до сих пор огромное множество фрэймворков написаны на чистом С.
С++ же только внешне похож на С (и то был когда-то), под капотом совершенно другое. В их компиляторах ни одной общей строчки, один и тот же код компилится в совершенно другой АСМ.
Про писать на C/C++ нет смысла расскажите разработчикам ОС и, например, игровых движков, им же расскажете и про тупые наборы байт.
Я тоже обычный прикладной девелопер и пишу на swift, где днем с огнем не нужда адресная арифметика и системы реального времени. Но хоть нас и большинство, опрометчиво всех ровнять под одно.
Как и автору оригинального поста - С++ до декларативности как Пугачеве до Мадонны, уж слишком он древний и громоздкий.
Kelbon Автор
13.07.2022 21:21-21Как и автору оригинального поста - С++ до декларативности как Пугачеве до Мадонны, уж слишком он древний и громоздкий.
Может вы просто не умеете?)
Chaos_Optima
13.07.2022 22:07+17А может вы приведёте пример декларативного кода на С++?
tzlom
13.07.2022 22:39-2У меня есть маленькая библиотечка на C++11 которая из набора функций строит граф зависимостей (compile time) и выполняет его для заданных входов и результата/ов ,правда в публичном доступе её пока нет.
Chaos_Optima
14.07.2022 06:10Это всё использование библиотек, с таким же успехом можно написать библиотеки и на С которые позволят писать в декларативном стиле (да и вообще в любом), а на уровне языка есть что-то?
Kelbon Автор
14.07.2022 06:39-2Вот именно, что в С таких библиотек нет и быть не может. Даже теоретически.
Реализуйте std:vector на С
Chaos_Optima
14.07.2022 12:12Ну что вам мешает написать на Си функцию на подобии этой?
struct IntArray arrr = zip(some_array, &zip_func);
Чем не декларативный стиль?
Kelbon Автор
14.07.2022 12:17-1мешает внезапно язык С, который такое абсолютно неприемлет, плохо оптимизирует и написание этого(да и использование) становится практически невозможным.
Сравните производительность и интерфейс сишной сортировки и С++
Chaos_Optima
14.07.2022 13:17А чем конкретно он мешает? Не скомпилится или что?
Сравните производительность и интерфейс сишной сортировки и С++
Сравнил cpp.sh/43drd С++ получился быстрее. Только не понимаю к чему это?
Kelbon Автор
13.07.2022 22:45-8посмотрите на алгоритмы стл
Chaos_Optima
14.07.2022 06:07То есть для вас вызов готовых функций допустим из range уже является декларативным подходом, впринципе с если смотреть с этой стороны то любой язык может быть декларативным. Какие декларативные решения имеются на уровне синтаксиса языка? Интересно было бы увидеть.
Kelbon Автор
14.07.2022 06:43-4Эм, для вас использование готового языка программирования является декларативным подходом? Что за бред? Чем отличается "использование библиотеки" от использования языка в этом контексте?
С++ позволяет писать свои абстракции, в этом его главное преимущество. Почитайте что такое декларативность вообще, непонятно причём тут синтаксис языка.
Вот вам из синтаксиса С++ декларативность : template, ... (pack expanding), range based for loop
0xd34df00d
14.07.2022 06:53+6Вот вам из синтаксиса С++ декларативность: template,… (pack expanding), range based for loop
Что из этого декларативно?
template
— это такой закос под параметрический полиморфизм с элементами паттерн-матчинга. range-based for loop — это практически целиком сахар поверх вызоваbegin
иend
. pack expansion — это вообще костыль для очень неудобной обработки списков параметров.Kelbon Автор
14.07.2022 07:05-8Хаскель - это практически целиком сахар поверх вызова машинного кода. <Любая фича> такой вообще костыль для очень неудобного написания кода в С стиле
0xd34df00d
14.07.2022 07:46+2Хаскель — это практически целиком сахар поверх вызова машинного кода.
В чём принципиальное отличие range-based for от
BOOST_FOREACH
/Q_FOREACH
?Но да, хаскель декларативным языком я бы не называл. Его прелесть не в этом.
0xd34df00d
14.07.2022 05:38+3Лично я вообще не знаю, что такое декларативный подход, несмотря на десяток лет хаскелирования за плечами. Может, это оно в прологе есть, или в задачах смт-солверу, но это не мейнстримные штуки совсем.
Chaos_Optima
14.07.2022 06:20Ну полноценно декларативными языками можно назвать наверно html и sql всё остальное по мне выглядит императивным кодом, конечно можно сказать что вот например map это отношение данных и результата и это является декларативным стилем, но по мне это просто вызов готовой функции по сути инструкция.
0xd34df00d
14.07.2022 06:30+2html не оч яп :]
sql — ну, навскидку, я могу чисто синтаксически преобразовать некое его подмножество в условный хаскель.
конечно можно сказать что вот например map это отношение данных и результата и это является декларативным стилем, но по мне это просто вызов готовой функции по сути инструкция.
Именно, я тут с вами полностью согласен. И
map
ничем не отличается от плюсовогоstd::transform
, например.DabjeilQutwyngo
14.07.2022 09:50-2html не оч яп :]
hypertext markup language: программируется разметка, исполняется движком браузера как вычисление свойств всех элементов визуального представления. Просто потому что язык вообще -- это средство передачи информации, представленной значением выражения на этом языке. А как её интерпретирует получатель и что делает в связи с этим -- вопрос семантики и прагматики соответственно.
Людей вон тоже прекрасно программируют, используя естественный язык и механизм веры. Принцип его работы ничем не отличается от результата работы программатора: происходит подстановка программируемым объектом чего-либо на место значения выражения (в частности, термина или обозначения), которая не зависит от информационной связи с описываемым объектом.
Если со значением связать последовательность действий, у программируемого возникает некоторое поведение. И если записать в журнал последовательность действий, выполненных человеком за некоторый период времени в фиксированном контексте, получится программа этих действий, по определению. Потому что системные законы действуют неспецифично, а возникающее поведение не обязательно имеет программное описание, что никак не отменяет сказанное.
Ivanshka
14.07.2022 12:15+3программируется разметка
Разметка не программируется, а просто описывается. Именно потому HTML и не язык программирования, а язык разметки.
DabjeilQutwyngo
14.07.2022 22:18Тогда отсылаю вас к изучению понятия "программа" и производного от него понятия "программирование", и лучше в англоязычной литературе. Существенным признаком программы, как описания чего-либо, является определение исполняющего устройства, которое по этому описанию будет менять своё состояние согласно коду программы. Никаких ограничений на формы и способы выражения программы, способы представления вообще, как и реализации исполняющего устройства, не накладывается. План выполнения чего-либо или сценарий тоже являются программами.
0xd34df00d
14.07.2022 16:29+3hypertext markup language: программируется разметка, исполняется движком браузера как вычисление свойств всех элементов визуального представления
Там нет программирования в классическом околотьюринговом смысле.
Людей вон тоже прекрасно программируют, используя естественный язык и механизм веры.
На хабре объявили неделю игр с многозначными терминами, что ли? Позавчера вон в соседнем треде товарищ всё находил диалектические противоречия в том, что переменные — переменные, теперь у вас программирование людей почему-то идёт следом за языком программирования html.
DabjeilQutwyngo
14.07.2022 22:29То, что вы исключили что-либо из объёма понятия "программа", и, как следствие, "программирование", извменив тем самым содержание понятия (т.к. для вашего понятия требуется иное характеристическое свойство), и в результате термин стал для вас многозначный, не означает, что термин многозначный: это вы его так понимаете, проводя ложное разделение, как минимум. См. выше объяснение такому же товарищу.
Ваша общая ошибка в том, что считаете программой лишь то, что выражено в императивном стиле. Если выразить в командах рисования графических объектов то, что показывается на экране в результате рендеринга html-кода, то получится именно то, что вы называете программой. Хотя программа -- это предписание исполняющему устройству. А программирование -- это формирование такого предписания.
Ни форма предписания, ни способы его представления и выражения никак не ограничиваются данным принципом. И никаких ограничений на множество объектов, выполняющих роль исполняющего устройства, тоже не накладывается данным принципом. Все ограничения возникают из последующих выборов способа представления и способа реализации. Поэтому не нужно путать эти ограничения с сущностью программы и программирования.
Cerberuser
15.07.2022 05:21+1https://esolangs.org/wiki/Piet (либо его аналог, если нужно что-то, к примеру, с большим количеством цветов)? That is, она может не являться программой сама по себе, но она может быть интерпретирована как таковая (а без способа интерпретации и код на C, и bare-metal бинарник тоже программами являться не будут).
0xd34df00d
15.07.2022 17:42В принципе, так-то и последовательность камешков, которыми выстлана дорожка к двери в мой дом, является программой.
arTk_ev
15.07.2022 12:34Программа - это строго линейный набор инструкций для машины тьюринга. Это определения для императивных языков. Программирование в первую очередь, связана с декомпозицией до состояния этого списка команд.
Машина состояний не является тьюринг полной, поэтому не является полноценной программой.
Для функционального и асинхронного программирования, программа - это уже не набор инструкций, но все равно тьюринг полное.
0xd34df00d
15.07.2022 17:44Настолько строгим я бы всё-таки не был, потому что есть интересные языки, которые вы вполне назовёте языками программирования, но на которых программы всегда завершаются (и, следовательно, они не тьюринг-полны).
pfffffffffffff
14.07.2022 08:08+1Пролог как раз декларативно логический язык. Что выглядит декларативно так это описание интерфейса в vuejs.
DabjeilQutwyngo
14.07.2022 09:58-1что такое декларативный подход
boost::xpressive::sregex
, например.Jian
14.07.2022 10:47что такое декларативный подход
boost::xpressive::sregex
, например.Нет, это объектно-ориентированный подход, и он как для декларативности, так и для императивности - перпендикулярен, а не противоположен. Объектами возможно оперировать как в декларативном, так и в императивном стиле.
DabjeilQutwyngo
14.07.2022 21:21То, что нечто реализовано посредством ООП, не означает, что на уровне использования не декларативный подход. А он декларативный: описывается выражение на
sregex
(производный специализированный доменный язык (Domain Specific Language) от PerlRe и синтаксиса C++), но при этом никаких шагов по его выполнению не предписывается. Пример:sregex re_year = bos >> repeat<4,4>(_d) >> eos;
А шагами выполнения, напомню, является выполнение переходов конечным автоматом, проверяющим принадлежность заданной цепочки регулярному языку, заданному регулярным выражением.
0xd34df00d
14.07.2022 16:31+2А что там декларативного?
Является ли, к слову о регулярках, декларативным это? На мой взгляд — нет.
DabjeilQutwyngo
14.07.2022 21:23По определению проверять нужно, а не по взгляду. В регулярном выражении никаких шагов по его выполнению не предписывается. А шагами выполнения, напомню, является выполнение переходов конечным автоматом, проверяющим принадлежность заданной цепочки регулярному языку, заданному регулярным выражением. Пример выше.
0xd34df00d
14.07.2022 22:36А, то есть, декларативна не библиотека, а сами регулярки, включая какой-нибудь
std::regex
и вполне себе сишный PCRE?
eao197
14.07.2022 07:43+3Пожалуй, не буду скромным и приведу пример из своей же статьи двухлетней давности:
auto make_parser() { auto token_to_v = []( std::string v ) -> param_value_t { return { std::move(v), value_form_t::token }; }; auto qstring_to_v = []( std::string v ) -> param_value_t { return { std::move(v), value_form_t::quoted_string }; }; auto token68_seq = sequence( token68_p() >> as_result(), not_clause( any_symbol_p() >> skip() ) ); // Список параметров вида name=value может быть пустым. auto params_seq = maybe_empty_comma_separated_list_p< param_container_t >( produce< param_t >( token_p() >> to_lower() >> ¶m_t::name, ows(), symbol('='), ows(), produce< param_value_t >( alternatives( token_p() >> convert( token_to_v ) >> as_result(), quoted_string_p() >> convert( qstring_to_v ) >> as_result() ) ) >> ¶m_t::value ) ) >> as_result(); return produce< authorization_value_t >( token_p() >> to_lower() >> &authorization_value_t::auth_scheme, maybe( repeat( 1, N, space() ), produce< auth_param_t >( alternatives( token68_seq, params_seq ) ) >> &authorization_value_t::auth_param ) ); }
Код не для статьи написан, он реально живет в нашей библиотеке.
Chaos_Optima
14.07.2022 12:33+1Это функциональный подход, только причём тут декларативность? Тут ясень порядок выполнения инструкций, он чётко определён вами. Если бы написали что-нибудь в духе
auto value = group | select| {value_form_t::token, value_form_t::quoted_string} | from | some_string;
И компилятор бы сам решал в каком порядке всё выполнять и как разбивать это можно было бы назвать декларативным программированием.
eao197
14.07.2022 12:40-2Это функциональный подход, только причём тут декларативность?
При том, что здесь декларативное описание парсера, который затем когда-то будет работать.
Тут ясень порядок выполнения инструкций, он чётко определён вами.
Он определен не мной, а грамматикой. Или, по вашему мнению, EBNF нотации не декларативны?
0xd34df00d
14.07.2022 16:33По (e)BNF можно чисто синтаксически построить императивный парсер.
Выглядит разумным утверждение, что языки, между которыми существует чисто синтаксическое преобразование, должны принадлежать одному классу по декларативности/императивности/етц.
eao197
14.07.2022 16:40По (e)BNF можно чисто синтаксически построить императивный парсер.
Само описание EBNF что-то говорит о том, какой это будет парсер: нисходящий или восходящий?
0xd34df00d
14.07.2022 16:45Для некоторого класса языков это неважно, для другого класса языков только восходящий (ЕМНИП, я-то обычно вообще PEG'и пишу, и мне пофиг) сработает.
DabjeilQutwyngo
14.07.2022 21:59Ну так парсер и язык -- не одно и тоже: это разные уровни и разные системы. Более того, парсера мало: для получения поведения требуется интерпретатор, как минимум. Путаете уровень интерфейса с уровнем реализации опять.
Парсер можно реализовывать кустарно ("что вижу, то пою") -- в императивном стиле. Т.е. выражать обработку комбинаций элементов языка, встретившихся в анализируемой цепочке, процедурно, непосредственно заданными шагами. А можно через построение отображения на основе грамматики языка и таблицы трансляции ситуаций, определённых комбинациями правил грамматики языка, в требуемое (символы и цепочки другого языка).
В случае компилятора этими цепочками являются команды ассемблера, в частности, представляемые потоком байт. В процессоре есть дешифратор, который поток байт интерпретирует в команды, реализуемые его микропрограммами. А они, в конечном счёте, представлены аппаратной системой из регистров, триггеров и TTL-ключей, выстроенных и увязанных таким образом, чтобы с каждым тактом происходили требуемые переходы.
Такими переходами являются изменение физического состояния электронного компонента, представленное уровнем напряжения, электрического или магнитного заряда. Возможны и другие способы представления, например, уровнем давления (см. про вычислительное устройство, реализованное гидравлически). В итоге получается, что процессор бегает по предустановленным таблицам отношений и производит при этом цепочки на выходном языке, т.к. иное невозможно. Вот и вся магия "вычисления".
Даже банальная последовательность цифр числа в арабской записи -- не что иное, как краткая запись суммы чисел, обозначенных цифрами, умноженными на степень основания выбранной системы счисления. Показатель этой степени поставлен в однооднозначное соответствие порядковому номеру разряда числа (нумеровать можно с разным смещением и направлением). Эта сумма считается неприводимой к более компактной в рамках выбранного языка.
Сложность и методы таблицы трансляции не ограничиваются. Фактически, всё время имеем дело с синтаксически управляемым переводом. И в какой-то момент символами и терминалами становятся действия исполняющего устройства. Т.е. язык -- это средство описания варианта поведения исполняющего устройства. Понятия декларативности и императивности применяются к языку и выражениям на нём, а не исполняющему устройству.
Всё это про архитектуру ЭВМ, теорию множеств и отношений, формальных алгебр, теорию формальных языков, синтаксического анализа и компиляции, являющееся частью фундаментального образования в ИТ.
0xd34df00d
14.07.2022 22:44Ну так парсер и язык — не одно и тоже: это разные уровни и разные системы. Более того, парсера мало: для получения поведения требуется интерпретатор, как минимум.
Формальные языки в информатике — это именно что чистый синтаксис, без какого-либо поведения, интерпретатора и так далее.
И никто не мешает задать формальный язык соответствующим парсером. Просто обычно язык описания парсеров слишком выразительный, чтобы рассуждать о свойствах получившегося парсера, но это другой вопрос.
Т.е. выражать обработку комбинаций элементов языка, встретившихся в анализируемой цепочке, процедурно, непосредственно заданными шагами.
Это императивный стиль?
А можно через построение отображения на основе грамматики языка и таблицы трансляции ситуаций, определённых комбинациями правил грамматики языка, в требуемое (символы и цепочки другого языка).
Вы ведь знаете, что таблицы трансляций [конечного числа] состояний недостаточно для существенных классов языков? Ну там, фундаментальное образование в ИТ, все дела?
antonkrechetov
14.07.2022 21:43А может вы приведёте пример декларативного кода на С++?
Я попробую. Вот код для вычисления N-го числа Фибоначчи на этапе компиляции:
По-моему, вполне декларативно) Конечно, для вывода результата понадобится императивный код, например std::cout << fib<6>::value (выведет 13).typedef unsigned long int num; template <num N> class fib { public: static const num value = fib<N-1>::value + fib<N-2>::value; }; template <> class fib<0> { public: static const num value = 1; }; template <> class fib<1> { public: static const num value = 1; };
Если что, я не настоящий С++-разработчик, так что качество кода может хромать и т.д.
funca
13.07.2022 22:11+3Потому что код обжа сначала транслируется в чистый С, а потом компилится уже компилятором С
Многие языки умеют транслироваться в С. В этом случае С выполняет роль этакого макроассемблера - компиляторы С есть практически под любую платформу и не надо мудрить со своим. Так что наличие этой фичи самой по себе вовсе не аргумент, чтобы считать одно подмножеством другого.
domix32
13.07.2022 22:35+6В их компиляторах ни одной общей строчки
Супер спорное утверждение. При желании я думаю довольно просто можно будет найти пару одинаковых строчек, но в то же время существует высокая вероятность, что два компилятора одного и того же языка под капотом не найдут одинаковых строчек в по крайней мере на тех же этапах обработки кода.
kin4stat
15.07.2022 09:52+1Фронтенд LLVM, например, у обоих будет разных. Но Middleend и Backend одинаковый. Ни одной строчки это конечно очень сильно было сказано
Icon0clast
13.07.2022 22:24+10C не есть подмножество C++ как минимум из-за: restrict указателей, _Generic макросов и _Noreturn функций. Кроме этого, описанный вами миф развенчивал ещё Страуструп в своей книжке, почитайте его на досуге.
F0iL
13.07.2022 22:55Из того что обсуждали недавно тут, в копилку о том, почему C не есть подмножество C++: в Си type-puning через union'ы вполне допустим и повсеместно используется, в C++ же он является нарушением стандарта.
Nick_Shl
14.07.2022 04:42C это подмножество C++
Нет! Наоборот: С++ это "надстройка" над С.
IKStantin
14.07.2022 14:24+1Когда я работал преподавателем программирования на УПК в 1992 году, именно так мы и преподносили.
C++ - расширение языка C для работы с классами. И ООП мы изучали на библиотеке Turbo Vision. Была такая в Borland C++ для работы с псевдографическими окнами в текстовом режиме DOS.
Jian
14.07.2022 14:28+1С++ это "надстройка" над С
Первоначально, когда С++ только появился, всё так и было. Но, затем, с годами, особенно с появлением шаблонов, C++ начал всё дальше отдаляться от C.
agalakhov
14.07.2022 21:27Начиная с C++11 С++ перестал быть надмножеством C, так как изменился смысл некоторых конструкций. В частности, ключевого слова auto. Начиная с C++11 можно написать корректную программу на C, которая не будет компилироваться с ошибкой компилятором C++.
QtRoS
15.07.2022 12:20Давно уже можно такое написать, смотрите мой комментарий. Минимум начиная с 98 стандарта.
"or" is an inbuilt keyword that has been around since at least C++98
tsilia
15.07.2022 09:49Что-то никто в этой ветке не упоминает
VLA
как одну из фич C, которых нет в C++. Или его уже в стандарт плюсов завезли? Когда чекал это последний раз, g++ поддерживал его только как расширение стандарта C++.
Racheengel
13.07.2022 20:36+27Обычно поиск программиста на С/С++ значит, что на фирме есть и код на С, и код на С++, а скорее всего даже внутри одного продукта. И бедняге придётся окунаться в оба болота сразу.
staticmain
13.07.2022 21:43+18Или когда весь код на С++, но писали его программисты на С с malloc, free, функциями из cstd хедеров.
PkXwmpgN
13.07.2022 22:42+8Я встречал немного другой взгляд на требование C/C++. Все что перечислено в статье
шаблоны, алгоритмы, итераторы, ренжи...
- это все высокоуровневые абстракции. Помимо этого важно понимать, как из этих высокоуровневых абстракций в итоге получается машинный код. Как они реализованы и чего стоят. Возможно это кого-то удивит, но очень много программистов, у которых в резюме написан опыт 3+ лет на C++, не знают низкоуровневых вещей, например, многих ставит в тупик вопрос про выравнивание данных (здесь конечно можно заметить что "настоящему" программисту на С++, который пишет на "хорошем", "декларативном" С++ эти знания ни к чему - это тот же холивар, что и про алгоритмы). При этом я не встречал ни одного программиста на С, у которого подобные вопросы вызывали бы недоумение. Поэтому иногда когда пишут про C/C++ имеют ввиду знание как раз вот этого низкоуровневого наследия С, на котором построен C++. Но такой взгляд встречается редко, здесь вы правы скорее всего.
victor_1212
13.07.2022 20:46+15> И это не абстрактные рассуждения в вакууме
просто любопытно, что такого автор сделал в программировании чтобы выражаться в стиле "тупыми наборами байт", " глубокий наколеночный эмбед", " языки требуют абсолютно разных умений и подходов к разработке", оба использовал практически с момента появления первых компиляторов, (30+ лет), ничего особенного в С++ vs С не вижу, просто tool, для одних проектов лучше С, для других больше подходит С++, не более того
0xd34df00d
14.07.2022 05:40+6Спердоб — это, если честно, так себе аргумент. Особенно когда в роли контр-спердоба выступает «да я 30 лет пишу».
victor_1212
14.07.2022 20:14+1Вы не совсем поняли, предмета спора "кто больше знает" нет, вопрос к терминологии автора, профессионалы так не пишут, даже если он эксперт и сидел в офисе рядом с Страуструпом, остальное слегка Ваши фантазии
0xd34df00d
14.07.2022 22:45Вы не совсем поняли, предмета спора "кто больше знает" нет, вопрос к терминологии автора, профессионалы так не пишут
Профессионалы как только не пишут. Не надо так сильно молиться на серьёзность.
даже если он эксперт и сидел в офисе рядом с Страуструпом
Сомнительный критерий экспертности, ну да ладно.
остальное слегка Ваши фантазии
Формулировка
оба использовал практически с момента появления первых компиляторов, (30+ лет) [...]
не оставляет особого простора для фантазий.
victor_1212
14.07.2022 23:59тем не менее нашли "спердоб", какие у Вас вопросы к "30+ лет"?
если нет, предлагаю закончить, не в возрасте дело, адреналина от демонстрации технических знаний не получал никогда, в том числе когда было столько лет, сколько Вам сейчас, типа более интересные занятия были
0xd34df00d
15.07.2022 17:46Я иначе не очень понимаю, к чему там эти 30+ лет были, только и всего. Ровно ту же мысль, которую вы написали, можно передать и без упоминания личного опыта в 30+ лет.
Но, впрочем, не имели в виду спердоб — ну и славно.
в том числе когда было столько лет, сколько Вам сейчас
:]
victor_1212
15.07.2022 21:24нет проблем, " 30+" в общем случайно, приходится задумываться иногда, может поймете когда-нибудь :)
Koval97
15.07.2022 13:04-2Согласен. Сам таким подходом, как вы описали выше, отличаюсь от коллег. Никогда не понимал этого дрочева на "в стандарте же явно не оговоренно", но и не запрещено же. Да иногда оговариваются, что "будьте внимательным к данным, тут могут быть неприятные побочные эффекты" - так по моему опыту, пусть не выдающемуся, когда ты вступаешь на тропу С, а следом движешься к С++, то внимание к данным становится настолько крепкой привычкой, что эти холливары уже не имеют никакого значения - с такого уровня всегда ясно в чем суть вещей.
victor_1212
15.07.2022 14:54примерно понимаю, лопата придумана чтобы траншеи копать и каналы строить, конечно документацию на лопату тоже надо знать, включая стандарт как reference, и особенности компилятора, imho особенно гордиться здесь не чем, в конце концов стандарт люди создали, и компилятор как правило кто-то другой написал, типа чужая работа, лучше что-нибудь свое как следует сделать, пусть даже чужой лопатой :)
victor_1212
15.07.2022 16:28вероятно наступил кому-нибудь на лапу :)
через 20 мин это сообщение принесло -1 в карму, без разницы конечно, посмотрим что дальше будет
Magister-Ice
13.07.2022 20:51+1Исходя из изложенного материала, например, программа написанная на чистом C с использованием API DirectX, является C или C++ программой?
Chaos_Optima
13.07.2022 21:25+1А как ты на С напишешь с использованием DirectX там же ооп апи, или я что-то упустил.
Magister-Ice
13.07.2022 21:35+1Да, получится гибридное решение, C код с созданием и использоваине объектов из API. ИМХО в итоге получится некий гибрид C/C++, котрый не C, не C++, вполне жизнеспособен и ничему не противоречит.
tangro
13.07.2022 22:17+5Чтоб Вы были в курсе: DirectX имеет два набора API: для С и для С++. Это вот прямо разные заголовочные файлы или отделённые через ifdef секции в одном заголовочном файле. DirectX изначально разрабатывался как высокопродуктивная графическая подсистема и её, конечно, нельзя было намертво прикрутить к одному лишь С++ и забыть С.
Chaos_Optima
14.07.2022 06:11+1Да действительно посмотрел щас d3d12.h и правда, был не прав С api присутствует
shybovycha
15.07.2022 01:24Всегда как смотрел на DirectX воспринимал его как такой сильный C way - когда объявляется дескриптор, который struct с десятком полей, а потом на нем вызывается функция (даже если как метод класса). В моем понимании C++ way это ООП, потому я ожидал бы какой-нибудь builder (как шаблон проектирования). Но это все философия, я так понимаю это либо пережиток прошлого, либо решение в пользу производительности. Вроде тот же Vulkan имеет точно такой же подход, так что скорее второе.
r6l-025
13.07.2022 21:01+7Я подозреваю что когда пишет не C++, а C/C++ - то хотят видеть человека умеющего работать с обоими парадигмами. Хотя, иногда попадаются очень забавные объявление от HR котрые их не всегда различают: " Хотим знание Си и boost". А вот на счет наколеночного embedded я бы поспорил (иначе Linux kernel, Postgresqsl, etc. превращаются в наколеночный embedded)
Kelbon Автор
13.07.2022 21:01-21У линукса нет объективных причин не использовать С++. Но есть Линус, который по личным каким то предпочтениям не хочет
lorc
13.07.2022 22:56+17А какой профит принесет C++ ядру линукса? Появится 11 разных способов инициализировать поле класса? Как там со стабильным ABI? В ядре периодически то ассемблерный код дергает сишные функции, то сишный код дергает ассемблер. И модули загружаются/выгружаются прямо в рантайме.
А если вы расскажете как делать всякие более продвинутые штуки типа динамической трассировки C++ кода (как ftrace и kprobe в ядре) - будет вообще круто.
Kelbon Автор
14.07.2022 07:12+1Появится 11 разных способов инициализировать поле класса?
Появится способ создавать классы. Сейчас там это эмулируется через ужас и ужас. С потерей производительности, ужасным кодом и макросами
Как там со стабильным ABI
Как-то коммитету по стандартизации С++ получается держать стабильное ABI, про extern "C" промолчу
И модули загружаются/выгружаются прямо в рантайме.
и чё? Как это мешает то?
А если вы расскажете как делать всякие более продвинутые штуки типа динамической трассировки C++ кода (как ftrace и kprobe в ядре) - будет вообще круто.
Нет никаких оснований говорить, что на С++ этого не сделать. Лучше покажите как на С сделать "класс" не выделяя его в куче
Ну или как прочитать строку, пришедшую в мейн с неизвестным размером
Cerberuser
14.07.2022 08:18+1Появится способ создавать классы.
Но зачем? Какую задачу это будет решать с точки зрения ядра? А то фраза звучит так, как будто наличие такой задачи заведомо очевидно.
Kelbon Автор
14.07.2022 08:32Если там на языке С эмулируют это, то очевидно потребность есть
mikhanoid
14.07.2022 10:19+16Там не классы эмулируют, а интерфейсы. А для этого C++ - overkill. C++ сложный язык, создающий зависимость от разработчиков компиляторов для C++, от комитета стандартизации C++ и от многолетней учёбы, необходимой для освоения C++ и от прочих слциальных институтов. Linux, он, как бы, не про это, а про индивидуальную свободу и независимость. Сносный компилятор C можно написать с нуля в одиночку за приемлемое время и раскрутить на этом компиляторе GNU/Linux. Написание компилятора C++ современного стандарта - это отдельное приключение длинною в десятилетие.
F0iL
14.07.2022 12:15+3Сносный компилятор C можно написать с нуля в одиночку за приемлемое время и раскрутить на этом компиляторе GNU/Linux.
Ага, ага. Достаточно вспомнить, что спустя три года активной разработки Clang все еще не мог полностью собрать линуксовое ядро, и даже спустя еще 7 лет после этого ядро нормально собиралось им только со специальными патчами. TCC может собирать только очень древние версии ядра (2.4.x), и даже навороченный интеловский ICC собирал ядро тоже только при наложении специальных патчей, а сейчас они вообще на это дело забили.
lorc
14.07.2022 12:52+1Некорректный пример, потому что линуксовое ядро прямо заточено под сборку на GCC. Там используется куча gcc-шних расширений где только можно. Вообще, говорить что ядро линукса написано на C - не совсем верно.
В то же время есть приложения которые собираются хоть gcc, хоть icc, хоть Keil C.
F0iL
14.07.2022 15:24+3Что значит "некорректный пример"? Перечитайте исходный комментарий выше, там человек говорил именно про компиляцию линуксового ядра, поэтому я и отвечаю про компиляцию линуксового ядра.
Gordon01
14.07.2022 13:29Там только создают объекты из структур, без наследования и прочей ООП чепухи.
На си это нормально получается, хотя в каком-нибудь расте еще красивее.
r6l-025
14.07.2022 10:11+1А чем вам не понравились классы в ядре? Их там не много, и они весьма в удачных местах. Например, класс с файловыми операциями. А на счет производительности... нужны бенчмарки. Как-то интуитивно выглядит как раз наоборот. В Си это просто разыменование указателей для вызова функций. А в плюсах?
Kelbon Автор
14.07.2022 10:20-3интуитивно создавать все объекты на куче потому что язык не позволяет сделать это на стеке это не эффективно. И разыменовывать каждый раз указатель чтобы пойти к значению это тоже неэффективно. Зачем вообще рассуждать об эффективности, если вы не понимаете как методы в плюсах вызываются?
lorc
14.07.2022 10:53+4интуитивно создавать все объекты на куче потому что язык не позволяет сделать это на стеке это не эффективно.
В каком месте C не позволяет создавать объекты на стеке? О чем вы вообще? К тому же, у всех линуксовых объектов очень длинное время жизни, их нет смысла создавать на стеке.
И разыменовывать каждый раз указатель чтобы пойти к значению это тоже неэффективно
А как вы собираетесь вызывать виртуальные методы? В C++ точно то же самое - полезли в vtable, нашли указатель на метод.
Зачем вообще рассуждать об эффективности, если вы не понимаете как методы в плюсах вызываются?
О, а вы можете рассказать как вызываются методы в плюсах? Вот например я хочу вызвать C++ метод из ассемблерного кода. Как мне это сделать? (вопрос с подвохом, да).
Kelbon Автор
14.07.2022 10:59-3В С эмулируют инкапсуляцию используя pimpl повсеместно, что не даёт возможности создавать на стеке. И там же образуется лишний дереференс при доступе к объекту
Виртуальные таблицы в С++ хотя бы не создаются вручную, что уже громадное преимущество.
Из ассемблера вызывайте че хотите как хотите, мне это не нужно
lorc
14.07.2022 11:02+3В С эмулируют инкапсуляцию используя pimpl повсеместно, что не даёт возможности создавать на стеке.
Почему? Я не могу создать структуру на стеке? Или о чем вы?
Из ассемблера вызывайте че хотите как хотите, мне это не нужно
Ядру линукса это нужно. Мы же тут говорим о том, что линукс мог бы быть написать на C++, не так ли? Вот ваши слова:
У линукса нет объективных причин не использовать С++.
Раз уж нет объективных причин не использовать, то расскажите про interop с ассемблером.
Kelbon Автор
14.07.2022 11:06-6Почему? Я не могу создать структуру на стеке? Или о чем вы?
Когда нибудь вы поймёте
Мы же тут говорим о том, что линукс мог бы быть написать на C++, не так ли? Вот ваши слова:
Где я это сказал? Откуда вы это взяли вообще? Мои слова, это то что у Линуса Торвальдса нет никаких реальных оснований не допускать С++ в линукс.
lorc
14.07.2022 11:17+2Когда нибудь вы поймёте
Это не ответ, а риторический прием. При чем - плохой риторический прием. Расскажите мне что именно C не позволяет создавать на стеке и чем это плохо. Или покажите где почитать об этом.
Мои слова, это то что у Линуса Торвальдса нет никаких реальных оснований не допускать С++ в линукс.
Может все таки есть? Вот например - отсутствие стандартного ABI и проблемы взаимодействия с низкоуровневым кодом из-за этого.
eao197
14.07.2022 12:08+1Давайте возьмем простенький пример: https://wandbox.org/permlink/SCa50brKPVZfghMF
Интересно посмотреть на объем кода на чистом Си, который бы делал бы тоже самое.
lorc
14.07.2022 12:38+1Да легко: https://wandbox.org/permlink/wKY1HxU9zj1G1cHY
Сильно объем кода отличается?
lorc
14.07.2022 12:47Я рад за него. Только это ж банальный геттер который возвращает константу. Не проще тогда хранить сразу константу? Ах да, наследование в C++ не позволяет переопределять значения полей... Поэтому вариант C получился быстрее даже - минус один вызов функции/метода.
Вообще из всего разнообразия фич на C++ вы выбрали самый примитивный пример, для которого собственно C++ не очень то и нужен.
eao197
14.07.2022 12:52+1Только это ж банальный геттер который возвращает константу.
Кто вам сказал? Метод в базовом классе объявлен как виртуальный.
Ах да, наследование в C++ не позволяет переопределять значения полей...
Это вы сейчас о чем?
Вообще из всего разнообразия фич на C++ вы выбрали самый примитивный пример, для которого собственно C++ не очень то и нужен.
Этот примитивнейший пример показывать насколько хрупким будет аналог на чистом Си, в котором все гарантии на уровне "мамой клянусь".
Да и тот повторить не смогли 1-в-1.
lorc
14.07.2022 13:07Кто вам сказал? Метод в базовом классе объявлен как виртуальный.
Ну по факту он возвращает константную строку. Ведет себя как классический геттер (без сеттера, да).
Это вы сейчас о чем?
Именно о том, что в C++ у вас по другому не получится переопределить значение свойства при наследовании. Только вводить метод-геттер, что вы и сделали.
Да и тот повторить не смогли 1-в-1.
А была цель повторить 1:1? Вы просили код
который бы делал бы тоже самое.
Я привел код который делает то же самое. Предоставляет интерфейс Animal который позволяет узнать имя конкретного класса и вызвать метод.
eao197
14.07.2022 13:15Ну по факту он возвращает константную строку.
Если уж говорить про факты, то
a) возвращается динамическая строка;
b) функция use_animal завязана на то, чтобы вызывать name у наследников и этот name может иметь разные реализации.
Ничего подобного у вас в коде нет. Хотя докапываться до `const char*` вместо
std::string
у меня желания и не было. Хотелось просто увидеть в коде ручное проставление нескольких указателей на "виртуальные" методы, но это оказалось слишкамсложна.Именно о том, что в C++ у вас по другому не получится переопределить значение свойства при наследовании.
Давайте еще раз и так, чтобы было понятно не только вам. А то складывается впечатление, что C++ вы и не знаете.
А была цель повторить 1:1? Вы просили код
Который бы делал тоже самое. Тоже самое. А не то, что вам оказалось проще.
Я привел код который делает то же самое.
Нет.
Kelbon Автор
14.07.2022 13:17-1Для сишников нет понятия расширяемость кода, интерфейс, у них есть аналогичный порядок байт и всё. А что с этим делать потом - не их дело же
lorc
14.07.2022 13:19Который бы делал тоже самое. Тоже самое. А не то, что вам оказалось проще.
Очевидно, что если взять язык B и попросить сделать на нем "точно то же самое" что было сделано на языке А, то получится дикая каша. Потому что это разные языки.
Я утверждаю что моя программа семантически эквивалентна вашей.
Нет.
Okay...
Kelbon Автор
14.07.2022 13:21-1Почему то на С++ можно сделать то же самое что на С, а вот на С то же самое что на С++ не получается, интересно...
lorc
14.07.2022 13:29+2Сделайте мне на C++
per_cpu
переменную как в ядре линукса. И нет, это не thread local storage, это именно per CPU.
eao197
14.07.2022 13:22Я утверждаю что моя программа семантически эквивалентна вашей.
Вот только элементарных проверок ваше утверждение не проходит.
lorc
14.07.2022 13:31Нужно было точнее ставить задачу. Или хотя бы привести список "проверок".
Не все обладают телепатией.
eao197
14.07.2022 13:50Для знающего C++ там все очевидно:
есть интерфейс;
в этом интерфейсе есть две виртуальные функции. Причем виртуальную функцию
action
нельзя применять к константному объекту (указателю/ссылке);оба эти функции являются чистыми виртуальными. Т.е. компилятор не даст нам создать экземпляр типа, в котором не реализована хотя бы одна из них;
есть функция
use_animal
, которая завязана на этот интерфейс;есть два наследника, которые реализуют этот интерфейс;
наследники создаются на стеке, но передаются в
use_animal
, при этомuse_animal
ничего не знает о наследниках.
Я тут даже не буду заострять внимание на то, что
name
возвращает созданную в динамической памяти строку. И на то, что наследниковAnimal
можно безопасно удалять по указателю на самAnimal
. Т.к. к вопросу о создании экземпляров на стеке это не имеет отношения.
eao197
14.07.2022 13:20Я привел код который делает то же самое
Еще одно важное отличие забыл вписать: у вас нет отдельных типов для Cat и Dog. Т.е. мой пример можно расширить условной функцией:
void walk_dog(const Dog & d);
И в эту функцию нельзя будет просто так передать Cat. А у вас вообще понятия Dog нет.
Kelbon Автор
14.07.2022 13:07нет не проще, строка в отличие от вашего случая может создаваться на рантайме. И владеющая. И вы забыли деструктор в таблицу.
Вы изменили логику, а не "сделали быстрее", бенчмарки должны быть идентичными.
Но вы лучше вот это прокомментируйте, как вы сделаете лучше на С(никак)
0xd34df00d
14.07.2022 16:43Только это ж банальный геттер который возвращает константу. Не проще тогда хранить сразу константу?
Одна из характеристик интеллекта — это способность разрешать неоднозначности и понимать абстракции.
Если так уж хотите, то рассмотрите такой пример: https://wandbox.org/permlink/Wuz2ahyHUGXb1V74
Ах да, наследование в C++ не позволяет переопределять значения полей...
Не понял.
struct Base { int theAnswer = 42; }; struct Derived : Base { Derived() { theAnswer = 6; } };
lorc
14.07.2022 17:07Одна из характеристик интеллекта — это способность разрешать неоднозначности и понимать абстракции. Если так уж хотите, то рассмотрите такой пример: https://wandbox.org/permlink/Wuz2ahyHUGXb1V74
Хех. Ну окей. Я могу завести отдельную структуру animal_ops и держать там поинтеры на разные функции. Будет совсем такой vtable как хотел ОП.
Ну и да, очевидно что это не так безопасно по типам как было в C++.
Не понял.
О, вот сразу бы так :)
Kelbon Автор
14.07.2022 12:46-1У вас vtable неправильная, переделывайте. Во первых метода не 3 а 1, во вторых не указатель хранится, а сам vtable, ваш код значительно менее эффективен.
lorc
14.07.2022 13:00А мы тут собрались копировать плюсовую имплементацию 1:1 или повторить семантику? Семантика интерфейса повторена, а то что оно ведет себя не как код на C++ - так это и понятно. Оно ж написано не на C++.
Я могу повторить тот же код на Python например. Там вы тоже будете предъявлять претензии к тому что vtable не такой?
Kelbon Автор
14.07.2022 13:02А теперь задачка вам действительно сложная, попробуйте реализовать эффективнее чем понятный и логичный, короткий код на С++, но с использованием другого вида динамического полиморфизма. Там нет никаких виртуальных таблиц внутри типов, они гораздо лучше расположены. И выделений памяти нет.
И эффективнее, понятнее и короче код чем в С
https://godbolt.org/z/6zc5Ync3x
Спойлер - не получится у вас
lorc
14.07.2022 13:15И эффективнее, понятнее и короче код чем в С
Ну если вот это: https://raw.githubusercontent.com/kelbon/AnyAny/main/include/anyany.hpp
"Эффективнее и короче" чем мой код на C, то я даже не знаю...
Kelbon Автор
14.07.2022 13:18-1Ну тогда линуксом не пользуйтесь, там небось тоже много кода
Ну или напишите аналогичную библиотеку на С. ОЙ, у вас не получится .. Потому что это невозможно..
lorc
14.07.2022 13:25Ну смотрите. Вы привели пример, который с помощью дикой шаблонной магии на полторы тысячи строк смог сгенерировать ассемблерный код на 6 инструкций короче чем простая программа на С размером в 50 строк кода.
Это конечно интересный trade off. Есть чем гордиться.
Если я покажу другому программисту на C++ вот этот весь код - как быстро он поймет что там происходит?
Kelbon Автор
14.07.2022 13:29Эта "дикая шаблонная магия" может генерить бесконечное множество подобных кодов. Она для того и создана. Вместо того чтобы писать это руками(никто никогда не сможет писать это руками), она делает эффективный, понятный и компилятору и разработчику код. С её помощью можно не только cat и dog сделать, а всё что угодно.
Потенциально эта машина всего в 1000 строк сгенерирует вам миллиарды строк качественного кода. В этом и смысл.
Если вам нравится раз за разом писать бесполезную херню к тому же абсолютно нерасширяемо и неправильно(тут вам указали на громадные недочёты в вашей vtable), то можете конечно продолжать. А стоило бы уже начать использовать С++
gecube
14.07.2022 14:04Эта "дикая шаблонная магия" может генерить бесконечное множество подобных кодов. Она для того и создана.
ага, только у нее две огромные проблемы. Это криптоошибки при неправильном применении шаблонов и очень долгое время компиляции. Почему какой-то чертов Rust собирает большой проект в 10 раз быстрее, чем средний проект на крестах с шаблонами ? Почему в пайтоне я получаю результат гораздо быстрее, чем в С++ ? Да даже джава - ей не нужна самая современная машина для компиляции кода
Kelbon Автор
14.07.2022 14:25-4Нормальные там ошибки если вы не то пишете
Нормальное время компиляции, С++20 добавляет модули, что значительно ускоряет компиляцию в сравнении с сишными инклудами
Раст компилируется дольше С++, это просто факт.
В питоне вы получаете результат дольше, потому что он работает дольше...
Racheengel
14.07.2022 19:43Вот про "нормальность" и "факт" хотелось бы с доказательством, а то как-то совсем голословно получилось...
Kelbon Автор
14.07.2022 13:31Покажите мне любой сишный код и я укажу, что вы инклудите тонны кода. Но это вас почему то не смущает
Chaos_Optima
14.07.2022 12:43Он имеет ввиду ipmpl структуры размер которых не определён в рамках единиц трансляции где структура не определенна. Но и тут это будет не совсем верно, потому что можно изловчится и зная размер структуры успешно смапить её на память в стеке.
lorc
14.07.2022 10:28+4Появится способ создавать классы. Сейчас там это эмулируется через ужас и ужас. С потерей производительности, ужасным кодом и макросами
И где там потеря производительности? Что вызывать функцию через vtable, что по указателю - один хрен.
Как-то коммитету по стандартизации С++ получается держать стабильное ABI.
Да? Я вообще не встречал описаного C++ ABI. Тем более в стандарте. Насколько я знаю, у каждого компилятора свой ABI. Грубо говоря, вы не сможете статически слинковать вместе объектные файлы сгенерированные gcc и msvc. MSVC не поддерживает стабильное ABI даже между версиями, кстати: https://stackoverflow.com/a/67844737
и чё? Как это мешает то?
Ну их как бы надо динамически линковать. Делать это не имея стабильного ABI - то еще развлечение.
Лучше покажите как на С сделать "класс" не выделяя его в куче
Кто мне мешает выделить структуру на стеке? :)
Более того, я могу выделить структуру (и вообще любой другой объект) на стеке динамически. Благодаря магииalloca
.Ну или как прочитать строку, пришедшую в мейн с неизвестным размером
А в чем проблема?
realloc
никто не отменял.Kelbon Автор
14.07.2022 12:01-4Кто мне мешает выделить структуру на стеке? :)Более того, я могу выделить структуру (и вообще любой другой объект) на стеке динамически. Благодаря магии
alloca
.Это нестандартный С и это ухудшает значительно полученный в итоге ассемблер. Вы ломаете одну из главных гарантий для компилятора, что размер объекта на стеке известен на компиляции всегда.
К тому же код с этим выглядит чрезмерно ужасно
А в чем проблема?
realloc
никто не отменял.вот напишите код и увидите в чём проблема, сколкьо можно талдычить очевидные вещи
lorc
14.07.2022 12:17Это нестандартный С и это ухудшает значительно полученный в итоге ассемблер.
Ну я ж не говорю что это нужно использовать. Вообще ни разу не использовал alloca и не видел чтобы его использовали где-то. Но он есть. И да, ассемблеру x86 например вообще пофиг на такое. Он все равно восстанавливает правильное значение SP при выходе из функции.
вот напишите код и увидите в чём проблема, сколкьо можно талдычить очевидные вещи
Зачем так нервничать? Ну писал я код который использует realloc. Ничего ужасного.
truthfinder
14.07.2022 10:52+2По мнению автора статьи выходит, что весь код ядра линукса UB. А статью на хабре никто не помнит уже, где компилятор плюсов оправдывал стандартом вызов кода, который не должен был вызываться в принципе?
F0iL
14.07.2022 12:20+2Вот эта статья: https://habr.com/ru/company/infopulse/blog/338812/
Правда, при компиляции сишным компилятором безо всяких там плюсов оно ведет себя точно так же, вызывая невызываемую функцию: https://godbolt.org/z/W85MWr47e
dd1911
13.07.2022 21:03+41Я специально зарегистрировался что бы оставить этот комментарий.
Устал слышать этот бред.
Все говорят С это embeded. Вопрос только когда все так решили?
Вот про это мне интересно почитать статью и даже очень.
Когда все переписали на С++, когда С++ начал жить полноценно. Когда многие репозитории переписали те самые 5-10% С кода?
Я начну с низов, много графических библиотек без С? Так или иначе большинство библиотек с которыми я работаю, основываются на С или частично зависят от него.
Skia / SDL / Qt / GTK / ect.. Вы говорить что С++ с UB это не C++ (тогда что?) потому что это огромное число софта, которое все называют C++. Ладно это мелочи.
Но каждый раз я слышу С этот embeded и меня прям трясет. Потому что наверное большую часть софта с которым я работаю, написано на С и С++. Зачастую вместе. Я не понимаю, почему люди так открещиваются от С и говорят это только embeded. Хотя наверное эти люди считают embeded всем? От микроконтроллеров до графических систем вроде OpenGL / Vulkan. GUI, Gamedev, OC, Сервера, прокси, другие языки программирования и так далее можно долго. Зачем я все это написал?
Обидно слышать что С это embeded, а без него... Я бы не имел софта которым пользуюсь.
И пользуются люди по всему миру!
Что раньше появилось курица или яйцо? Да это и не важно, но как мне видится.
Мир без С++ будет сложен, а без С невозможен. Мое личное мнение!Kelbon Автор
13.07.2022 21:19-30Какой смысл сейчас использовать С там, где существует компилятор С++?
omxela
13.07.2022 21:47+9Какой смысл ...
А это смотря что Вы понимаете под "смыслом". Та или иная парадигма, тот или иной язык выбирается не по хайпу (хотя Вы именно за это и топите), а по задаче. В этом смысле структурный подход может оказаться проще и эффективнее любого другого для данного конкретного проекта. А ООП - для другого конкретного проекта. Можно, конечно, делать вид, что компьютера как двоичного автомата не существует. Но я Вас удивлю. Любая, даже самая продвинутая, модель данных - это всего лишь тупой набор байтов. Доказательство - скомпилированный код. Взгляните на досуге. И если Вы действительно хотите отдавать себе отчёт в том, что Вы делаете, то без этих байтиков никак. А уж декларативно или императивно - определяется, повторюсь, задачей, да ещё традициями команды, пожалуй.
0xd34df00d
14.07.2022 06:19+5Вы вполне можете писать на C++ в структурном стиле. Но при этом у вас будут ещё и все фишки плюсов: чуть большая типобезопасность, чуть большая выразительность, RAII какой-нибудь (который вполне может скомпилироваться в тот же код, что и вы руками будете писать на C), чуть больше средств для обобщения вроде шаблонов, чуть больше готовых алгоритмов из STL вроде всяких
std::all_of
(зачем это писать руками?).Я ненавижу плюсы (потому что потратил на их тонкости, совершенно не переносящиеся на любой другой язык или деятельность, этак 17-18 лет жизни из своих 31, включая самые сочные, когда я мог бы ботать матан или, в конце концов, с девочками за ручку ходить) и презираю ООП (потому что, ИМХО, это очень неудачный базис для разложения программ), но не вижу смысла использовать C вместо плюсов, кроме, возможно, трёх случаев:
- Под вашу целевую платформу нет компилятора плюсов, а сей — есть.
- В вашей команде нет плюсистов.
- Вы работаете над сишным легаси.
На сдачу — если вы пишете сишную обвязку над библиотекой на плюсах/хаскеле/етц и используете его в качестве того самого lingua franca. Но C в этом случае не является языком реализации.
Admz
14.07.2022 07:26+1А вы бы могли поделиться с нами на что лучше бы потратили своё время, как личное так и профессиональное. Я думаю что многим, особенно те кто мечтает "войтивайти" за баблом, будет познавательно на что всё-таки тратить свои лучшие годы и энергию. Какой совет вы бы дали себе 20 летнему?
0xd34df00d
14.07.2022 08:07+2От интересов зависит. Я бы, наверное, порекомендовал себе лично тратить больше времени на фундаментальную математику и на сон.
Ну и в любом случае история не терпит сослагательного наклонения, так что не знаю, как бы оно там вышло.
DabjeilQutwyngo
14.07.2022 10:25-1презираю ООП (потому что, ИМХО, это очень неудачный базис для разложения программ)
Так это базис языка и мышления в англоязычной культуре. Более того, видимо, кроме концепций (представленных на начальному уровне классами, а в общем случае шаблонами, посредством которых производятся описания уточнённых концепций), представленных как отношение, и их применения (т.е. выражений из терминов, обозначающих применяемые концепции), иное невозможно, т.к. иначе придётся заниматься подстановкой элементов в формулы, определяющие концепции, и плодить неимоверное количество подобных информационных структур.
Применение теории множеств и отношений, выразившейся в сказанном выше, аналогично применению архивирования (т.е. сжатия и распаковки). Поэтому же в конструкции классов и ассоциаций в UML общим предком является Classifier, а принципиальная разница между ними лишь в отсутствии поведенческих фич у вторых и возможности определения вложенных классов. А для понимания ООП очень полезно изучить таксономию классификаторов UML (производных от Classifier).
alexac
13.07.2022 22:27+10Есть смысл, на самом деле. C - это lingua franca. Когда нужно дружить друг с другом код на разных языках, зачастую все сводится к предоставлению C-интерфейса. И может быть в имплементации этого интерфейса будет уже C++, go, rust, ассемблере или вообще чем-то экзотичном. Но интерфейс будет на C потому что подцепиться к нему можно из максимально широкого круга языков. Писать весь проект на C, мало кто будет, но какие-то части регулярно получаются написанными на C или хотя бы представляют из себя интерфейсы на C.
Kelbon Автор
13.07.2022 22:47extern "C"
Потому что вам нужно не писать на С, а просто не манглить имя функции чтобы её экспортировать
lorc
13.07.2022 22:59+1Потому что на каждой платформе C предоставляет стабильный ABI: как представлять типы в памяти, как вызывать функции, как передавать в эти функции параметры, как возвращать результаты. Никакой другой язык это не делает.
Icon0clast
13.07.2022 22:28+6Это может конечно вкусовщина (да кого я обманываю, так и есть) но C++ достаточно перегружен и в некоторых задачах его мощь и сложность просто ненужны. Кресты иногда сподабливаются пушке по воробьям — дорого, тяжело и глупо.
isadora-6th
14.07.2022 11:19+2Всегда использую std::vector даже в ситуациях, где хватило бы std::array или Сишного[].
Потому что расти проще. Если я положу туда стракты, я уверен, что ничего с моими std::string не произойдет, они не потеряются.
Я могу просто СКОПИРОВАТЬ struct -> struct, без мыслей о том, что блиииин у меня висит char* в двух страктах, где его почистить.Я просто пишу реализацию std::less лямбдочкой, что-бы посортировать этот самый вектор.
Можно очень долго думать про оверхеды. Но будем откровенны, если на каждое место в коде думать про оверхед, код написан не будет.
Нет смысла бороться за каждый такт в коде который вызовется 1-2 раза.Если у тебя есть пушка, то зачем гоняться за воробьями с ножом?
Icon0clast
14.07.2022 19:01+1Вы немного спутали вопрос (не в последнюю очередь благодаря неточности моей речи) одно дело писать небольшие (или любые вообще) программы в которых не предполагается изменение, долгая поддержка и т.д. то бишь утилиты, и совсем другое проекты, которые вы планируете расширять и улучшать. В первом случае есть повод не заморачиваться с абстракциями вроде std::vector и ему подобными, а сделать тупо массив, есть повод не использовать ООП и другие достижения цивилизации.
Совершенно другое дело, писать программы с заделом на будующее. В таком случае использование абстракций вполне оправдано. И C++ оправдан в таком случае.
Говоря языком метафор: вам не нужен отбойный молоток, чтобы забить гвоздь в стену. А коли вы возьмётесь за серьёзное строительство, тогда да, почему бы и не воспользоваться таким громоздким инструментарием.
embedded_bat
14.07.2022 17:57Наверное люди просто ненавидят С++, раз он такой классный во всех отношениях, но всё равно не смог за десятилетия заменить С во всех нишах.
F0iL
14.07.2022 18:11Не надо недооценивать широко распространенный синдром под названием "здесь так принято".
eao197
14.07.2022 20:19+1Наверное люди просто ненавидят С++
Вы не поверите, но даже в этом обсуждении есть люди,
которым C++ нагадил в шароварыкоторые открыто говорят о своей ненависти к этому языку программирования:Я ненавижу плюсы (потому что потратил на их тонкости, совершенно не переносящиеся на любой другой язык или деятельность, этак 17-18 лет жизни из своих 31, включая самые сочные, когда я мог бы ботать матан или, в конце концов, с девочками за ручку ходить)
Так что да, фактор "не люблю и все" имеет место быть.
includedlibrary
13.07.2022 23:34Я это утверждение немного по-другому понимаю - нет смысла начинать новый проект на си, если это не embedded. Особого профита вы не получите, но зато получите кучу потенциальных ошибок. Существуют языки, которые с помощью сильной системы типов позволяют множество ошибок сделать невозможными, поэтому странно начинать новый проект на си, если есть возможность этого не делать
Source
14.07.2022 02:50+4По такой же логике можно сказать, что нет смысла начинать новый проект на C++. Особого профита вы не получите, но зато получите кучу потенциальных ошибок)
Я такого рода утверждения уже больше 10 лет постоянно слышу. А люди зачем-то всё начинают и начинают.Существуют языки, которые с помощью сильной системы типов позволяют множество ошибок сделать невозможными
Ну, вот это точно не про С++. Это вам не Haskell, не Idris и даже не Rust в плане системы типов.
includedlibrary
14.07.2022 09:40Я аналогичного мнения насчёт c++ придерживаюсь.
Я такого рода утверждения уже больше 10 лет постоянно слышу. А люди зачем-то всё начинают и начинают.
Я не отрицаю, что есть специфические области, где c и c++ необходимы. Например, в игровой индустрии движки на c++ написаны, поэтому новые игры пишутся на нём.
mikhanoid
14.07.2022 10:23Просто по опыту, при программировании на Си ошибки в типах - не самые часто встречающиеся ошибки. Как и при программировании на языке с более сложной системой тимпов, основные ошибки алгоритмические. А от этого никакая система типов не спасёт.
includedlibrary
14.07.2022 11:07Просто по опыту, при программировании на Си ошибки в типах - не самые часто встречающиеся ошибки.
По-моему опыту самые частые ошибки - это segmentation fault, выход за границу массива, переполнение числовой переменной, ошибки при работе с указателями. Плюс нужно следить, что твой код не является UB. Ко всему этому ещё и алгоритмические ошибки добавляются.
Как и при программировании на языке с более сложной системой типов,
основные ошибки алгоритмические. А от этого никакая система типов не
спасёт.Некоторые алгоритмические ошибки можно устранить типами, сделав так, что бы неверные значения нельзя было в принципе создать. Плюс в языках с зависимыми типами можно доказать, что ваш алгоритм делает именно то, что от него требуется. Самые простой пример - сортировка списка. Можно создать тип, значение которого будет являться доказательством того факта, что каждый следующий элемент списка равен или больше предыдущего, и возвращать его вместе с отсортированным списком.
Я не считаю, что всем нужно использовать зависимые типы и доказательства. Я считаю, что нужно минимизировать использование языков, в которых существуют ошибки легко устраняемые компилятором. На том же rust низкоуровневый код создавать много приятнее и безопаснее, потому что нет UB, проблем с обработкой строк, сегфолтами, указателями и ручным управлением памятью. Понятное дело, что при взаимодействии со сторонним сишным кодом многие из этих проблем всплывают, но опять же только в нескольких местах, а не во всём коде проекта
0xd34df00d
14.07.2022 16:48+3Как и при программировании на языке с более сложной системой тимпов, основные ошибки алгоритмические. А от этого никакая система типов не спасёт.
Мы с вами это регулярно обсуждаем, и вы регулярно пишете, что не спасёт, а я регулярно пишу, что некоторые — спасут.
Как там битонная сортировка, кстати? :]
0xd34df00d
14.07.2022 05:56+5Так или иначе большинство библиотек с которыми я работаю, основываются на С или частично зависят от него.
Skia / SDL / Qt / GTK / ect..Можете сказать, где в Qt есть C в значимых количествах (кроме ОС, на которой оно в итоге исполняется, конечно)?
Вы говорить что С++ с UB это не C++ (тогда что?)
Не C++. Компилятор может полагать, что в программе на C++ UB не бывает.
pai3eji
14.07.2022 06:29-2>>Все говорят С это embeded.
полагаю вы это понимаете как "непригоден для чего-либо кроме embed", но говорящие так [опять же полагаю] имеют ввиду что то типа "он тут ненужен".
>>Вопрос только когда все так решили?
а потому и
непригоденненужен он стал, когда стало модным реализовать GUI посредством вкрячивания целого браузера в каждый второй hello world и не абы какого а свежего хромиума, заказывая верстку у джунов пашущих "за еду"... вспомните во что превратились тот же skype, wot...помнится, как раз тут на хабре лет эдак много назад кто-то хвастался как "изящно" парой строк кода призвал уведомление в трэй. только эти его пара строчек кода скомпилировались в exe весом 50+Мб...
впрочем, чего это я на хромиум взъелся... сейчас же контейнеризация в моде, а это на минуточку, развёртывание по целой ОС на каждое приложение *pokerface*. и ведь с точки зрения ИБ, только так и надо...
Sazonov
13.07.2022 21:40+6В вакансиях указывают C/C++ когда ищут человека на проект, в котором тонны спагетти кода, которые писали не очень хорошие си-программисты на языке «си-с-классами».
А вообще я сам сейчас на проекте где есть и чистые си приложения и приложения на хорошем с++. Скажу по себе - действительно очень сложно переключаться с си++ мышления на си. Очень не хватает шаблонов и деструкторов. Но постепенно начинаешь пользоваться определенными практиками и жить становится можно, хоть и неудобно. Не считаю себя хорошим си программистом, после си++ на нём очень тяжело писать (хотя когда я начинал, я писал на чистом си/винапи)
WhiteWhiteWalker
13.07.2022 21:46+3А ничего, что требования и подход != язык? Да, в современном С++ есть RAII и лямбды, но это лишь еще +1 вариант написания функционала, те N вариантов, что были до этого, работать не перестанут. С++ как развитие С необходимо для: уменьшения количества кода, улучшения читабельности (и как следствие, броня ноги +1), развитие стандартной библиотеки для кроссплатформенности. Но того же сокращения кода можно добиться и макросами и goto, что будет лучше циклов внутри циклов или шаблонов с переменным количеством аргументов. Да и интерфейсы динамических библиотек всегда сначала оформляются в C, а затем уже пишется обёртка на С++.
funca
13.07.2022 22:20+4Мне кажется C/C++ появился с подачи Microsoft. В свое время Visual C был именно таким мутантом, толком не поддерживающим ни тот и не другой стандарт. При этом код стандартных библиотек представлял собой ацкую смесь с элементами обоих языков (а может где-то и сейчас так).
KivApple
13.07.2022 22:49+9Во-первых, 95% Си совместимы с плюсами. При должной аккуратности можно писать код совместимый с обоими языками, причём ifdef потребуется только для extern "C", если нужна интероперабильность. Просто надо будет расставлять больше приведений типов и не использовать пару сишных фич.
Во-вторых, из плюсов очень часто вызывается большое количество сишных библиотек. Так что программисту на плюсах нужно знать Си (но это не сложно зная плюсы, см. первый пункт) всё равно.
В итоге либо нужен только Си, либо оба языка.
yeputons
14.07.2022 02:11+2Для вызова библиотек всё-таки надо знать не Си, а очень небольшое пересечение Си с плюсами: немножечко про указатели и конвенции ручного управления ресурсами.
При этом знать
goto
/setjmp
на Си, мне кажется, надо почти всегда, а вот в C++ без этого можно довольно долго жить.
FlameStorm
13.07.2022 23:35+9Зачем писать на C, когда можно делать современные хеловорлды на гигабайт оперативки, вскипячение восьмиядерника и запускающегося на оси не ниже 100500й версии?
Если без горького сарказма, то всему своё конечно. Но лично мне нравится подход к С++ как к C (с дурацкими байтами) с ООП классами. И соответственно мышлением где равноважными являются оба компонента - и предметная область и её формализация через ООП, и забота о эффективности по железу, мышление на уровне проца, байт оперативки, байт i/o с винтов и сети, эффективными алгоритмами, бенчмарками.
Без унавоживания слоями чрезмерных абстракций и прочих модных шуточек вроде "для сложения двух интов давайте подтянем 1042 библиотеки на пару гигов сурцов". Либы безусловно местами незаменимая штука, но может попытаемся не терять разум?
[ C <3 C++ ]
PS: Еще ассемблерные вставки рулят! Asm 4eva !
Kelbon Автор
13.07.2022 23:50-5вот только С++ эффективнее С за счёт больших гарантий и большей выразительности кода
FlameStorm
14.07.2022 00:04-1Когда у нас в стране (перед лучезарным взлётом отечественного железа) будет период пятилетки-другой с двумя миллионами тыжпрограммистов на 200 000 продакшн машин и устаревающим парком техники юзернеймов, критерии эффективности всё же явно будут пересмотрены )
А прямой ответ на коммент - да, спички детям не игрушка. Но ты же ого раз-ра-бот-чик! Если тебе дали пистолет, ну не стреляй им в ногу и в прочее куда не надо (=
Kelbon Автор
14.07.2022 06:36-5С++ компилируется в банально более хороший ассемблер, вы не верите?
mikhanoid
14.07.2022 10:27+1Я тут недавно специально сравнивал -- не компилируется. На Си, конечно, тоже надо уметь писать.
Moraiatw
14.07.2022 16:31+1С++ компилируется в банально более хороший ассемблер, вы не верите?
Нет.
mapron
14.07.2022 21:20+1Как разработчик на С++ я тоже в это не верю. Возможно автор комментария про какие-то узкие кейсы по типу sort() который на C работает с void* и не может оптимизировать под конкретные типы и все такое. Но если не читерить с кейсами
«вот мы на С++ сделали шаблонный код, а на С такое руками писать утомительно поэтому сделаем тупо и неоптимально» — то не вижу причины с чего бы на С++ коду быть быстрее.
С++ эффективнее чем С, да. В выразительности, в скорости написания чего-то большого что надо поддерживать долго и толпой мало связанных меж собой разработчиков, я думаю множество средств где в поддержке он у С может выиграть.
А вот касаемо эффективности кода, ну чет такое. Слабо верится. Только в частностях.
Akon32
15.07.2022 09:12Еще ассемблерные вставки рулят!
Это пока программа запускается на одном
компьютеретипе процессора, а если говорить об оптимизации - на одной модели процессора. Потом через какое-то время ВНЕЗАПНО начинает рулить java. Или даже python.
DabjeilQutwyngo
14.07.2022 08:39+4А почему вы интерпретируете обозначение "C/C++" как язык? И наоборот, с точки зрения теории формальных языков, почему объединение всех возможных цепочек языков C и C++ не будет языком? С чего вдруг операция объединения языков перестала быть замкнутой в полукольце языков?
Символ "/" тоже следует интерпретировать как концепцию совместного использования или варьирования. Есть ведь ещё и CLI-расширение языка C++ для .NET. И как тогда прикажете сообщать, что требуется (кандидат может/программа написана с) применять и C, и C++, и CLI?
Копая дальше, а откуда такое предубеждение, что мозг может эффективно работать исключительно в одном стиле программирования? Вы ведь выражаете мысли и на естественном языке в программном коде, используя хотя бы говорящие наименования, не говоря уже про прямое документирование. Вам борьба за чистоту языка или стиля программирования (абстрактно -- множества концепций) не напоминает борьбу за "чистоту" крови, нации или расы?
Если копнуть ещё глубже, и посмотреть, как устроены живые системы (их изучением занимается системная биология), то выяснится, что природа использует носителей, максимально эффективно накаченных функциональностью. Т.е. и на уровне атомов, и на уровне молекул, и на уровне макромолекул, не говоря уже про их комплексы, метаболиты, в частности. Даже одна молекула (определена структурной формулой) часто проявляет множество биологических активностей. Однако, совмещение функциональности на одном носителе (объединение языков, например) многими осуждается в программировании. Как и совместное использование разных стилей. Хотя оракловые SQLJ как и PL/SQL очень даже удобны и эффективны.
Проверим этот подход сравнением по уровню сложности ИТ-систем, созданных людьми, с живой системой, да хотя бы человеком. У человека собственных клеток порядка 37 триллионов, и 23 триллиона живущих с ними в симбиозе. Одна клетка -- как небольшой завод на полном вертикальном обеспечении: включает и выработку энергии, и производство, и доставку к местам трансформаций всех компонент (от базовых деталей, т.е. простых молекул и аминокислот до молекулярных роботов, коими выступают белки и метаболиты, в частности), требующихся для жизнедеятельности клетки. А главное (о ужас!), в каждой клетке одна и та же ДНК, служащая исходным кодом (система нуклеотидных последовательностей: уложены в хромосомы), хотя многобразие клеток очень велико. И стволовые клетки могут трансформироваться в большое разнообразие других клеток. Т.е. природа несравненно превосходит по сложности всё то, что пока человеческий мозг смог сконструировать и понять. И это ещё не касались устройства систем управления на разных уровнях, особенно нервной системы и мозга. Природу нисколько не заботило требование понятности для кого-либо: функциональность важнее.
Так зачем вам разделение по языкам, по стилям программирования и запрет их совмещения на одном носителе (как программисте, так и исполняемом модуле)? Ради индивидуальной понятности это бессмысленно. Ради поддержания функциональной закреплённости ещё и вредно. Так зачем?
0xd34df00d
14.07.2022 16:50+1И наоборот, с точки зрения теории формальных языков, почему объединение всех возможных цепочек языков C и C++ не будет языком?
Потому что C и C++ назначают некоторым одинаковым синтаксическим конструкциям разные семантики.
DabjeilQutwyngo
14.07.2022 22:57И чтобы эту неоднозначность устранить, есть
extern "C"
. А может быть иextern "SQL"
, и много других языковых приёмов. Более того, многозначная интерпретация не всегда взаимоисключающая, и есть масса случаев, когда она одновременно верная. А взаимоисключающая интерпретация часто возникает даже в одном языке, для чего вводят дополнительные факторы: пресловутый пример с if-else. Многозначная интерпретация и её обработка -- основная проблема разработки таблицы трансляции, т.е. перевода некоторой комбинации из элементов языка во что-либо (другую цепочку или действие). Сложность этих комбинаций и их многообразие является следствием класса языка.Более того, семантика содержится вне языка, как и прагматика: разные языки могут описывать и предписывать одно и тоже. И ключевой характеристикой языка является его выразительная способность (описательная мощность). Именно поэтому есть понятие "синтаксически управляемый перевод", и весь ИТ построен на его применении и вокруг этой проблемы, т.к. для оперирования информацией требуется носитель. Напомню, что язык, по определению, -- это подмножество произвольной итерации алфавита, т.е. подмножество универсального языка.
Поэтому ваше справедливое наблюдение про различие семантики, поставленной в соответствие цепочкам языка, не является ни критерием, ни признаком, но является закономерным эффектом существования пересечения таблиц трансляции по входным комбинациям элементов языка.
0xd34df00d
14.07.2022 23:37И чтобы эту неоднозначность устранить, есть extern "C".
wat.jpg
int meh() { return sizeof('a'); } extern "C" { int foo() { return sizeof('a'); } }
тут обе функции вернут 1.
То есть тут я, конечно, поступаю нестрого, рассматривая язык ещё и как его семантику, но для обсуждения сей и плюсов это разумная нестрогость, на мой взгляд.
Впрочем, даже чисто синтаксически вы в
extern "C"
блоке не можете написатьint class;
, а в C — можете.Многозначная интерпретация и её обработка — основная проблема разработки таблицы трансляции, т.е. перевода некоторой комбинации из элементов языка во что-либо (другую цепочку или действие).
Почему у вас везде вылезают таблицы? Вы что, студент с блохой?
bolk
14.07.2022 08:55+1Гугл, может, и не знает, но «Вики» вполне знает о развитии Си: https://ru.wikipedia.org/wiki/C11
mikhanoid
14.07.2022 10:31+1https://en.m.wikipedia.org/wiki/C2x - даже так. Но не факт, что это всё на пользу языку. Всё же, хотелось бы, чтобы KISS.
khe404
14.07.2022 11:52Прочитал я статью и задумался, решил перевести на понятный мне "С/С++", дабы ,было легче читать и вот что получилось:
bool inconsistency_in_technical_article=false; Date date{"01/01/1983"}; ArticlesLibrary al; std::map<std::string,bool> languages; // {{"C/C++",true},{"C",false},{"C++",false},OTHER_LANGUAGES}; langages = getAllLangauges(); //Каждый день while(++date){ //везде auto article = al.getNext(); //появляется язык С/С++ auto lit = languages.find("C/C++"); if(lit==languages.end()){ throw std::system_error(); // если такого языка нет то это вообще коллапс } // И он мифический bool is_mythical = lit->second; auto found = article.search(lit->first); if(found==article.end()) throw std::system_error(0); // если не везде то с системой что то не так if(article.isTechnical) inconsistency_in_technical_article=true; // даже в технических статьях auto [l1,l2] = split(lit->first,"/"); explain_difference(l1,l2); //todo more C: printf("%s != %s",l1,l2); ... auto c_language = redefine_language("C"); auto cpp_language = redefine_language("C++"); ASSERT(c_language!=cpp_language); lets_finally_think(); // пора бы уже задуматься }
Я конечно не программист и шарж получился так сяк, но приходится сделать вывод, что с++ язык и он может повсеместно совмещаться с С и наоборот. И оба эти языка самодостаточны, однако и оба совместимы как друг с другом, так и с другими языками.
И в этом нет ничего плохого. Как мне кажется, если вы умеете описывать что-то с использованием семантики некоего языка, то, с большой вероятностью за короткий срок вы сможете перестроиться и использовать семантику любого другого языка. Ведь в конце концов это все сделано лишь для того чтобы не изучать команды работы процессора и системы аппаратной адресации памяти. Но и их вы тоже можете начать применять, если задача того потребует.
В общем, хорош вносить смуту в ряды С/С++ программистов, лучше пойдемте вместе побеждать питоноидов. :)
nadoelo
14.07.2022 13:09+1Посыл хороший, но орфографические ошибки и опечатки очень сильно портят читаемость
Vfedosov
14.07.2022 17:02С++ по сути надстройка над С. Сейчас стандарты чуть чуть разошлись, но большая часть си кода отлично работает на С++. Насчет того, что классы не байты и си код это неопределенное поведение - бред. Есть в стандарте плюсов и правила выравнивания полей классов по байтам. Хороший программист С++ всегда учитывает, как поля класса распределены по байтам - позволяет выжать больше из языка. И указатели работают отлично на с++ и все еще очень даже в ходу - особенно при оптимизации. И декларативное программирование обычно занимает меньше половины кодовой базы на плюсах - если не считать библиотеки шаблонных классов и прочие малочитабельные вещи. Если человек считает себя спецом по плюсам, то основы си он знать должен - иначе не поймет значительную часть кода библитек и легаси. Обратное конечно не верно. Так что, если автор выбрал для себя какую-то часть языка С++, как более понятную и приятную, это не значит, что остальная часть языка забыта и не используется.
F0iL
14.07.2022 18:16Ну и раз уж тут такое веселье в комментариях, не могу не оставить тут ссылку на старую статью:
sergey-kuznetsov
14.07.2022 18:52Это как путать -тся и -ться. Слова выглядят одинаково казалось бы, но смысл совершенно разный.
oleg_shamshura
15.07.2022 09:46Когда пишу в резюме C/C++, имею в виду "могу так, могу этак".
Видимо, придется обозначать по-другому: C|C++F0iL
15.07.2022 10:02Что мешает написать через запятую, как и при любом другом перечислении языков и технологий?
Jian
15.07.2022 13:02Потому что встречается смешанный код, когда разные части одной и той же программы написаны и на C и на C++.
F0iL
15.07.2022 13:35В моей практике встречались случаи, когда разные части одной и той же программы были написаны на смешанном коде C++ и JavaScript с довольно плотным их взаимодействием и объектами пошаренными между ними, но указывать используемый язк как "C++/JS" почему-то в голову никому не приходило.
В проектах на Go, где трогают разные низкоуровневые вещи, тоже можно встретить смесь Go и C даже в прямо в одном файле с кодом, и почему-то там через слеш при этом тоже не пишут :)
Jian
15.07.2022 13:37Когда начинали писать C/C++ смешивать их в одной программе было общепринятой распространённой практикой.
F0iL
15.07.2022 18:00Ну мы то сейчас живём в 2022, а не в 1992. К тому же автор изначального комментария говорил о "могу так, могу этак", а не про смешивание двух языков в одной программе.
Ais_Zuhelcer
15.07.2022 09:51+1Что и говорить про провинциальные колледжи и вузы... Особенно веселит когда приходит очередной препод и говорит: "Сейчас, Я вас буду учить программированию, показывает Си-шный код и говорит мы будем изучать С++". А ты у него спрашиваешь в конце лекции: "...так мы изучаем Си или С++, и можно ли писать в C++ стиле?" - он смотрит на тебя с недоумевающим видом и либо переспрашивает, либо начинает поднимать тебя на смех.
ormuzd
15.07.2022 14:45Наверно имеется ввиду, что человек должен знать С и С++, что не удивительно.
Вообще было забавно читать весь этот бред о том, что нельзя реализовать простой вектор на С, потому что язык тебе этого не даст сделать.
Столько программ написано на С, все *nix на нём. А у некоторых вектор не получается реализовать. Что это может говорить? Только об уровне владения знаниями авторов подобных утверждений.
И как говорил старина Торвальд - "C++ можно использовать только если выкинуть из него всё д*рьмо, чтобы остался по итогу чистый С" )Jian
15.07.2022 14:54Вообще было забавно читать весь этот бред о том, что нельзя реализовать простой вектор на С, потому что язык тебе этого не даст сделать.
C является тьюринг-полным и потому эмуляцию подобного вектора возможно написать. :)
#ifndef CVECTOR_H_ #define CVECTOR_H_ #include // для функций работы с памятью #include // также для некоторых функций для работы с памятью //Стандартный размер вектора при инициализации #define CVECTOR_INIT_CAPACITY 4 /* * Структура, представляющая собой "объект" вектора. */ typedef struct{ void** data; int size; int capacity; size_t element_size; } cvector; #endif /* CVECTOR_H_ */
#ifndef CVECTOR_H_ #define CVECTOR_H_ #include // для функций работы с памятью #include // также для некоторых функций для работы с памятью //Стандартный размер вектора при инициализации #define CVECTOR_INIT_CAPACITY 4 /* * Структура, представляющая собой "объект" вектора. * */ typedef struct{ void** data; int size; int capacity; size_t element_size; } cvector; /* * Инициализация структуры вектора. * * @param __v указатель на структуру вектора * @param __dataSize размер элемента данных в байтах */ void cvector_init(cvector* __v, size_t __dataSize); /* * Функция, которая возвращает текущее количество. * элементов в векторе * * @param __v указатель на структуру вектора */ int cvector_size(cvector* __v); /* * Функция добавления данных в конец вектора. * * @param __v указатель на структуру вектора * @param __data указатель на данные */ void cvector_push(cvector* __v, void* __data); /* * Функция изменения элемента вектора по индексу. * * @param __v указатель на структуру вектора * @param __index индекс элемента * @param __data указатель на новые данные */ int cvector_set(cvector* __v, int __index, void* __data); /* * Удаление элемента из вектора по индексу. * * @param __v указатель на структуру вектора * @param __index индекс удаляемого элемента */ int cvector_delete(cvector* __v, int __index); /* * Получение значения элемента по индексу. * * @param __v указатель на структуру вектора * @param __index индекс элемента */ void* cvector_get(cvector* __v, int __index); /* * Полная очистка вектора. * * @param __v указатель на структуру вектора */ void cvector_clear(cvector* __v); void cvector_resize(cvector* __v, int __newCap); #endif /* CVECTOR_H_ */
Взято от сюда http://tetraquark.ru/archives/66
0xd34df00d
15.07.2022 17:51+1А как сделать так, чтобы вектор был только из элементов типа
struct Foo { int a; double b; ];
и компилятор выдавал мне ошибку, если я туда попытаюсь засунуть что-то ещё или прочитать что-то ещё? И, конечно же, чтобы в другом месте в тот же вектор без копипаста можно было засовывать какие-нибудь другие структуры.
Поможет тут тьюринг-полнота?
Akon32
15.07.2022 22:00ЕМНИП была какая-то техника обмазывания дефайнами, которая сводит копипаст программиста (но не препроцессора) к нулю; наверно, и ошибки препроцессором можно проверять.
Но что интересно в примере - тип элемента там полностью описывается лишь размером элемента, да ещё и проверка возможна лишь в рантайме. Динамическая типизация на минималках, однако.
0xd34df00d
15.07.2022 22:23ЕМНИП была какая-то техника обмазывания дефайнами, которая сводит копипаст программиста (но не препроцессора) к нулю; наверно, и ошибки препроцессором можно проверять.
В качестве вопроса со звёздочкой предлагаю реализовать гетерогенный лукап в гипотетической сишной мапе (перегрузки 3-4 здесь).
beeruser
15.07.2022 22:02Самый удобный «вектор» на C — это «stretchy buffers».
ourmachinery.com/post/minimalist-container-library-in-c-part-1
Динамический массив будет выглядеть и работать как обычный указатель:
struct Foo *array;
Дополнительные данные (size, capacity) лежат перед первым элементом.
И никакого дублирования кода.0xd34df00d
15.07.2022 22:22+1Нашёл реализацию, и у меня остаётся вопрос:
А как сделать так, чтобы [...] компилятор выдавал мне ошибку, если я туда попытаюсь засунуть что-то ещё или прочитать что-то ещё?
stb_sb_push
не выглядит особо проверяющим типы.
funca
Как и Java/JavaScript
iago
Вы не поверите, но в 2019 году помню, на огромной американской корпорации на курилке разговаривал с JS-разработчиком. И он мне рассказывал что Java - это браузерный движок для работы JavaScript. Помню, у меня тогда так же подгорало как у автора этого поста
Jian
Путаница из-за того, что VB-script - это действительно браузерный движок для работы Visual Basic в Internet Explorer. :)
tyomitch
VBScript от Visual Basic отличается примерно так же, как JavaScript от Java: синтаксис немного похож, на этом сходства заканчиваются.
charypopper
когда учишь ЯП, особенно первые - то сходство синтаксиса первично, чтобы сказать что языки похожи. И людей которые "плохо знают"* язык гораздо больше чем людей которые "хорошо знают"** язык, а людей которые используют эти термины и "вообще не знают" язык - соизмеримо, имхо. из этого и выходит c/c++, т.к. реальность формируют не только умельцы, но и те кто дают базу, сворачивают с разработки, конкретно с этих ЯП и тд. Даже взять если хабр, автора статьи поймут многие, но сколько статей и комментов написано с подачи что c/c++ похожи. Плюс есть еще "с-подобность", да и сравнение идет с этими ЯП т.к. их давали многим кто в итоге остается в программировании.
*, ** - абстрактная субъективщина с тысячей около истинных определений
Arsmerk_true
а как же VA-script?
https://habr.com/ru/post/554014/
funca
В свое время Microsoft'у запрещали называть свой интерпретатор словом JavaScript из-за того, что они пытались творить там несовместимые вещи. Поэтому язык назывался JScript. VbScript и JScript входили в состав компонента Windows Script Host (WSH), но также были доступны из IE.
Jian
J++, который двигала Microsoft, был на основе java-script, или на основе java?
DMGarikk
Java, из-за чего они помоему с sun судились в итоге тоже
in_heb
видимо это обозначение фулстэк разработчика
GospodinKolhoznik
Но уж язык Java/Kotlin точно существует?
Xobotun
Тогда уж Java/Lombok. Это действительно уже диалект джавы, как мне кажется.
RyAtex
Lombok это библиотека. Тогда уж праильне сказать Java/Spring
loltrol
Не согласен. Код на ломбоке это ситаксически-верный код java. Если бы lombok добавлял бы парочку новых keyword'ов, тогда другое дело.