Как стать автором
Обновить

Комментарии 383

Вы не поверите, но в 2019 году помню, на огромной американской корпорации на курилке разговаривал с JS-разработчиком. И он мне рассказывал что Java - это браузерный движок для работы JavaScript. Помню, у меня тогда так же подгорало как у автора этого поста

НЛО прилетело и опубликовало эту надпись здесь

VBScript от Visual Basic отличается примерно так же, как JavaScript от Java: синтаксис немного похож, на этом сходства заканчиваются.

когда учишь ЯП, особенно первые - то сходство синтаксиса первично, чтобы сказать что языки похожи. И людей которые "плохо знают"* язык гораздо больше чем людей которые "хорошо знают"** язык, а людей которые используют эти термины и "вообще не знают" язык - соизмеримо, имхо. из этого и выходит c/c++, т.к. реальность формируют не только умельцы, но и те кто дают базу, сворачивают с разработки, конкретно с этих ЯП и тд. Даже взять если хабр, автора статьи поймут многие, но сколько статей и комментов написано с подачи что c/c++ похожи. Плюс есть еще "с-подобность", да и сравнение идет с этими ЯП т.к. их давали многим кто в итоге остается в программировании.

*, ** - абстрактная субъективщина с тысячей около истинных определений

В свое время Microsoft'у запрещали называть свой интерпретатор словом JavaScript из-за того, что они пытались творить там несовместимые вещи. Поэтому язык назывался JScript. VbScript и JScript входили в состав компонента Windows Script Host (WSH), но также были доступны из IE.

НЛО прилетело и опубликовало эту надпись здесь
Java, из-за чего они помоему с sun судились в итоге тоже

видимо это обозначение фулстэк разработчика

Вообще, тот парень был чистым JS, но ваш коммент прям заставил меня хорошо поваляться под столом :) спасибо вам за прекрасное настроение!

Но уж язык Java/Kotlin точно существует?

Тогда уж Java/Lombok. Это действительно уже диалект джавы, как мне кажется.

Lombok это библиотека. Тогда уж праильне сказать Java/Spring

Не согласен. Код на ломбоке это ситаксически-верный код java. Если бы lombok добавлял бы парочку новых keyword'ов, тогда другое дело.

Не-а. C это подмножество C++, ровно как и подмножество Objective-C. Компании ищут "C/C++" так как программисту часто (всегда) будет нужно работать с "тупыми наборами байт" и что еще хуже, взаимодействовать с железом (даже если посредством толстого фреймворка типа CUDA Toolkit). Если этого нет, то писать на C/C++ не имеет никакого смысла, для бизнес логики есть всякие сишарпы с джавами.

только часть С это подмножество С++
А с точки зрения хорошего С++ кода практически ничего из С использовать не нужно

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

Кстати ООП также преимущественно является императивным стилем, как и функциональное программирование. Я честно говоря ещё ни разу за 12 лет не видел в С++ декларативного программирования, или для вас использование готовых библиотек является декларативным программированием?

А как связаны аллокации и С?

Я имею ввиду написание своих аллокаторов, что по моему мнению напрямую схоже с С way

И это не считая что существует куча именно Сишных зависимостей, типа того же curl или openal. Да те же pthreads/epoll и все что их окружает. И в мире С++ они появляются заметно чаще, чем хотелось бы. Потому оно и С/С++.

Я вообще не уверен что в C++ можно открыть сокет и послать пакет без reinterpret_cast или там C-style cast. На этапе connect уже возникнут проблемы.

Как-то так до сих пор и нет std::network в библиотеке. Всё остальное из cstd подключают.

подключают boost::asio, а не сишные сокеты

Открыл исходник 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для сишного сокета.

это не сишный сокет, а платформозависимый, вы бы ещё сказали "открыл .exe, там машинный код"

Прикольный пример С++ кода, у которого первых пять include, прямо в названии указывают на зависимость от старого доброго С :-) , а 3 последних из библиотеки, которая пытается подтянуть С++ до гордого звания языка высокого уровня!

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

Аллокатор в С и аллокатор в С++ это совершенно разные вещи.

С++ аллокатор это абстракция, легковесный объект предоставляющий интерфейс для получения памяти.

Ну ок чем отличается по вашему

//C++
auto data = Allocate<Data>();
// от С
struct DataHandle* data = ALLOCATE(Data);

Внутри ведь они могут будут устроены практически одинаково.

Вы понятия не имеете как выглядит аллокатор в С++

Спойлер

ВОТ ТАК

template<typename T, typename Alloc = std::allocator<T>>
struct vector { ... };

facepalm Я говорю про реализацию аллокаторов а не про использование их в дальнейшем, видно вы никогда не писали свои аллокаторы. Ладно хотите пример применения на подобие С++ ок

struct vector = create_vector(ALLOCATOR_FOR(Data));
...
struct Data item = at(vector, i);

Это уже рантайм передача аллокатора, ваш код значительно менее эффективен чем аналогичный на С++, выглядит хуже, использует макросы

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

Это уже рантайм передача аллокатора, ваш код значительно менее эффективен чем аналогичный на С++, выглядит хуже, использует макросы

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

Нет не в рантайме. Стандартный аллокатор не занимает там места вообще, кастомный с состоянием может занимать состояние и подставляться на рантайме

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

и как вы это реализуете?

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

Ну так с ядра все начинается и им же все заканчивается. Какая разница, прочитает - ядерный загрузчик размер секции .bss из ELF для того чтобы выделить кусок памяти или само приложение вызовет mmap(ANONYMOUS) чтобы получить себе памяти в свое адресное пространство?

Рантайм любого языка будет дергать ядерные вызовы типа mmap или sbrk(не к ночи будь упомянут) чтобы получить память от ядра для своего внутреннего аллокатора.

НЛО прилетело и опубликовало эту надпись здесь

Вы удивитесь, но скорее всего нет. Если продраться через абстракции, то все сводится к записи в таблицу трансляции MMU. Прямо из сишного кода. Что-то типа page_table[virtual_addr] = physical_addr.

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

А как по-вашему ядро знает какие у него физические страницы заняты, а какие свободны?

Ну как.... У него есть свой аллокатор страниц. Который следит за тем, какие страницы кому были выданы.

НЛО прилетело и опубликовало эту надпись здесь

Откуда берётся физический адрес?

Из аллокатора страниц. Аллокатор знает где вообще расположена память потому что ему об этом сказал условный BIOS.

Какое место в стандарте C гарантирует, что оттуда можно читать и туда можно писать?

А это самая банальная память. Туда можно писать и оттуда можно читать как угодно.

Вы наверное спрашиваете про Memory Mapped IO, которые хоть и выглядят как память, но ею не является. Поэтому ядро кстати туда на пишет через a = 17. Есть специальные функции обертки: writel(a, 17). Которые с одной стороны обламывают шибко умный компилятор, и с другой - обеспечивают совместимость со стандартом. (А вот всякий код на arduinо пишет в лоб. Стыд и позор им.)

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

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

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

Ядро же - это отдельная сущность. Отдельная абстрактная машина с точки зрения стандарта. Для ядра - операции над page tables приложения выглядят как просто работа с массивом структур. Стандарт это позволяет, надеюсь :)

НЛО прилетело и опубликовало эту надпись здесь

Как сказал? Какая сишная функция отвечает за взаимодействие с 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 переменные.

НЛО прилетело и опубликовало эту надпись здесь

Ну, я бы сформулировал корректнее: невозможно на одном лишь стандартном С реализовать весь сетевой стек.

Почему? Стандарт даже прерывания упоминает. И 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, т.е. возможность запускать программу без ОС.

НЛО прилетело и опубликовало эту надпись здесь

Ну так стандарт описывает оба случая. Они там называются conforming program и strictly conforming program соответственно.

Лично я выступаю за strictly conforming программы тогда, когда это возможно. Учитывая, что мы тут говорим все же про embedded и всякое такое, которое в принципе не может быть strictly conforming, остается довольствоваться просто conforming.

НЛО прилетело и опубликовало эту надпись здесь

А на стандартном C++ можно?

НЛО прилетело и опубликовало эту надпись здесь

mmap и sbrk подключают в адресное пространство процесса дополнительные mmu-странички с памятью. И всё. Как потом эту память нарезать на куски, выдаваемые в программу libc-шным malloc'ом или ещё каким аллокатором -- проблема исключительно этого malloc'а и прочего кода, выполняемого в юзерспейсе.

А вы сможете по машинному коду определить, на каком языке он написан? С++ от Асма отличите в ехе-шнике?

НЛО прилетело и опубликовало эту надпись здесь

Вы, наверно, не в курсе, что запаривание алайнментом, аллокациями и т.д. ещё не значит, что это вообще как-то связано с Си. И то, что в С11, равно как и в С++11, вошёл тот же alignas, ещё не делает написание его или размышление над подобными вещами сишным.

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

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

Под запариванием с выравниванием я подразумевал работу с теми самыми пресловутыми байтами, понятное дело что оно может быть сделано на более высоком уровне, тем не менее это всё тоже ковыряние с байтами, например DX константные буфера (да и все остальные ресурсы) мапятся в обычный кусок памяти и функция возвращает void* а дальше работай с этим куском памяти как заблагорассудится тут например и нужно запариваться с выравниванием. То что С++ имеет огромное количество инструментов для эффективного управления памятью не делает его менее С way это просто обёртка, которую без проблем можно заменить на свою.

Гм, ну, так любую работу с байтами на любом языке можно назвать абстракцикй над Си. А там и над ассемблером. Но банально вот до С++20 множество способов, которыми ковыряли и ковыряют байты в Си были попросту запрещены и приводили к неопределённому поведению. Ну а то, что всюду и везде Си апи это да. Но так, если докопаться, везде устроено – что-то в конечном итоге да будет дёргать код, написанный на Си. Это ведь всё ещё не значит, что любая работа с байтами в любом языке Си-way...

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

Я не говорю про абстракции на Си я говорю что любое ковыряние с байтами, будь то выравнивание или мапинг, это С way

Теперь бы еще понять почему.

Ковыряние байтов в Ada -- это тоже C way? А в Modula-2? А в Lisp, а в Algol-68?

В рамках данной статьи я считаю да, потому что идёт сравнение с С. Ясно дело что можно всё свести к тому что языки перебирающие байтики были и до С и по сути С way это ada way и так далее вплоть до Тьюринга.

Смысла в ваших словах не видно. Но мысль я понял: если вам удобно называть операции с байтами "C way", значит вы будете называть это "C way" не смотря на то, что байтиками манипулировали и до Си, и после Си в языках, которые к Си никакого отношения не имели.

Удобно, чё.

Если я правильно понимаю, то арифметика на указателях это далеко от C++ way. И если мне не изменяет память, все те выразительные инструменты (в т.ч. для работы с памятью) которые предлагает С++ не особо хорошо натягиваются на некоторые задачи (программирование микроконтроллеров, например).

То есть в моем понимании, если надо дикий перформанс и/или присутствуют жесткие ограничения по железу (ATtiny85 - 8KB flash, 512B RAM, 20MHz), то те же итераторы и умные указатели (насколько я помню, C++ Core Guidelines говорят о ручных выделениях и освобождениях памяти как о анти-паттерне) - не вариант. А иначе - это уже не каноничный С++.

Итераторы это абстракция с отрицательной стоимостью, они не делают ваш код медленнее, наоборот ускоряет. unique_ptr только в некоторых случаях на некоторых платформах и АБИ будет иметь маленький оверхед. shared_ptr просто невозможен в С, замучаетесь.

И да, указатели на умные, а владеющие. Это не замена T*

unique_ptr только в некоторых случаях на некоторых платформах и АБИ будет иметь маленький оверхед

https://youtu.be/rHIkrotSwcc?t=1058

ATtiny85 даже среди микроконтроллеров 90х годов запредельный аскетизм, его можно на ассемблере программировать. Современные микроконтроллеры это типично STM32F4, 240МГц, мегабайты флеша, сотни килобайт памяти, он сравним с первыми Пентиумами по производительности.

Не согласен с самой концепцией которую вы вкладываете в "современные микроконтроллеры". У каждого производителя есть несколько линеек микроконтроллеров, каждая из которых имеет свое функциональное позиционирование, где она будет наиболее дешева и востребована. И из этой функциональной иерархии никуда не исчезли сверхдешевые 8-битники, или не производительные и емкие, но очень экономичные Cortex-M0/-M1.

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

Еще есть вариант с ПЛИС/FPGA, там вообще нет программы в обычном понимании, набор логических элементов, от 240 штук в простых микросхемах до миллионов в более сложных. Это если смотреть в сторону упрощения управляющей логики. Тоже свои плюсы и минусы.

Плис хорош до тех пор пока его плюсы перевешивают относительно высокую цену, потому что в нее входят и конские ценники актуальных девкитов и программистов) У fpga и uC слишком разный диапазон задач, чтобы сравнивать их в лоб, поэтому я их и не упомянул.

Дикий аскетизм это pic12 серия. 64 байта озу/1024 слов флеш программы/128 байт еепром. Прекрасно программируется на С (компилятор xc8 от микрочип). МК выпустили в начале 2000х, до сих пор много где применяются, особенно в китайских изделиях. Но это реальная мука. Аттини гораздо лучше и удобней.

Вроде он для ассемблера удобен. Мелкие МК идеальны для программирования на ассемблере, каждый байт под контролем и программа не сможет увеличится в объеме так, что потеряется общая картина происходящего. А вот STM32 на ассемблере уже не попрограммируешь, энтузиасты пробовали, но быстро интерес пропал судя по форумам.

Микрочип сделала очень хороший компилятор и вроде пользуешься синтаксисом С, а вместо переменных идут имена регистров, а в значения пишешь битовые маски и отдельные биты, через имя регистра и добавления к нему слова bits. Например, недавно делал хобби проект для друга на PIC12F675. Нужно было всего лишь менять состояние трех цифровых выходов, два на светодиоды (или один rg светодиод), один на управление через транзистор якорем реле, переключающего обмотки трансформатора паяльной станции. Один вход на кнопку. Также нужно было запоминать состояние на момент выключения питания. Сделал на С - заняло 17% озу, 16% программной памяти и два первых байта eeprom. На ассемблере это приличная партянка, на С гораздо удобнее и код читается на "раз-два". И кроме того, здесь не то что stm32, здесь даже avr328p будет избыточным. Моё мнение - или 8ми ногий аттини или пик.

Подобные компиляторы упрощенные это codevision avr, там тоже можно к битам байта обращаться напрямую. И есть еще bascom avr.

Синтакис типа такого удобен весьма

TWCR.twint = 1
TWCR.twsto = 1
TWCR.twen = 1

Ну хз, много лет назад @DIHALT потрясающие нетлентки писал на тему ARM и ASM. Я до сих пор пользуюсь.

Я помню, читал раньше постоянно его. Да и сейчас на Ютубе отличный материал по 3D проектированию во Fusion 360 у него есть.

дикий аскетизм — это attiny12 (емнип), там вообще RAM отсутствует, 32 регистра и все :)

Офигеть, вот это огрызок, и это уже в 2007ом году. Кстати, под мой проектец, тоже подошло бы...

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

НЛО прилетело и опубликовало эту надпись здесь

Кстати, а как функциональное программирование является императивным стилем? Какой смысл вы вообще вкладываете в эти слова?

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

НЛО прилетело и опубликовало эту надпись здесь

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

К тому же если взглянуть на QT или любую другую event based, ну или корутины С++20(или в целом асинхронное программирование), то там нет никакой последовательности, есть события и объекты

Вы, похоже путаете императивность с "код движется строго сверху вниз", а декларативность с "код может перепрыгивать в функции/методы, которые, формально, выше исполняемого кода". Меня еще смутило, что вы признаком декларативности назвали goto...

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

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

декларативность с "код может перепрыгивать в функции/методы, которые, формально, выше исполняемого кода". Меня еще смутило, что вы признаком декларативности назвали goto...

Откуда вы это взяли вообще? Я такого даже близко не говорил

Я так понял автор считает любую программу которая описывает последовательность действий для получения результата императивной. В таком случае декларативный только какой-нибудь Пролог (если я правильно понял автора).

Я в геймдеве как core программист работаю уже больше 15 лет.
И полностью согласен с автором: при использовании С++ практически ничего из С не используется.
При этом С я тоже знаю, писал для смарт часов софт на чистом С. И это адище то еще. Совершенно другой подход, сильно раздражающий.
Но вернемся к геймдеву:
Даже когда на плюсах пишеш низкоуровневую логику, всё равно придерживаешься С++ подходов. Даже условный аллокатор... Где там нужен С?
Тем более современный геймдев, там вообще во всю шаблоны. И если 15 лет назад это воспринималось как "Эпики вообще больные, пишут движок на шаблонах. Это же будет тормозить!". То сейчас это "Эпики сделали стандарт индустрии".

Очень интересно, можете рассказать как вы мапите те же Constant Buffers или заливаете ресурсы? А можно пример аллокатора без использования адресной арифметики? Конечно если вы используете готовые библиотеки или движки для этого, то вы и не столкнётесь с Си, но если пишете с нуля сишные ноги будут торчать из всех щелей.

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

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

void* allocate(size_type size)
{
	void* out = hip_pos;
    hip_pos = hip_pos + size;
    return out;
}

Это код на С++ или на С? Я веду к тому что когда пишутся низкоуровневые вещи без использования дополнительных библиотек грань различия С и С++ практически стирается.

Разница обнаружится сразу же по выходу из allocate, т.к. в C++ вы не можете с void* обращаться так же вольно, как в Си. Начиная от того, что в C++ нет автоматического неявного каста из void* во что-то другое. И заканчивая тем, что нельзя просто так делать reinterpret_cast<T*>(some_void_p) (может потребоваться применять std::launder).

Ну на выходе да, но речь то про внутренности. Взять тот же std::string снаружи всё красиво а внутри сишная карусель с указателями и байтами если есть sso

Как минимум kernel в том же CUDA Вы пишите именно на C.

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

Там даже есть контейнеры, такие как vector для хоста и девайса. Но это все ещё С

Что значит "это всё ещё С"? В 2011 году уже были С++ классы в device-коде, и С++ шаблоны. Это не С.

>А с точки зрения хорошего С++ кода практически ничего из С использовать не нужно

...но линуксовые API всё ещё написаны на C. Да и в целом, если необходимая тебе библиотека написана на С, то С рано или поздно в твоём cpp-коде окажется.

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

Я помню оранжевую книжку, перевод Страуструпа
image(без ката, пусть кто-то всплакнет)

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

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

Уже давно не подмножество. В плюсах нет как минимум restrict и _Generic (действительно, зачем он когда есть шаблоны?), да и код без них не всегда может быть скомпилирован обоими компиляторами из-за разных ограничений на приведение типов (тут я конкретный пример, впрочем, не вспомню).

auto же! В C и в C++ это слово значит совсем разные вещи.

C это подмножество C++

Псст, хотите немного уличной магии!

1) Преобразование int к enum

enum { e0 = 0 } e = 0; // валидно в C, но не в C++

2) Неявное преобразование void*

void* pv = 0;

int* pi = pv; // валидно в C, но не в C++

Да можно не выдумывать сложное, допустим вот так:

int or;

Скомпилируется в C, но не в C++

Фантазия, однако!

Какое популярное заблуждение, я думал его уже давно нет. С - это подмножество Ojbc, которому я отдал 8 лет с 2009 по 2017 - это утверждение верно. Потому что код обжа сначала транслируется в чистый С, а потом компилится уже компилятором С. И в iOS до сих пор огромное множество фрэймворков написаны на чистом С.

С++ же только внешне похож на С (и то был когда-то), под капотом совершенно другое. В их компиляторах ни одной общей строчки, один и тот же код компилится в совершенно другой АСМ.

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

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

Как и автору оригинального поста - С++ до декларативности как Пугачеве до Мадонны, уж слишком он древний и громоздкий.

Как и автору оригинального поста - С++ до декларативности как Пугачеве до Мадонны, уж слишком он древний и громоздкий.

Может вы просто не умеете?)

А может вы приведёте пример декларативного кода на С++?

У меня есть маленькая библиотечка на C++11 которая из набора функций строит граф зависимостей (compile time) и выполняет его для заданных входов и результата/ов ,правда в публичном доступе её пока нет.

Это всё использование библиотек, с таким же успехом можно написать библиотеки и на С которые позволят писать в декларативном стиле (да и вообще в любом), а на уровне языка есть что-то?

Вот именно, что в С таких библиотек нет и быть не может. Даже теоретически.

Реализуйте std:vector на С

Ну что вам мешает написать на Си функцию на подобии этой?

struct IntArray arrr = zip(some_array, &zip_func);

Чем не декларативный стиль?

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

Сравните производительность и интерфейс сишной сортировки и С++

А чем конкретно он мешает? Не скомпилится или что?

Сравните производительность и интерфейс сишной сортировки и С++

Сравнил  cpp.sh/43drd С++ получился быстрее. Только не понимаю к чему это?

посмотрите на алгоритмы стл

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

Эм, для вас использование готового языка программирования является декларативным подходом? Что за бред? Чем отличается "использование библиотеки" от использования языка в этом контексте?

С++ позволяет писать свои абстракции, в этом его главное преимущество. Почитайте что такое декларативность вообще, непонятно причём тут синтаксис языка.

Вот вам из синтаксиса С++ декларативность : template, ... (pack expanding), range based for loop

НЛО прилетело и опубликовало эту надпись здесь

Хаскель - это практически целиком сахар поверх вызова машинного кода. <Любая фича> такой вообще костыль для очень неудобного написания кода в С стиле

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Ну полноценно декларативными языками можно назвать наверно html и sql всё остальное по мне выглядит императивным кодом, конечно можно сказать что вот например map это отношение данных и результата и это является декларативным стилем, но по мне это просто вызов готовой функции по сути инструкция.

НЛО прилетело и опубликовало эту надпись здесь

html не оч яп :]

hypertext markup language: программируется разметка, исполняется движком браузера как вычисление свойств всех элементов визуального представления. Просто потому что язык вообще -- это средство передачи информации, представленной значением выражения на этом языке. А как её интерпретирует получатель и что делает в связи с этим -- вопрос семантики и прагматики соответственно.

Людей вон тоже прекрасно программируют, используя естественный язык и механизм веры. Принцип его работы ничем не отличается от результата работы программатора: происходит подстановка программируемым объектом чего-либо на место значения выражения (в частности, термина или обозначения), которая не зависит от информационной связи с описываемым объектом.

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

программируется разметка

Разметка не программируется, а просто описывается. Именно потому HTML и не язык программирования, а язык разметки.

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

НЛО прилетело и опубликовало эту надпись здесь

Что ж, тогда мне придется отослать вас к совершенно любому англо-русскому словарю, который покажет вам, что словосочетание "markup language" переводится как "язык разметки", а не "язык программирования". А разметка, к вашему сведению, не является сценарием или планом выполнения. План выполнения и сценарий описывают последовательность действий, которые необходимо выполнить, чтобы получить результат (сложить 2 и 3, из 5 вычесть 4). Разметка же в свою очередь описывает непосредственно САМ РЕЗУЛЬТАТ. А то, каким образом этот результат будет получен, к ней не относится ни коим образом.

НЛО прилетело и опубликовало эту надпись здесь

То, что вы исключили что-либо из объёма понятия "программа", и, как следствие, "программирование", извменив тем самым содержание понятия (т.к. для вашего понятия требуется иное характеристическое свойство), и в результате термин стал для вас многозначный, не означает, что термин многозначный: это вы его так понимаете, проводя ложное разделение, как минимум. См. выше объяснение такому же товарищу.

Ваша общая ошибка в том, что считаете программой лишь то, что выражено в императивном стиле. Если выразить в командах рисования графических объектов то, что показывается на экране в результате рендеринга html-кода, то получится именно то, что вы называете программой. Хотя программа -- это предписание исполняющему устройству. А программирование -- это формирование такого предписания.

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

НЛО прилетело и опубликовало эту надпись здесь

https://esolangs.org/wiki/Piet (либо его аналог, если нужно что-то, к примеру, с большим количеством цветов)? That is, она может не являться программой сама по себе, но она может быть интерпретирована как таковая (а без способа интерпретации и код на C, и bare-metal бинарник тоже программами являться не будут).

НЛО прилетело и опубликовало эту надпись здесь

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

Машина состояний не является тьюринг полной, поэтому не является полноценной программой.

Для функционального и асинхронного программирования, программа - это уже не набор инструкций, но все равно тьюринг полное.

НЛО прилетело и опубликовало эту надпись здесь

Пролог как раз декларативно логический язык. Что выглядит декларативно так это описание интерфейса в vuejs.

что такое декларативный подход

boost::xpressive::sregex, например.

НЛО прилетело и опубликовало эту надпись здесь

То, что нечто реализовано посредством ООП, не означает, что на уровне использования не декларативный подход. А он декларативный: описывается выражение на sregex (производный специализированный доменный язык (Domain Specific Language) от PerlRe и синтаксиса C++), но при этом никаких шагов по его выполнению не предписывается. Пример:

sregex re_year = bos >> repeat<4,4>(_d) >> eos;

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

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

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

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() >> &param_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()
               )
            ) >> &param_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
         )
   );
}

Код не для статьи написан, он реально живет в нашей библиотеке.

Это функциональный подход, только причём тут декларативность? Тут ясень порядок выполнения инструкций, он чётко определён вами. Если бы написали что-нибудь в духе

auto value = group | 
  select| {value_form_t::token, value_form_t::quoted_string} |
  from | some_string;

И компилятор бы сам решал в каком порядке всё выполнять и как разбивать это можно было бы назвать декларативным программированием.

так он и решает ...

Это функциональный подход, только причём тут декларативность?

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

Тут ясень порядок выполнения инструкций, он чётко определён вами.

Он определен не мной, а грамматикой. Или, по вашему мнению, EBNF нотации не декларативны?

НЛО прилетело и опубликовало эту надпись здесь

По (e)BNF можно чисто синтаксически построить императивный парсер.

Само описание EBNF что-то говорит о том, какой это будет парсер: нисходящий или восходящий?

НЛО прилетело и опубликовало эту надпись здесь

А могли бы вы упражняться в софистике самостоятельно?

НЛО прилетело и опубликовало эту надпись здесь

Ну так парсер и язык -- не одно и тоже: это разные уровни и разные системы. Более того, парсера мало: для получения поведения требуется интерпретатор, как минимум. Путаете уровень интерфейса с уровнем реализации опять.

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

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

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

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

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

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

НЛО прилетело и опубликовало эту надпись здесь
А может вы приведёте пример декларативного кода на С++?
Я попробую. Вот код для вычисления N-го числа Фибоначчи на этапе компиляции:
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;
};
По-моему, вполне декларативно) Конечно, для вывода результата понадобится императивный код, например std::cout << fib<6>::value (выведет 13).

Если что, я не настоящий С++-разработчик, так что качество кода может хромать и т.д.

Потому что код обжа сначала транслируется в чистый С, а потом компилится уже компилятором С

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

В их компиляторах ни одной общей строчки

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

Фронтенд LLVM, например, у обоих будет разных. Но Middleend и Backend одинаковый. Ни одной строчки это конечно очень сильно было сказано

когда-то помню Google и Oracle судились из-за того, что какая-то элементарная функция была одинаковая и в Oracle JVM, и в Darwin VM. И судья не выдержал и накричал на юристов, что это может быть простым совпадением.

А так я, конечно, утрировал, но более чем уверен что на 95% компиляторы совершенно разные, более того, один и тот же код на С и С++ компилится в совершенно разный ASM.

А вот один и тот же код Objc и C будет совершенно одинаково компилиться. Вот вам и критерий, что является подмножеством, а что - нет

C не есть подмножество C++ как минимум из-за: restrict указателей, _Generic макросов и _Noreturn функций. Кроме этого, описанный вами миф развенчивал ещё Страуструп в своей книжке, почитайте его на досуге.

НЛО прилетело и опубликовало эту надпись здесь

Я в C совсем не эксперт, но, помнится, читал обсуждение использования этой конструкции в рассылке разработчиков Linux, и там был вердикт вида «да, по стандарту C это UB, но мы пишем код под конкретный компилятор, который даёт гарантии того, что это будет работать как надо.»

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Когда я работал преподавателем программирования на УПК в 1992 году, именно так мы и преподносили.

C++ - расширение языка C для работы с классами. И ООП мы изучали на библиотеке Turbo Vision. Была такая в Borland C++ для работы с псевдографическими окнами в текстовом режиме DOS.

НЛО прилетело и опубликовало эту надпись здесь

Начиная с C++11 С++ перестал быть надмножеством C, так как изменился смысл некоторых конструкций. В частности, ключевого слова auto. Начиная с C++11 можно написать корректную программу на C, которая не будет компилироваться с ошибкой компилятором C++.

Давно уже можно такое написать, смотрите мой комментарий. Минимум начиная с 98 стандарта.

"or" is an inbuilt keyword that has been around since at least C++98

Что-то никто в этой ветке не упоминает VLA как одну из фич C, которых нет в C++. Или его уже в стандарт плюсов завезли? Когда чекал это последний раз, g++ поддерживал его только как расширение стандарта C++.

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

НЛО прилетело и опубликовало эту надпись здесь

Я встречал немного другой взгляд на требование C/C++. Все что перечислено в статье шаблоны, алгоритмы, итераторы, ренжи... - это все высокоуровневые абстракции. Помимо этого важно понимать, как из этих высокоуровневых абстракций в итоге получается машинный код. Как они реализованы и чего стоят. Возможно это кого-то удивит, но очень много программистов, у которых в резюме написан опыт 3+ лет на C++, не знают низкоуровневых вещей, например, многих ставит в тупик вопрос про выравнивание данных (здесь конечно можно заметить что "настоящему" программисту на С++, который пишет на "хорошем", "декларативном" С++ эти знания ни к чему - это тот же холивар, что и про алгоритмы). При этом я не встречал ни одного программиста на С, у которого подобные вопросы вызывали бы недоумение. Поэтому иногда когда пишут про C/C++ имеют ввиду знание как раз вот этого низкоуровневого наследия С, на котором построен C++. Но такой взгляд встречается редко, здесь вы правы скорее всего.

> И это не абстрактные рассуждения в вакууме

просто любопытно, что такого автор сделал в программировании чтобы выражаться в стиле "тупыми наборами байт", " глубокий наколеночный эмбед", " языки требуют абсолютно разных умений и подходов к разработке", оба использовал практически с момента появления первых компиляторов, (30+ лет), ничего особенного в С++ vs С не вижу, просто tool, для одних проектов лучше С, для других больше подходит С++, не более того

НЛО прилетело и опубликовало эту надпись здесь

Вы не совсем поняли, предмета спора "кто больше знает" нет, вопрос к терминологии автора, профессионалы так не пишут, даже если он эксперт и сидел в офисе рядом с Страуструпом, остальное слегка Ваши фантазии

НЛО прилетело и опубликовало эту надпись здесь

тем не менее нашли "спердоб", какие у Вас вопросы к "30+ лет"?

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

НЛО прилетело и опубликовало эту надпись здесь

нет проблем, " 30+" в общем случайно, приходится задумываться иногда, может поймете когда-нибудь :)

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

примерно понимаю, лопата придумана чтобы траншеи копать и каналы строить, конечно документацию на лопату тоже надо знать, включая стандарт как reference, и особенности компилятора, imho особенно гордиться здесь не чем, в конце концов стандарт люди создали, и компилятор как правило кто-то другой написал, типа чужая работа, лучше что-нибудь свое как следует сделать, пусть даже чужой лопатой :)

вероятно наступил кому-нибудь на лапу :)

через 20 мин это сообщение принесло -1 в карму, без разницы конечно, посмотрим что дальше будет

Исходя из изложенного материала, например, программа написанная на чистом C с использованием API DirectX, является C или C++ программой?

А как ты на С напишешь с использованием DirectX там же ооп апи, или я что-то упустил.

Да, получится гибридное решение, C код с созданием и использоваине объектов из API. ИМХО в итоге получится некий гибрид C/C++, котрый не C, не C++, вполне жизнеспособен и ничему не противоречит.

Чтоб Вы были в курсе: DirectX имеет два набора API: для С и для С++. Это вот прямо разные заголовочные файлы или отделённые через ifdef секции в одном заголовочном файле. DirectX изначально разрабатывался как высокопродуктивная графическая подсистема и её, конечно, нельзя было намертво прикрутить к одному лишь С++ и забыть С.

Да действительно посмотрел щас d3d12.h и правда, был не прав С api присутствует

Всегда как смотрел на DirectX воспринимал его как такой сильный C way - когда объявляется дескриптор, который struct с десятком полей, а потом на нем вызывается функция (даже если как метод класса). В моем понимании C++ way это ООП, потому я ожидал бы какой-нибудь builder (как шаблон проектирования). Но это все философия, я так понимаю это либо пережиток прошлого, либо решение в пользу производительности. Вроде тот же Vulkan имеет точно такой же подход, так что скорее второе.

API DirectX основан на COM, а та имеет реализацию под самые разные языки программирования: как минимум, Си, С++, Delphi и C#. Ещё в Java и Rust я что-то видел.

Я подозреваю что когда пишет не C++, а C/C++ - то хотят видеть человека умеющего работать с обоими парадигмами. Хотя, иногда попадаются очень забавные объявление от HR котрые их не всегда различают: " Хотим знание Си и boost". А вот на счет наколеночного embedded я бы поспорил (иначе Linux kernel, Postgresqsl, etc. превращаются в наколеночный embedded)

У линукса нет объективных причин не использовать С++. Но есть Линус, который по личным каким то предпочтениям не хочет

А какой профит принесет C++ ядру линукса? Появится 11 разных способов инициализировать поле класса? Как там со стабильным ABI? В ядре периодически то ассемблерный код дергает сишные функции, то сишный код дергает ассемблер. И модули загружаются/выгружаются прямо в рантайме.

А если вы расскажете как делать всякие более продвинутые штуки типа динамической трассировки C++ кода (как ftrace и kprobe в ядре) - будет вообще круто.

Появится 11 разных способов инициализировать поле класса?

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

Как там со стабильным ABI

Как-то коммитету по стандартизации С++ получается держать стабильное ABI, про extern "C" промолчу

 И модули загружаются/выгружаются прямо в рантайме.

и чё? Как это мешает то?

А если вы расскажете как делать всякие более продвинутые штуки типа динамической трассировки C++ кода (как ftrace и kprobe в ядре) - будет вообще круто.

Нет никаких оснований говорить, что на С++ этого не сделать. Лучше покажите как на С сделать "класс" не выделяя его в куче

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

Появится способ создавать классы.

Но зачем? Какую задачу это будет решать с точки зрения ядра? А то фраза звучит так, как будто наличие такой задачи заведомо очевидно.

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

Там не классы эмулируют, а интерфейсы. А для этого C++ - overkill. C++ сложный язык, создающий зависимость от разработчиков компиляторов для C++, от комитета стандартизации C++ и от многолетней учёбы, необходимой для освоения C++ и от прочих слциальных институтов. Linux, он, как бы, не про это, а про индивидуальную свободу и независимость. Сносный компилятор C можно написать с нуля в одиночку за приемлемое время и раскрутить на этом компиляторе GNU/Linux. Написание компилятора C++ современного стандарта - это отдельное приключение длинною в десятилетие.

p.s. прошу прощения за грамматические ошибки

НЛО прилетело и опубликовало эту надпись здесь

Некорректный пример, потому что линуксовое ядро прямо заточено под сборку на GCC. Там используется куча gcc-шних расширений где только можно. Вообще, говорить что ядро линукса написано на C - не совсем верно.

В то же время есть приложения которые собираются хоть gcc, хоть icc, хоть Keil C.

НЛО прилетело и опубликовало эту надпись здесь

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

На си это нормально получается, хотя в каком-нибудь расте еще красивее.

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

интуитивно создавать все объекты на куче потому что язык не позволяет сделать это на стеке это не эффективно. И разыменовывать каждый раз указатель чтобы пойти к значению это тоже неэффективно. Зачем вообще рассуждать об эффективности, если вы не понимаете как методы в плюсах вызываются?

интуитивно создавать все объекты на куче потому что язык не позволяет сделать это на стеке это не эффективно.

В каком месте C не позволяет создавать объекты на стеке? О чем вы вообще? К тому же, у всех линуксовых объектов очень длинное время жизни, их нет смысла создавать на стеке.

И разыменовывать каждый раз указатель чтобы пойти к значению это тоже неэффективно

А как вы собираетесь вызывать виртуальные методы? В C++ точно то же самое - полезли в vtable, нашли указатель на метод.

Зачем вообще рассуждать об эффективности, если вы не понимаете как методы в плюсах вызываются?

О, а вы можете рассказать как вызываются методы в плюсах? Вот например я хочу вызвать C++ метод из ассемблерного кода. Как мне это сделать? (вопрос с подвохом, да).

В С эмулируют инкапсуляцию используя pimpl повсеместно, что не даёт возможности создавать на стеке. И там же образуется лишний дереференс при доступе к объекту

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

Из ассемблера вызывайте че хотите как хотите, мне это не нужно

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

Почему? Я не могу создать структуру на стеке? Или о чем вы?

Из ассемблера вызывайте че хотите как хотите, мне это не нужно

Ядру линукса это нужно. Мы же тут говорим о том, что линукс мог бы быть написать на C++, не так ли? Вот ваши слова:

У линукса нет объективных причин не использовать С++.

Раз уж нет объективных причин не использовать, то расскажите про interop с ассемблером.

Почему? Я не могу создать структуру на стеке? Или о чем вы?

Когда нибудь вы поймёте

 Мы же тут говорим о том, что линукс мог бы быть написать на C++, не так ли? Вот ваши слова:

Где я это сказал? Откуда вы это взяли вообще? Мои слова, это то что у Линуса Торвальдса нет никаких реальных оснований не допускать С++ в линукс.

Когда нибудь вы поймёте

Это не ответ, а риторический прием. При чем - плохой риторический прием. Расскажите мне что именно C не позволяет создавать на стеке и чем это плохо. Или покажите где почитать об этом.

Мои слова, это то что у Линуса Торвальдса нет никаких реальных оснований не допускать С++ в линукс.

Может все таки есть? Вот например - отсутствие стандартного ABI и проблемы взаимодействия с низкоуровневым кодом из-за этого.

Давайте возьмем простенький пример: https://wandbox.org/permlink/SCa50brKPVZfghMF

Интересно посмотреть на объем кода на чистом Си, который бы делал бы тоже самое.

Не зачет. name -- это метод.

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

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

Только это ж банальный геттер который возвращает константу. 

Кто вам сказал? Метод в базовом классе объявлен как виртуальный.

Ах да, наследование в C++ не позволяет переопределять значения полей...

Это вы сейчас о чем?

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

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

Да и тот повторить не смогли 1-в-1.

Кто вам сказал? Метод в базовом классе объявлен как виртуальный.

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

Это вы сейчас о чем?

Именно о том, что в C++ у вас по другому не получится переопределить значение свойства при наследовании. Только вводить метод-геттер, что вы и сделали.

Да и тот повторить не смогли 1-в-1.

А была цель повторить 1:1? Вы просили код

который бы делал бы тоже самое.

Я привел код который делает то же самое. Предоставляет интерфейс Animal который позволяет узнать имя конкретного класса и вызвать метод.

Ну по факту он возвращает константную строку.

Если уж говорить про факты, то

a) возвращается динамическая строка;

b) функция use_animal завязана на то, чтобы вызывать name у наследников и этот name может иметь разные реализации.

Ничего подобного у вас в коде нет. Хотя докапываться до `const char*` вместо std::string у меня желания и не было. Хотелось просто увидеть в коде ручное проставление нескольких указателей на "виртуальные" методы, но это оказалось слишкамсложна.

Именно о том, что в C++ у вас по другому не получится переопределить значение свойства при наследовании.

Давайте еще раз и так, чтобы было понятно не только вам. А то складывается впечатление, что C++ вы и не знаете.

А была цель повторить 1:1? Вы просили код

Который бы делал тоже самое. Тоже самое. А не то, что вам оказалось проще.

Я привел код который делает то же самое.

Нет.

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

Давайте не будем грести всех Сишников под одну гребенку.

Который бы делал тоже самое. Тоже самое. А не то, что вам оказалось проще.

Очевидно, что если взять язык B и попросить сделать на нем "точно то же самое" что было сделано на языке А, то получится дикая каша. Потому что это разные языки.

Я утверждаю что моя программа семантически эквивалентна вашей.

Нет.

Okay...

Почему то на С++ можно сделать то же самое что на С, а вот на С то же самое что на С++ не получается, интересно...

Сделайте мне на C++ per_cpu переменную как в ядре линукса. И нет, это не thread local storage, это именно per CPU.

Я утверждаю что моя программа семантически эквивалентна вашей.

Вот только элементарных проверок ваше утверждение не проходит.

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

Для знающего C++ там все очевидно:

  • есть интерфейс;

  • в этом интерфейсе есть две виртуальные функции. Причем виртуальную функцию action нельзя применять к константному объекту (указателю/ссылке);

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

  • есть функция use_animal, которая завязана на этот интерфейс;

  • есть два наследника, которые реализуют этот интерфейс;

  • наследники создаются на стеке, но передаются в use_animal, при этом use_animal ничего не знает о наследниках.

Я тут даже не буду заострять внимание на то, что name возвращает созданную в динамической памяти строку. И на то, что наследников Animal можно безопасно удалять по указателю на сам Animal. Т.к. к вопросу о создании экземпляров на стеке это не имеет отношения.

Я привел код который делает то же самое

Еще одно важное отличие забыл вписать: у вас нет отдельных типов для Cat и Dog. Т.е. мой пример можно расширить условной функцией:

void walk_dog(const Dog & d);

И в эту функцию нельзя будет просто так передать Cat. А у вас вообще понятия Dog нет.

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

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

https://godbolt.org/z/PMMeGMevT

НЛО прилетело и опубликовало эту надпись здесь

Одна из характеристик интеллекта — это способность разрешать неоднозначности и понимать абстракции. Если так уж хотите, то рассмотрите такой пример: https://wandbox.org/permlink/Wuz2ahyHUGXb1V74

Хех. Ну окей. Я могу завести отдельную структуру animal_ops и держать там поинтеры на разные функции. Будет совсем такой vtable как хотел ОП.

Ну и да, очевидно что это не так безопасно по типам как было в C++.

Не понял.

О, вот сразу бы так :)

У вас vtable неправильная, переделывайте. Во первых метода не 3 а 1, во вторых не указатель хранится, а сам vtable, ваш код значительно менее эффективен.

А мы тут собрались копировать плюсовую имплементацию 1:1 или повторить семантику? Семантика интерфейса повторена, а то что оно ведет себя не как код на C++ - так это и понятно. Оно ж написано не на C++.

Я могу повторить тот же код на Python например. Там вы тоже будете предъявлять претензии к тому что vtable не такой?

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

И эффективнее, понятнее и короче код чем в С

https://godbolt.org/z/6zc5Ync3x

Спойлер - не получится у вас

Ну тогда линуксом не пользуйтесь, там небось тоже много кода

Ну или напишите аналогичную библиотеку на С. ОЙ, у вас не получится .. Потому что это невозможно..

Ну смотрите. Вы привели пример, который с помощью дикой шаблонной магии на полторы тысячи строк смог сгенерировать ассемблерный код на 6 инструкций короче чем простая программа на С размером в 50 строк кода.

Это конечно интересный trade off. Есть чем гордиться.

Если я покажу другому программисту на C++ вот этот весь код - как быстро он поймет что там происходит?

Эта "дикая шаблонная магия" может генерить бесконечное множество подобных кодов. Она для того и создана. Вместо того чтобы писать это руками(никто никогда не сможет писать это руками), она делает эффективный, понятный и компилятору и разработчику код. С её помощью можно не только cat и dog сделать, а всё что угодно.

Потенциально эта машина всего в 1000 строк сгенерирует вам миллиарды строк качественного кода. В этом и смысл.

Если вам нравится раз за разом писать бесполезную херню к тому же абсолютно нерасширяемо и неправильно(тут вам указали на громадные недочёты в вашей vtable), то можете конечно продолжать. А стоило бы уже начать использовать С++

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

ага, только у нее две огромные проблемы. Это криптоошибки при неправильном применении шаблонов и очень долгое время компиляции. Почему какой-то чертов Rust собирает большой проект в 10 раз быстрее, чем средний проект на крестах с шаблонами ? Почему в пайтоне я получаю результат гораздо быстрее, чем в С++ ? Да даже джава - ей не нужна самая современная машина для компиляции кода

  1. Нормальные там ошибки если вы не то пишете

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

  3. Раст компилируется дольше С++, это просто факт.

  4. В питоне вы получаете результат дольше, потому что он работает дольше...

Вот про "нормальность" и "факт" хотелось бы с доказательством, а то как-то совсем голословно получилось...

Покажите мне любой сишный код и я укажу, что вы инклудите тонны кода. Но это вас почему то не смущает

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

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


И нет, использование чего-то pimpl-подобного — это осознанное архитектурное решение разработчиков ядра, а вовсе не ограничение языка Си.

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

И где там потеря производительности? Что вызывать функцию через vtable, что по указателю - один хрен.

Как-то коммитету по стандартизации С++ получается держать стабильное ABI.

Да? Я вообще не встречал описаного C++ ABI. Тем более в стандарте. Насколько я знаю, у каждого компилятора свой ABI. Грубо говоря, вы не сможете статически слинковать вместе объектные файлы сгенерированные gcc и msvc. MSVC не поддерживает стабильное ABI даже между версиями, кстати: https://stackoverflow.com/a/67844737

и чё? Как это мешает то?

Ну их как бы надо динамически линковать. Делать это не имея стабильного ABI - то еще развлечение.

Лучше покажите как на С сделать "класс" не выделяя его в куче

Кто мне мешает выделить структуру на стеке? :)
Более того, я могу выделить структуру (и вообще любой другой объект) на стеке динамически. Благодаря магии alloca.

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

А в чем проблема? realloc никто не отменял.

Кто мне мешает выделить структуру на стеке? :)Более того, я могу выделить структуру (и вообще любой другой объект) на стеке динамически. Благодаря магии alloca.

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

К тому же код с этим выглядит чрезмерно ужасно

А в чем проблема? realloc никто не отменял.

вот напишите код и увидите в чём проблема, сколкьо можно талдычить очевидные вещи

Это нестандартный С и это ухудшает значительно полученный в итоге ассемблер.

Ну я ж не говорю что это нужно использовать. Вообще ни разу не использовал alloca и не видел чтобы его использовали где-то. Но он есть. И да, ассемблеру x86 например вообще пофиг на такое. Он все равно восстанавливает правильное значение SP при выходе из функции.

вот напишите код и увидите в чём проблема, сколкьо можно талдычить очевидные вещи

Зачем так нервничать? Ну писал я код который использует realloc. Ничего ужасного.

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

НЛО прилетело и опубликовало эту надпись здесь

В ядре Линукс будет Rust. Шляпа вроде С++ с его спагетти та не нужна. К тому же Линус ненавидит С++. Все современные языки отказываются от ООП парадигмы.

Rust не до конца отказался.

Я специально зарегистрировался что бы оставить этот комментарий.
Устал слышать этот бред.
Все говорят С это embeded. Вопрос только когда все так решили?
Вот про это мне интересно почитать статью и даже очень.
Когда все переписали на С++, когда С++ начал жить полноценно. Когда многие репозитории переписали те самые 5-10% С кода?
Я начну с низов, много графических библиотек без С? Так или иначе большинство библиотек с которыми я работаю, основываются на С или частично зависят от него.
Skia / SDL / Qt / GTK / ect.. Вы говорить что С++ с UB это не C++ (тогда что?) потому что это огромное число софта, которое все называют C++. Ладно это мелочи.
Но каждый раз я слышу С этот embeded и меня прям трясет. Потому что наверное большую часть софта с которым я работаю, написано на С и С++. Зачастую вместе. Я не понимаю, почему люди так открещиваются от С и говорят это только embeded. Хотя наверное эти люди считают embeded всем? От микроконтроллеров до графических систем вроде OpenGL / Vulkan. GUI, Gamedev, OC, Сервера, прокси, другие языки программирования и так далее можно долго. Зачем я все это написал?
Обидно слышать что С это embeded, а без него... Я бы не имел софта которым пользуюсь.
И пользуются люди по всему миру!

Что раньше появилось курица или яйцо? Да это и не важно, но как мне видится.
Мир без С++ будет сложен, а без С невозможен. Мое личное мнение!

Какой смысл сейчас использовать С там, где существует компилятор С++?

Какой смысл ...

А это смотря что Вы понимаете под "смыслом". Та или иная парадигма, тот или иной язык выбирается не по хайпу (хотя Вы именно за это и топите), а по задаче. В этом смысле структурный подход может оказаться проще и эффективнее любого другого для данного конкретного проекта. А ООП - для другого конкретного проекта. Можно, конечно, делать вид, что компьютера как двоичного автомата не существует. Но я Вас удивлю. Любая, даже самая продвинутая, модель данных - это всего лишь тупой набор байтов. Доказательство - скомпилированный код. Взгляните на досуге. И если Вы действительно хотите отдавать себе отчёт в том, что Вы делаете, то без этих байтиков никак. А уж декларативно или императивно - определяется, повторюсь, задачей, да ещё традициями команды, пожалуй.

НЛО прилетело и опубликовало эту надпись здесь

А вы бы могли поделиться с нами на что лучше бы потратили своё время, как личное так и профессиональное. Я думаю что многим, особенно те кто мечтает "войтивайти" за баблом, будет познавательно на что всё-таки тратить свои лучшие годы и энергию. Какой совет вы бы дали себе 20 летнему?

НЛО прилетело и опубликовало эту надпись здесь

презираю ООП (потому что, ИМХО, это очень неудачный базис для разложения программ)

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

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

Есть смысл, на самом деле. C - это lingua franca. Когда нужно дружить друг с другом код на разных языках, зачастую все сводится к предоставлению C-интерфейса. И может быть в имплементации этого интерфейса будет уже C++, go, rust, ассемблере или вообще чем-то экзотичном. Но интерфейс будет на C потому что подцепиться к нему можно из максимально широкого круга языков. Писать весь проект на C, мало кто будет, но какие-то части регулярно получаются написанными на C или хотя бы представляют из себя интерфейсы на C.

extern "C"

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

Потому что на каждой платформе C предоставляет стабильный ABI: как представлять типы в памяти, как вызывать функции, как передавать в эти функции параметры, как возвращать результаты. Никакой другой язык это не делает.

Это может конечно вкусовщина (да кого я обманываю, так и есть) но C++ достаточно перегружен и в некоторых задачах его мощь и сложность просто ненужны. Кресты иногда сподабливаются пушке по воробьям — дорого, тяжело и глупо.

НЛО прилетело и опубликовало эту надпись здесь

Вы немного спутали вопрос (не в последнюю очередь благодаря неточности моей речи) одно дело писать небольшие (или любые вообще) программы в которых не предполагается изменение, долгая поддержка и т.д. то бишь утилиты, и совсем другое проекты, которые вы планируете расширять и улучшать. В первом случае есть повод не заморачиваться с абстракциями вроде std::vector и ему подобными, а сделать тупо массив, есть повод не использовать ООП и другие достижения цивилизации.
Совершенно другое дело, писать программы с заделом на будующее. В таком случае использование абстракций вполне оправдано. И C++ оправдан в таком случае.
Говоря языком метафор: вам не нужен отбойный молоток, чтобы забить гвоздь в стену. А коли вы возьмётесь за серьёзное строительство, тогда да, почему бы и не воспользоваться таким громоздким инструментарием.

Наверное люди просто ненавидят С++, раз он такой классный во всех отношениях, но всё равно не смог за десятилетия заменить С во всех нишах.

НЛО прилетело и опубликовало эту надпись здесь

Наверное люди просто ненавидят С++

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

Я ненавижу плюсы (потому что потратил на их тонкости, совершенно не переносящиеся на любой другой язык или деятельность, этак 17-18 лет жизни из своих 31, включая самые сочные, когда я мог бы ботать матан или, в конце концов, с девочками за ручку ходить)

Так что да, фактор "не люблю и все" имеет место быть.

НЛО прилетело и опубликовало эту надпись здесь

С это embeded

А эти люди сейчас с нами в одной комнате?

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

По такой же логике можно сказать, что нет смысла начинать новый проект на C++. Особого профита вы не получите, но зато получите кучу потенциальных ошибок)

Я такого рода утверждения уже больше 10 лет постоянно слышу. А люди зачем-то всё начинают и начинают.

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

Ну, вот это точно не про С++. Это вам не Haskell, не Idris и даже не Rust в плане системы типов.

Я аналогичного мнения насчёт c++ придерживаюсь.

Я такого рода утверждения уже больше 10 лет постоянно слышу. А люди зачем-то всё начинают и начинают.

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

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

Просто по опыту, при программировании на Си ошибки в типах - не самые часто встречающиеся ошибки.

По-моему опыту самые частые ошибки - это segmentation fault, выход за границу массива, переполнение числовой переменной, ошибки при работе с указателями. Плюс нужно следить, что твой код не является UB. Ко всему этому ещё и алгоритмические ошибки добавляются.

Как и при программировании на языке с более сложной системой типов,
основные ошибки алгоритмические. А от этого никакая система типов не
спасёт.

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

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

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

>>Все говорят С это embeded.

полагаю вы это понимаете как "непригоден для чего-либо кроме embed", но говорящие так [опять же полагаю] имеют ввиду что то типа "он тут ненужен".

>>Вопрос только когда все так решили?

а потому и непригоден ненужен он стал, когда стало модным реализовать GUI посредством вкрячивания целого браузера в каждый второй hello world и не абы какого а свежего хромиума, заказывая верстку у джунов пашущих "за еду"... вспомните во что превратились тот же skype, wot...

помнится, как раз тут на хабре лет эдак много назад кто-то хвастался как "изящно" парой строк кода призвал уведомление в трэй. только эти его пара строчек кода скомпилировались в exe весом 50+Мб...

впрочем, чего это я на хромиум взъелся... сейчас же контейнеризация в моде, а это на минуточку, развёртывание по целой ОС на каждое приложение *pokerface*. и ведь с точки зрения ИБ, только так и надо...

Добавьте еще nginx.

Видимо тоже "emdedded"

В вакансиях указывают C/C++ когда ищут человека на проект, в котором тонны спагетти кода, которые писали не очень хорошие си-программисты на языке «си-с-классами».

А вообще я сам сейчас на проекте где есть и чистые си приложения и приложения на хорошем с++. Скажу по себе - действительно очень сложно переключаться с си++ мышления на си. Очень не хватает шаблонов и деструкторов. Но постепенно начинаешь пользоваться определенными практиками и жить становится можно, хоть и неудобно. Не считаю себя хорошим си программистом, после си++ на нём очень тяжело писать (хотя когда я начинал, я писал на чистом си/винапи)

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Я, возможно, выражу общее мнение:

"И чо?"

Мне кажется C/C++ появился с подачи Microsoft. В свое время Visual C был именно таким мутантом, толком не поддерживающим ни тот и не другой стандарт. При этом код стандартных библиотек представлял собой ацкую смесь с элементами обоих языков (а может где-то и сейчас так).

Ждем когда они отчебучат Rust/Verona

Во-первых, 95% Си совместимы с плюсами. При должной аккуратности можно писать код совместимый с обоими языками, причём ifdef потребуется только для extern "C", если нужна интероперабильность. Просто надо будет расставлять больше приведений типов и не использовать пару сишных фич.

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

В итоге либо нужен только Си, либо оба языка.

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

При этом знать goto/setjmp на Си, мне кажется, надо почти всегда, а вот в C++ без этого можно довольно долго жить.

Зачем писать на C, когда можно делать современные хеловорлды на гигабайт оперативки, вскипячение восьмиядерника и запускающегося на оси не ниже 100500й версии?

Если без горького сарказма, то всему своё конечно. Но лично мне нравится подход к С++ как к C (с дурацкими байтами) с ООП классами. И соответственно мышлением где равноважными являются оба компонента - и предметная область и её формализация через ООП, и забота о эффективности по железу, мышление на уровне проца, байт оперативки, байт i/o с винтов и сети, эффективными алгоритмами, бенчмарками.

Без унавоживания слоями чрезмерных абстракций и прочих модных шуточек вроде "для сложения двух интов давайте подтянем 1042 библиотеки на пару гигов сурцов". Либы безусловно местами незаменимая штука, но может попытаемся не терять разум?

[ C <3 C++ ]

PS: Еще ассемблерные вставки рулят! Asm 4eva !

вот только С++ эффективнее С за счёт больших гарантий и большей выразительности кода

Когда у нас в стране (перед лучезарным взлётом отечественного железа) будет период пятилетки-другой с двумя миллионами тыжпрограммистов на 200 000 продакшн машин и устаревающим парком техники юзернеймов, критерии эффективности всё же явно будут пересмотрены )

А прямой ответ на коммент - да, спички детям не игрушка. Но ты же ого раз-ра-бот-чик! Если тебе дали пистолет, ну не стреляй им в ногу и в прочее куда не надо (=

С++ компилируется в банально более хороший ассемблер, вы не верите?

Я тут недавно специально сравнивал -- не компилируется. На Си, конечно, тоже надо уметь писать.

А может на С++ вы писали что то странное?

Ага, значит надо писать что-то "нормальное"? Можете дать какие-то критерии как отличить "странное" от "нормального"?

покажите код

Это у @mikhanoid надо спрашивать

С++ компилируется в банально более хороший ассемблер, вы не верите?

Нет.

Как разработчик на С++ я тоже в это не верю. Возможно автор комментария про какие-то узкие кейсы по типу sort() который на C работает с void* и не может оптимизировать под конкретные типы и все такое. Но если не читерить с кейсами
«вот мы на С++ сделали шаблонный код, а на С такое руками писать утомительно поэтому сделаем тупо и неоптимально» — то не вижу причины с чего бы на С++ коду быть быстрее.
С++ эффективнее чем С, да. В выразительности, в скорости написания чего-то большого что надо поддерживать долго и толпой мало связанных меж собой разработчиков, я думаю множество средств где в поддержке он у С может выиграть.
А вот касаемо эффективности кода, ну чет такое. Слабо верится. Только в частностях.

Чистый Си будет всегда почти быстрее. В С++ больше абстракций, а значит больше прослоек и больше низкого когда в который он транслируется.

Чистый Си будет всегда почти быстрее.

Отличная оговорка. Именно такой результат и будет. Вместо "почти всегда быстрее" как раз и получим "всегда почти быстрее".

НЛО прилетело и опубликовало эту надпись здесь

Еще ассемблерные вставки рулят!

Это пока программа запускается на одном компьютере типе процессора, а если говорить об оптимизации - на одной модели процессора. Потом через какое-то время ВНЕЗАПНО начинает рулить java. Или даже python.

НЛО прилетело и опубликовало эту надпись здесь

А почему вы интерпретируете обозначение "C/C++" как язык? И наоборот, с точки зрения теории формальных языков, почему объединение всех возможных цепочек языков C и C++ не будет языком? С чего вдруг операция объединения языков перестала быть замкнутой в полукольце языков?

Символ "/" тоже следует интерпретировать как концепцию совместного использования или варьирования. Есть ведь ещё и CLI-расширение языка C++ для .NET. И как тогда прикажете сообщать, что требуется (кандидат может/программа написана с) применять и C, и C++, и CLI?

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

Если копнуть ещё глубже, и посмотреть, как устроены живые системы (их изучением занимается системная биология), то выяснится, что природа использует носителей, максимально эффективно накаченных функциональностью. Т.е. и на уровне атомов, и на уровне молекул, и на уровне макромолекул, не говоря уже про их комплексы, метаболиты, в частности. Даже одна молекула (определена структурной формулой) часто проявляет множество биологических активностей. Однако, совмещение функциональности на одном носителе (объединение языков, например) многими осуждается в программировании. Как и совместное использование разных стилей. Хотя оракловые SQLJ как и PL/SQL очень даже удобны и эффективны.

Проверим этот подход сравнением по уровню сложности ИТ-систем, созданных людьми, с живой системой, да хотя бы человеком. У человека собственных клеток порядка 37 триллионов, и 23 триллиона живущих с ними в симбиозе. Одна клетка -- как небольшой завод на полном вертикальном обеспечении: включает и выработку энергии, и производство, и доставку к местам трансформаций всех компонент (от базовых деталей, т.е. простых молекул и аминокислот до молекулярных роботов, коими выступают белки и метаболиты, в частности), требующихся для жизнедеятельности клетки. А главное (о ужас!), в каждой клетке одна и та же ДНК, служащая исходным кодом (система нуклеотидных последовательностей: уложены в хромосомы), хотя многобразие клеток очень велико. И стволовые клетки могут трансформироваться в большое разнообразие других клеток. Т.е. природа несравненно превосходит по сложности всё то, что пока человеческий мозг смог сконструировать и понять. И это ещё не касались устройства систем управления на разных уровнях, особенно нервной системы и мозга. Природу нисколько не заботило требование понятности для кого-либо: функциональность важнее.

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

НЛО прилетело и опубликовало эту надпись здесь

И чтобы эту неоднозначность устранить, есть extern "C". А может быть и extern "SQL", и много других языковых приёмов. Более того, многозначная интерпретация не всегда взаимоисключающая, и есть масса случаев, когда она одновременно верная. А взаимоисключающая интерпретация часто возникает даже в одном языке, для чего вводят дополнительные факторы: пресловутый пример с if-else. Многозначная интерпретация и её обработка -- основная проблема разработки таблицы трансляции, т.е. перевода некоторой комбинации из элементов языка во что-либо (другую цепочку или действие). Сложность этих комбинаций и их многообразие является следствием класса языка.

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

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

НЛО прилетело и опубликовало эту надпись здесь

https://en.m.wikipedia.org/wiki/C2x - даже так. Но не факт, что это всё на пользу языку. Всё же, хотелось бы, чтобы KISS.

Прочитал я статью и задумался, решил перевести на понятный мне "С/С++", дабы ,было легче читать и вот что получилось:

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(); // пора бы уже задуматься 
}

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

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

В общем, хорош вносить смуту в ряды С/С++ программистов, лучше пойдемте вместе побеждать питоноидов. :)

Посыл хороший, но орфографические ошибки и опечатки очень сильно портят читаемость

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

НЛО прилетело и опубликовало эту надпись здесь
Вы пытаетесь открыть публикацию, написанную пользователем F0iL, однако, публикация скрыта в черновики (самим автором или НЛО)
НЛО прилетело и опубликовало эту надпись здесь

Это как путать -тся и -ться. Слова выглядят одинаково казалось бы, но смысл совершенно разный.

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Так и до C\C++ недалеко.

М\Ж

Что и говорить про провинциальные колледжи и вузы... Особенно веселит когда приходит очередной препод и говорит: "Сейчас, Я вас буду учить программированию, показывает Си-шный код и говорит мы будем изучать С++". А ты у него спрашиваешь в конце лекции: "...так мы изучаем Си или С++, и можно ли писать в C++ стиле?" - он смотрит на тебя с недоумевающим видом и либо переспрашивает, либо начинает поднимать тебя на смех.

Какой ты крутой перец. Бунтарь просто.

Потом я вижу таким студентов на форуме, которые суют в Си-шный код всё подряд, начиная с инклюдов С++. Это, наверно, называется писать в стиле С++.

НЛО прилетело и опубликовало эту надпись здесь

Наверно имеется ввиду, что человек должен знать С и С++, что не удивительно.
Вообще было забавно читать весь этот бред о том, что нельзя реализовать простой вектор на С, потому что язык тебе этого не даст сделать.
Столько программ написано на С, все *nix на нём. А у некоторых вектор не получается реализовать. Что это может говорить? Только об уровне владения знаниями авторов подобных утверждений.
И как говорил старина Торвальд - "C++ можно использовать только если выкинуть из него всё д*рьмо, чтобы остался по итогу чистый С" )

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

ЕМНИП была какая-то техника обмазывания дефайнами, которая сводит копипаст программиста (но не препроцессора) к нулю; наверно, и ошибки препроцессором можно проверять.

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

НЛО прилетело и опубликовало эту надпись здесь
Самый удобный «вектор» на C — это «stretchy buffers».
ourmachinery.com/post/minimalist-container-library-in-c-part-1

Динамический массив будет выглядеть и работать как обычный указатель:
struct Foo *array;
Дополнительные данные (size, capacity) лежат перед первым элементом.
И никакого дублирования кода.
НЛО прилетело и опубликовало эту надпись здесь
А как сделать так, чтобы [...] компилятор выдавал мне ошибку, если я туда попытаюсь засунуть что-то ещё или прочитать что-то ещё? stb_sb_push не выглядит особо проверяющим типы.

Типобезопасность в C? На святое покушаетесь? :)
Но если очень хочется, то можно (в GCC):
gcc.godbolt.org/z/PbvbPn8KG

Можно конечно использовать бомж-вариант с проверкой sizeof(), либо мутить магию с _Generic.
НЛО прилетело и опубликовало эту надпись здесь

А вы сами когда-нибудь пробовали реализовать хоть какую-нибудь коллекцию?

НЛО прилетело и опубликовало эту надпись здесь

Конечно можно. Я и сам его писал. Вектор - это просто динамически расширяющийся массив в куче, который перераспределяет память когда при добавлении новых элементов его длинна начинает превышать его ёмкость.
Про то, что это невозможно могут говорить только какие-то упоротые питонисты или джависты, которые память никогда в глаза не видели.