Comments 383
Как и Java/JavaScript
Вы не поверите, но в 2019 году помню, на огромной американской корпорации на курилке разговаривал с JS-разработчиком. И он мне рассказывал что Java - это браузерный движок для работы JavaScript. Помню, у меня тогда так же подгорало как у автора этого поста
VBScript от Visual Basic отличается примерно так же, как JavaScript от Java: синтаксис немного похож, на этом сходства заканчиваются.
когда учишь ЯП, особенно первые - то сходство синтаксиса первично, чтобы сказать что языки похожи. И людей которые "плохо знают"* язык гораздо больше чем людей которые "хорошо знают"** язык, а людей которые используют эти термины и "вообще не знают" язык - соизмеримо, имхо. из этого и выходит c/c++, т.к. реальность формируют не только умельцы, но и те кто дают базу, сворачивают с разработки, конкретно с этих ЯП и тд. Даже взять если хабр, автора статьи поймут многие, но сколько статей и комментов написано с подачи что c/c++ похожи. Плюс есть еще "с-подобность", да и сравнение идет с этими ЯП т.к. их давали многим кто в итоге остается в программировании.
*, ** - абстрактная субъективщина с тысячей около истинных определений
а как же VA-script?
В свое время Microsoft'у запрещали называть свой интерпретатор словом JavaScript из-за того, что они пытались творить там несовместимые вещи. Поэтому язык назывался JScript. VbScript и JScript входили в состав компонента Windows Script Host (WSH), но также были доступны из IE.
видимо это обозначение фулстэк разработчика
Но уж язык Java/Kotlin точно существует?
Не-а. 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. И если мне не изменяет память, все те выразительные инструменты (в т.ч. для работы с памятью) которые предлагает С++ не особо хорошо натягиваются на некоторые задачи (программирование микроконтроллеров, например).
То есть в моем понимании, если надо дикий перформанс и/или присутствуют жесткие ограничения по железу (ATtiny85 - 8KB flash, 512B RAM, 20MHz), то те же итераторы и умные указатели (насколько я помню, C++ Core Guidelines говорят о ручных выделениях и освобождениях памяти как о анти-паттерне) - не вариант. А иначе - это уже не каноничный С++.
Итераторы это абстракция с отрицательной стоимостью, они не делают ваш код медленнее, наоборот ускоряет. unique_ptr только в некоторых случаях на некоторых платформах и АБИ будет иметь маленький оверхед. shared_ptr просто невозможен в С, замучаетесь.
И да, указатели на умные, а владеющие. Это не замена T*
ATtiny85 даже среди микроконтроллеров 90х годов запредельный аскетизм, его можно на ассемблере программировать. Современные микроконтроллеры это типично STM32F4, 240МГц, мегабайты флеша, сотни килобайт памяти, он сравним с первыми Пентиумами по производительности.
Не согласен с самой концепцией которую вы вкладываете в "современные микроконтроллеры". У каждого производителя есть несколько линеек микроконтроллеров, каждая из которых имеет свое функциональное позиционирование, где она будет наиболее дешева и востребована. И из этой функциональной иерархии никуда не исчезли сверхдешевые 8-битники, или не производительные и емкие, но очень экономичные Cortex-M0/-M1.
Другое дело, что в некоторых случаях удобнее брать самый жирный чип "для всего", и потом уже думать над удешевлением. Но это экономия на инженерах.
Еще есть вариант с ПЛИС/FPGA, там вообще нет программы в обычном понимании, набор логических элементов, от 240 штук в простых микросхемах до миллионов в более сложных. Это если смотреть в сторону упрощения управляющей логики. Тоже свои плюсы и минусы.
Дикий аскетизм это pic12 серия. 64 байта озу/1024 слов флеш программы/128 байт еепром. Прекрасно программируется на С (компилятор xc8 от микрочип). МК выпустили в начале 2000х, до сих пор много где применяются, особенно в китайских изделиях. Но это реальная мука. Аттини гораздо лучше и удобней.
Вроде он для ассемблера удобен. Мелкие МК идеальны для программирования на ассемблере, каждый байт под контролем и программа не сможет увеличится в объеме так, что потеряется общая картина происходящего. А вот STM32 на ассемблере уже не попрограммируешь, энтузиасты пробовали, но быстро интерес пропал судя по форумам.
Микрочип сделала очень хороший компилятор и вроде пользуешься синтаксисом С, а вместо переменных идут имена регистров, а в значения пишешь битовые маски и отдельные биты, через имя регистра и добавления к нему слова bits. Например, недавно делал хобби проект для друга на PIC12F675. Нужно было всего лишь менять состояние трех цифровых выходов, два на светодиоды (или один rg светодиод), один на управление через транзистор якорем реле, переключающего обмотки трансформатора паяльной станции. Один вход на кнопку. Также нужно было запоминать состояние на момент выключения питания. Сделал на С - заняло 17% озу, 16% программной памяти и два первых байта eeprom. На ассемблере это приличная партянка, на С гораздо удобнее и код читается на "раз-два". И кроме того, здесь не то что stm32, здесь даже avr328p будет избыточным. Моё мнение - или 8ми ногий аттини или пик.
дикий аскетизм — это attiny12 (емнип), там вообще RAM отсутствует, 32 регистра и все :)
деструктор в некоторой степени можно назвать примером декларативного программирования
Кстати, а как функциональное программирование является императивным стилем? Какой смысл вы вообще вкладываете в эти слова?
Что подразумевается под императивным стилем? Это последовательное выполнение инструкций и переиспользование результатов этих инструкций. Чем функциональное программирование в данном случае отличается, я имею ввиду реальные функциональные яп, в идеале функциональные языки являются декларативными, но по факту получается что мы всё также описываем последовательность инструкций которые должны последовательно выполнятся (я не беру в расчёт возможность перестановки или кеширования результатов компилятором\интерпретатором т.к. это есть и в С и в С++). Возможно моё понимание декларативного стиля не соответствует с обще принятым, но для меня всё это выглядит как просто вызов функций которые внешне декларативные но внутри всё тажа императивщина
По такой логике под любым языком машинный код, значит всё императивность. Но это очевидный бред, т.к. декларативность в том как ты пишешь код, а не как он исполняется.
К тому же если взглянуть на 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
).
Как минимум kernel в том же CUDA Вы пишите именно на C.
К примеру из недавно встретившегося
github.com/NVIDIA/nvcomp/blob/branch-2.2/src/RunLengthEncodeGPU.cu#L162
CUDA ядра очень давно поддерживают фичи из С++, такие как классы, лет 10 минимум.
>А с точки зрения хорошего С++ кода практически ничего из С использовать не нужно
...но линуксовые API всё ещё написаны на C. Да и в целом, если необходимая тебе библиотека написана на С, то С рано или поздно в твоём cpp-коде окажется.
Собственно, несмотря на то, что C и C++ - разные языки, у них есть общее подмножество, которое почти полностью совпадает с С.
(без ката, пусть кто-то всплакнет)
Но еще, помню основы систематизации беспозвоночных из Догеля, которые, с 90х годов, уже несколько раз успели поменяться.
Систематизация — постоянно развивается. То, что раньше считалось отрядом — теперь считается подклассом. И наоборот. Можно продолжать хвататься за старое, но современные специалисты вам просто не поймут
Уже давно не подмножество. В плюсах нет как минимум restrict и _Generic (действительно, зачем он когда есть шаблоны?), да и код без них не всегда может быть скомпилирован обоими компиляторами из-за разных ограничений на приведение типов (тут я конкретный пример, впрочем, не вспомню).
C это подмножество C++
Псст, хотите немного уличной магии!
1) Преобразование int к enum
enum { e0 = 0 } e = 0; // валидно в C, но не в C++
2) Неявное преобразование void*
void* pv = 0;
int* pi = pv; // валидно в 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() >> ¶m_t::name,
ows(),
symbol('='),
ows(),
produce< param_value_t >(
alternatives(
token_p() >> convert( token_to_v ) >> as_result(),
quoted_string_p() >> convert( qstring_to_v )
>> as_result()
)
) >> ¶m_t::value
)
) >> as_result();
return produce< authorization_value_t >(
token_p() >> to_lower() >> &authorization_value_t::auth_scheme,
maybe(
repeat( 1, N, space() ),
produce< auth_param_t >(
alternatives( token68_seq, params_seq )
) >> &authorization_value_t::auth_param
)
);
}
Код не для статьи написан, он реально живет в нашей библиотеке.
Это функциональный подход, только причём тут декларативность? Тут ясень порядок выполнения инструкций, он чётко определён вами. Если бы написали что-нибудь в духе
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 функций. Кроме этого, описанный вами миф развенчивал ещё Страуструп в своей книжке, почитайте его на досуге.
Когда я работал преподавателем программирования на УПК в 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+ лет"?
если нет, предлагаю закончить, не в возрасте дело, адреналина от демонстрации технических знаний не получал никогда, в том числе когда было столько лет, сколько Вам сейчас, типа более интересные занятия были
Согласен. Сам таким подходом, как вы описали выше, отличаюсь от коллег. Никогда не понимал этого дрочева на "в стандарте же явно не оговоренно", но и не запрещено же. Да иногда оговариваются, что "будьте внимательным к данным, тут могут быть неприятные побочные эффекты" - так по моему опыту, пусть не выдающемуся, когда ты вступаешь на тропу С, а следом движешься к С++, то внимание к данным становится настолько крепкой привычкой, что эти холливары уже не имеют никакого значения - с такого уровня всегда ясно в чем суть вещей.
примерно понимаю, лопата придумана чтобы траншеи копать и каналы строить, конечно документацию на лопату тоже надо знать, включая стандарт как reference, и особенности компилятора, imho особенно гордиться здесь не чем, в конце концов стандарт люди создали, и компилятор как правило кто-то другой написал, типа чужая работа, лучше что-нибудь свое как следует сделать, пусть даже чужой лопатой :)
Исходя из изложенного материала, например, программа написанная на чистом 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. прошу прощения за грамматические ошибки
Там только создают объекты из структур, без наследования и прочей ООП чепухи.
На си это нормально получается, хотя в каком-нибудь расте еще красивее.
А чем вам не понравились классы в ядре? Их там не много, и они весьма в удачных местах. Например, класс с файловыми операциями. А на счет производительности... нужны бенчмарки. Как-то интуитивно выглядит как раз наоборот. В Си это просто разыменование указателей для вызова функций. А в плюсах?
интуитивно создавать все объекты на куче потому что язык не позволяет сделать это на стеке это не эффективно. И разыменовывать каждый раз указатель чтобы пойти к значению это тоже неэффективно. Зачем вообще рассуждать об эффективности, если вы не понимаете как методы в плюсах вызываются?
интуитивно создавать все объекты на куче потому что язык не позволяет сделать это на стеке это не эффективно.
В каком месте C не позволяет создавать объекты на стеке? О чем вы вообще? К тому же, у всех линуксовых объектов очень длинное время жизни, их нет смысла создавать на стеке.
И разыменовывать каждый раз указатель чтобы пойти к значению это тоже неэффективно
А как вы собираетесь вызывать виртуальные методы? В C++ точно то же самое - полезли в vtable, нашли указатель на метод.
Зачем вообще рассуждать об эффективности, если вы не понимаете как методы в плюсах вызываются?
О, а вы можете рассказать как вызываются методы в плюсах? Вот например я хочу вызвать C++ метод из ассемблерного кода. Как мне это сделать? (вопрос с подвохом, да).
В С эмулируют инкапсуляцию используя pimpl повсеместно, что не даёт возможности создавать на стеке. И там же образуется лишний дереференс при доступе к объекту
Виртуальные таблицы в С++ хотя бы не создаются вручную, что уже громадное преимущество.
Из ассемблера вызывайте че хотите как хотите, мне это не нужно
В С эмулируют инкапсуляцию используя pimpl повсеместно, что не даёт возможности создавать на стеке.
Почему? Я не могу создать структуру на стеке? Или о чем вы?
Из ассемблера вызывайте че хотите как хотите, мне это не нужно
Ядру линукса это нужно. Мы же тут говорим о том, что линукс мог бы быть написать на C++, не так ли? Вот ваши слова:
У линукса нет объективных причин не использовать С++.
Раз уж нет объективных причин не использовать, то расскажите про interop с ассемблером.
Почему? Я не могу создать структуру на стеке? Или о чем вы?
Когда нибудь вы поймёте
Мы же тут говорим о том, что линукс мог бы быть написать на C++, не так ли? Вот ваши слова:
Где я это сказал? Откуда вы это взяли вообще? Мои слова, это то что у Линуса Торвальдса нет никаких реальных оснований не допускать С++ в линукс.
Когда нибудь вы поймёте
Это не ответ, а риторический прием. При чем - плохой риторический прием. Расскажите мне что именно C не позволяет создавать на стеке и чем это плохо. Или покажите где почитать об этом.
Мои слова, это то что у Линуса Торвальдса нет никаких реальных оснований не допускать С++ в линукс.
Может все таки есть? Вот например - отсутствие стандартного ABI и проблемы взаимодействия с низкоуровневым кодом из-за этого.
Давайте возьмем простенький пример: https://wandbox.org/permlink/SCa50brKPVZfghMF
Интересно посмотреть на объем кода на чистом Си, который бы делал бы тоже самое.
Да легко: https://wandbox.org/permlink/wKY1HxU9zj1G1cHY
Сильно объем кода отличается?
Не зачет. 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++ там все очевидно:
есть интерфейс;
в этом интерфейсе есть две виртуальные функции. Причем виртуальную функцию
action
нельзя применять к константному объекту (указателю/ссылке);оба эти функции являются чистыми виртуальными. Т.е. компилятор не даст нам создать экземпляр типа, в котором не реализована хотя бы одна из них;
есть функция
use_animal
, которая завязана на этот интерфейс;есть два наследника, которые реализуют этот интерфейс;
наследники создаются на стеке, но передаются в
use_animal
, при этомuse_animal
ничего не знает о наследниках.
Я тут даже не буду заострять внимание на то, что name
возвращает созданную в динамической памяти строку. И на то, что наследников Animal
можно безопасно удалять по указателю на сам Animal
. Т.к. к вопросу о создании экземпляров на стеке это не имеет отношения.
Я привел код который делает то же самое
Еще одно важное отличие забыл вписать: у вас нет отдельных типов для Cat и Dog. Т.е. мой пример можно расширить условной функцией:
void walk_dog(const Dog & d);
И в эту функцию нельзя будет просто так передать Cat. А у вас вообще понятия Dog нет.
нет не проще, строка в отличие от вашего случая может создаваться на рантайме. И владеющая. И вы забыли деструктор в таблицу.
Вы изменили логику, а не "сделали быстрее", бенчмарки должны быть идентичными.
Но вы лучше вот это прокомментируйте, как вы сделаете лучше на С(никак)
Одна из характеристик интеллекта — это способность разрешать неоднозначности и понимать абстракции. Если так уж хотите, то рассмотрите такой пример: https://wandbox.org/permlink/Wuz2ahyHUGXb1V74
Хех. Ну окей. Я могу завести отдельную структуру animal_ops и держать там поинтеры на разные функции. Будет совсем такой vtable как хотел ОП.
Ну и да, очевидно что это не так безопасно по типам как было в C++.
Не понял.
О, вот сразу бы так :)
У вас vtable неправильная, переделывайте. Во первых метода не 3 а 1, во вторых не указатель хранится, а сам vtable, ваш код значительно менее эффективен.
А мы тут собрались копировать плюсовую имплементацию 1:1 или повторить семантику? Семантика интерфейса повторена, а то что оно ведет себя не как код на C++ - так это и понятно. Оно ж написано не на C++.
Я могу повторить тот же код на Python например. Там вы тоже будете предъявлять претензии к тому что vtable не такой?
А теперь задачка вам действительно сложная, попробуйте реализовать эффективнее чем понятный и логичный, короткий код на С++, но с использованием другого вида динамического полиморфизма. Там нет никаких виртуальных таблиц внутри типов, они гораздо лучше расположены. И выделений памяти нет.
И эффективнее, понятнее и короче код чем в С
https://godbolt.org/z/6zc5Ync3x
Спойлер - не получится у вас
И эффективнее, понятнее и короче код чем в С
Ну если вот это: https://raw.githubusercontent.com/kelbon/AnyAny/main/include/anyany.hpp
"Эффективнее и короче" чем мой код на C, то я даже не знаю...
Ну тогда линуксом не пользуйтесь, там небось тоже много кода
Ну или напишите аналогичную библиотеку на С. ОЙ, у вас не получится .. Потому что это невозможно..
Ну смотрите. Вы привели пример, который с помощью дикой шаблонной магии на полторы тысячи строк смог сгенерировать ассемблерный код на 6 инструкций короче чем простая программа на С размером в 50 строк кода.
Это конечно интересный trade off. Есть чем гордиться.
Если я покажу другому программисту на C++ вот этот весь код - как быстро он поймет что там происходит?
Эта "дикая шаблонная магия" может генерить бесконечное множество подобных кодов. Она для того и создана. Вместо того чтобы писать это руками(никто никогда не сможет писать это руками), она делает эффективный, понятный и компилятору и разработчику код. С её помощью можно не только cat и dog сделать, а всё что угодно.
Потенциально эта машина всего в 1000 строк сгенерирует вам миллиарды строк качественного кода. В этом и смысл.
Если вам нравится раз за разом писать бесполезную херню к тому же абсолютно нерасширяемо и неправильно(тут вам указали на громадные недочёты в вашей vtable), то можете конечно продолжать. А стоило бы уже начать использовать С++
Эта "дикая шаблонная магия" может генерить бесконечное множество подобных кодов. Она для того и создана.
ага, только у нее две огромные проблемы. Это криптоошибки при неправильном применении шаблонов и очень долгое время компиляции. Почему какой-то чертов Rust собирает большой проект в 10 раз быстрее, чем средний проект на крестах с шаблонами ? Почему в пайтоне я получаю результат гораздо быстрее, чем в С++ ? Да даже джава - ей не нужна самая современная машина для компиляции кода
Нормальные там ошибки если вы не то пишете
Нормальное время компиляции, С++20 добавляет модули, что значительно ускоряет компиляцию в сравнении с сишными инклудами
Раст компилируется дольше С++, это просто факт.
В питоне вы получаете результат дольше, потому что он работает дольше...
Покажите мне любой сишный код и я укажу, что вы инклудите тонны кода. Но это вас почему то не смущает
Он имеет ввиду ipmpl структуры размер которых не определён в рамках единиц трансляции где структура не определенна. Но и тут это будет не совсем верно, потому что можно изловчится и зная размер структуры успешно смапить её на память в стеке.
Появится способ создавать классы. Сейчас там это эмулируется через ужас и ужас. С потерей производительности, ужасным кодом и макросами
И где там потеря производительности? Что вызывать функцию через 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. Шляпа вроде С++ с его спагетти та не нужна. К тому же Линус ненавидит С++. Все современные языки отказываются от ООП парадигмы.
Я специально зарегистрировался что бы оставить этот комментарий.
Устал слышать этот бред.
Все говорят С это 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 был именно таким мутантом, толком не поддерживающим ни тот и не другой стандарт. При этом код стандартных библиотек представлял собой ацкую смесь с элементами обоих языков (а может где-то и сейчас так).
Во-первых, 95% Си совместимы с плюсами. При должной аккуратности можно писать код совместимый с обоими языками, причём ifdef потребуется только для extern "C", если нужна интероперабильность. Просто надо будет расставлять больше приведений типов и не использовать пару сишных фич.
Во-вторых, из плюсов очень часто вызывается большое количество сишных библиотек. Так что программисту на плюсах нужно знать Си (но это не сложно зная плюсы, см. первый пункт) всё равно.
В итоге либо нужен только Си, либо оба языка.
Зачем писать на C, когда можно делать современные хеловорлды на гигабайт оперативки, вскипячение восьмиядерника и запускающегося на оси не ниже 100500й версии?
Если без горького сарказма, то всему своё конечно. Но лично мне нравится подход к С++ как к C (с дурацкими байтами) с ООП классами. И соответственно мышлением где равноважными являются оба компонента - и предметная область и её формализация через ООП, и забота о эффективности по железу, мышление на уровне проца, байт оперативки, байт i/o с винтов и сети, эффективными алгоритмами, бенчмарками.
Без унавоживания слоями чрезмерных абстракций и прочих модных шуточек вроде "для сложения двух интов давайте подтянем 1042 библиотеки на пару гигов сурцов". Либы безусловно местами незаменимая штука, но может попытаемся не терять разум?
[ C <3 C++ ]
PS: Еще ассемблерные вставки рулят! Asm 4eva !
вот только С++ эффективнее С за счёт больших гарантий и большей выразительности кода
Когда у нас в стране (перед лучезарным взлётом отечественного железа) будет период пятилетки-другой с двумя миллионами тыжпрограммистов на 200 000 продакшн машин и устаревающим парком техники юзернеймов, критерии эффективности всё же явно будут пересмотрены )
А прямой ответ на коммент - да, спички детям не игрушка. Но ты же ого раз-ра-бот-чик! Если тебе дали пистолет, ну не стреляй им в ногу и в прочее куда не надо (=
С++ компилируется в банально более хороший ассемблер, вы не верите?
Я тут недавно специально сравнивал -- не компилируется. На Си, конечно, тоже надо уметь писать.
А может на С++ вы писали что то странное?
Ага, значит надо писать что-то "нормальное"? Можете дать какие-то критерии как отличить "странное" от "нормального"?
покажите код
Это у @mikhanoid надо спрашивать
С++ компилируется в банально более хороший ассемблер, вы не верите?
Нет.
«вот мы на С++ сделали шаблонный код, а на С такое руками писать утомительно поэтому сделаем тупо и неоптимально» — то не вижу причины с чего бы на С++ коду быть быстрее.
С++ эффективнее чем С, да. В выразительности, в скорости написания чего-то большого что надо поддерживать долго и толпой мало связанных меж собой разработчиков, я думаю множество средств где в поддержке он у С может выиграть.
А вот касаемо эффективности кода, ну чет такое. Слабо верится. Только в частностях.
Чистый Си будет всегда почти быстрее. В С++ больше абстракций, а значит больше прослоек и больше низкого когда в который он транслируется.
Еще ассемблерные вставки рулят!
Это пока программа запускается на одном компьютере типе процессора, а если говорить об оптимизации - на одной модели процессора. Потом через какое-то время ВНЕЗАПНО начинает рулить java. Или даже python.
Правильно писать C/C++/1C
А почему вы интерпретируете обозначение "C/C++" как язык? И наоборот, с точки зрения теории формальных языков, почему объединение всех возможных цепочек языков C и C++ не будет языком? С чего вдруг операция объединения языков перестала быть замкнутой в полукольце языков?
Символ "/" тоже следует интерпретировать как концепцию совместного использования или варьирования. Есть ведь ещё и CLI-расширение языка C++ для .NET. И как тогда прикажете сообщать, что требуется (кандидат может/программа написана с) применять и C, и C++, и CLI?
Копая дальше, а откуда такое предубеждение, что мозг может эффективно работать исключительно в одном стиле программирования? Вы ведь выражаете мысли и на естественном языке в программном коде, используя хотя бы говорящие наименования, не говоря уже про прямое документирование. Вам борьба за чистоту языка или стиля программирования (абстрактно -- множества концепций) не напоминает борьбу за "чистоту" крови, нации или расы?
Если копнуть ещё глубже, и посмотреть, как устроены живые системы (их изучением занимается системная биология), то выяснится, что природа использует носителей, максимально эффективно накаченных функциональностью. Т.е. и на уровне атомов, и на уровне молекул, и на уровне макромолекул, не говоря уже про их комплексы, метаболиты, в частности. Даже одна молекула (определена структурной формулой) часто проявляет множество биологических активностей. Однако, совмещение функциональности на одном носителе (объединение языков, например) многими осуждается в программировании. Как и совместное использование разных стилей. Хотя оракловые SQLJ как и PL/SQL очень даже удобны и эффективны.
Проверим этот подход сравнением по уровню сложности ИТ-систем, созданных людьми, с живой системой, да хотя бы человеком. У человека собственных клеток порядка 37 триллионов, и 23 триллиона живущих с ними в симбиозе. Одна клетка -- как небольшой завод на полном вертикальном обеспечении: включает и выработку энергии, и производство, и доставку к местам трансформаций всех компонент (от базовых деталей, т.е. простых молекул и аминокислот до молекулярных роботов, коими выступают белки и метаболиты, в частности), требующихся для жизнедеятельности клетки. А главное (о ужас!), в каждой клетке одна и та же ДНК, служащая исходным кодом (система нуклеотидных последовательностей: уложены в хромосомы), хотя многобразие клеток очень велико. И стволовые клетки могут трансформироваться в большое разнообразие других клеток. Т.е. природа несравненно превосходит по сложности всё то, что пока человеческий мозг смог сконструировать и понять. И это ещё не касались устройства систем управления на разных уровнях, особенно нервной системы и мозга. Природу нисколько не заботило требование понятности для кого-либо: функциональность важнее.
Так зачем вам разделение по языкам, по стилям программирования и запрет их совмещения на одном носителе (как программисте, так и исполняемом модуле)? Ради индивидуальной понятности это бессмысленно. Ради поддержания функциональной закреплённости ещё и вредно. Так зачем?
И чтобы эту неоднозначность устранить, есть extern "C"
. А может быть и extern "SQL"
, и много других языковых приёмов. Более того, многозначная интерпретация не всегда взаимоисключающая, и есть масса случаев, когда она одновременно верная. А взаимоисключающая интерпретация часто возникает даже в одном языке, для чего вводят дополнительные факторы: пресловутый пример с if-else. Многозначная интерпретация и её обработка -- основная проблема разработки таблицы трансляции, т.е. перевода некоторой комбинации из элементов языка во что-либо (другую цепочку или действие). Сложность этих комбинаций и их многообразие является следствием класса языка.
Более того, семантика содержится вне языка, как и прагматика: разные языки могут описывать и предписывать одно и тоже. И ключевой характеристикой языка является его выразительная способность (описательная мощность). Именно поэтому есть понятие "синтаксически управляемый перевод", и весь ИТ построен на его применении и вокруг этой проблемы, т.к. для оперирования информацией требуется носитель. Напомню, что язык, по определению, -- это подмножество произвольной итерации алфавита, т.е. подмножество универсального языка.
Поэтому ваше справедливое наблюдение про различие семантики, поставленной в соответствие цепочкам языка, не является ни критерием, ни признаком, но является закономерным эффектом существования пересечения таблиц трансляции по входным комбинациям элементов языка.
Гугл, может, и не знает, но «Вики» вполне знает о развитии Си: https://ru.wikipedia.org/wiki/C11
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(); // пора бы уже задуматься
}
Я конечно не программист и шарж получился так сяк, но приходится сделать вывод, что с++ язык и он может повсеместно совмещаться с С и наоборот. И оба эти языка самодостаточны, однако и оба совместимы как друг с другом, так и с другими языками.
И в этом нет ничего плохого. Как мне кажется, если вы умеете описывать что-то с использованием семантики некоего языка, то, с большой вероятностью за короткий срок вы сможете перестроиться и использовать семантику любого другого языка. Ведь в конце концов это все сделано лишь для того чтобы не изучать команды работы процессора и системы аппаратной адресации памяти. Но и их вы тоже можете начать применять, если задача того потребует.
В общем, хорош вносить смуту в ряды С/С++ программистов, лучше пойдемте вместе побеждать питоноидов. :)
Посыл хороший, но орфографические ошибки и опечатки очень сильно портят читаемость
С++ по сути надстройка над С. Сейчас стандарты чуть чуть разошлись, но большая часть си кода отлично работает на С++. Насчет того, что классы не байты и си код это неопределенное поведение - бред. Есть в стандарте плюсов и правила выравнивания полей классов по байтам. Хороший программист С++ всегда учитывает, как поля класса распределены по байтам - позволяет выжать больше из языка. И указатели работают отлично на с++ и все еще очень даже в ходу - особенно при оптимизации. И декларативное программирование обычно занимает меньше половины кодовой базы на плюсах - если не считать библиотеки шаблонных классов и прочие малочитабельные вещи. Если человек считает себя спецом по плюсам, то основы си он знать должен - иначе не поймет значительную часть кода библитек и легаси. Обратное конечно не верно. Так что, если автор выбрал для себя какую-то часть языка С++, как более понятную и приятную, это не значит, что остальная часть языка забыта и не используется.
Это как путать -тся и -ться. Слова выглядят одинаково казалось бы, но смысл совершенно разный.
Что и говорить про провинциальные колледжи и вузы... Особенно веселит когда приходит очередной препод и говорит: "Сейчас, Я вас буду учить программированию, показывает Си-шный код и говорит мы будем изучать С++". А ты у него спрашиваешь в конце лекции: "...так мы изучаем Си или С++, и можно ли писать в C++ стиле?" - он смотрит на тебя с недоумевающим видом и либо переспрашивает, либо начинает поднимать тебя на смех.
Абсолютно не согласен.
Наверно имеется ввиду, что человек должен знать С и С++, что не удивительно.
Вообще было забавно читать весь этот бред о том, что нельзя реализовать простой вектор на С, потому что язык тебе этого не даст сделать.
Столько программ написано на С, все *nix на нём. А у некоторых вектор не получается реализовать. Что это может говорить? Только об уровне владения знаниями авторов подобных утверждений.
И как говорил старина Торвальд - "C++ можно использовать только если выкинуть из него всё д*рьмо, чтобы остался по итогу чистый С" )
ЕМНИП была какая-то техника обмазывания дефайнами, которая сводит копипаст программиста (но не препроцессора) к нулю; наверно, и ошибки препроцессором можно проверять.
Но что интересно в примере - тип элемента там полностью описывается лишь размером элемента, да ещё и проверка возможна лишь в рантайме. Динамическая типизация на минималках, однако.
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.
А вы сами когда-нибудь пробовали реализовать хоть какую-нибудь коллекцию?
Конечно можно. Я и сам его писал. Вектор - это просто динамически расширяющийся массив в куче, который перераспределяет память когда при добавлении новых элементов его длинна начинает превышать его ёмкость.
Про то, что это невозможно могут говорить только какие-то упоротые питонисты или джависты, которые память никогда в глаза не видели.
Я далеко от темы и объясните мне почему просто C до сих пор есть? Потому-что на нём драйвера легче писать, или как?
Потому для него проще написать компилятор под новую платформу.
Потому что Си удобен и практичен. Потому что Си учит базовым вещам без которых программист - не программист. И потому что Си заботится об обратной совместимости.
Сейчас написал драйвер для железки pcie в среде msvc, на чистом C, т.е. C это подмножество C++, крестов, написал ООП шную обёртку верхнего уровня с классами, конструкторами, деструкторами для работы с железкой из Питона, при помощи скриптов.
Железка на чипе Xilinx, в ней есть ARMовое ядро или soft-ядро risc Microblaze, IDE Vitis, язык чистый Си, компилятор gcc, а по PCIe я работаю из под Винды в Visual Studio 2022, msvc компилятор, язык С++ ISO 14, я выдрал целые библиотеки из Vitis и вставил их в Msvc, написал обёртки, теперь код у меня одинаков в обоих средах.
Это живой пример близости языков. На 90% Сишный код будет работать в крестах
И никто не вспомнил песенку , "мой папа может в Си" . Про чистый C .
Прочёл заметку и поймал себя на вопросе, согласен ли я с автором или нет. И вот что мне думается. А зачем такие большие теоретические споры, что лучше, что хуже? Тут вопрос в том, что ты хочешь получить. Программисты-практики не будут заморачиваться с этими несчастными undefined behavior. Они просто возьмут и сделают, как надо. Я не ругаю С или С++. Как и везде имеются свои преимущества и недостатки. Поэтому всем творческих успехов!
А еще бывают ищут С++ программистов для embedded проекта. Вот только требование проекта - нужно свести использование динамической памяти к абсолютному минимуму (вдруг будет фрагментация кучи, а прибор даже раз в пару месяцев некому перезагрузить). Потому STL, c ее аллокаторами - в топку (включая всеми любимый std::vector и std::string). И будь внимателен с наследованием, вдруг там какой-то предок втихоря heap использует. И еще у нас есть библиотека драйверов на С, но эти драйвера должны быть подключены статически, а инициализированы уже в процессе работы (вы еще думаете про RAII?). Компилятор у нас новый, поддерживает с++11 и даже с++17, вот только поддержку std::thread не успели сделать, используйте С API RTOS, который даже немножко совместим с POSIX. Да, и тут еще вышел апдейт тулчейна, в котором в новой версии libc отключили обработку исключений, так что если вы уже успели где-то понаставить throw, то замените их на goto (и заодно, если таки использовали какие-то стандартные библиотеки, проверьте чтобы везде было nothrow).
А так да, нам нравится синтаксический сахарок С++ и инкапсуляция, так что все-таки нужны С++ программисты, чтобы внедрять современные практики.
Можно биться за идею, ради самой идеи, но уж очень это накладно. И заказчик платит не за то чтобы допилить С++ до языка высокого уровня на конкретной системе, а за решение его бизнес задачи.
Подгонять STL под реальные нужды - то еще занятие, лучше воспользоваться библиотекой с похожим синтаксисом, но изначально написанной с учетом требований к минимизации работы с динамической памятью. Есть такая - ETL, рекомендую.
Корпорация с многомиллиардными оборотами не нашла сил допилить свой SDK чтобы можно было использовать std::thread, а мне это делать тем более не с руки. Благо использование суржика С/С++ позволяет решить практическую задачу, не смотря на шипение пуристов.
суржика С/С++
А что значит "суржик" в данном контексте?
Вы пишите на чистом Си в .c файлах и на C++ в .cpp файлах. Соединяется это все затем только на этапе линковки. И, в таком случае, это ведь тоже самое, как когда вы пишете на Си и на Ruby/Python, и объединяете их через FFI. Только с поправкой на то, что интероп между Си и C++ на порядки дешевле.
Или же вы в .cpp файлы помещаете чисто Сишный код и компилируете это все C++ным компилятором?
И то и другое и третье. Есть С библиотеки из SDK, скомпилированные С компилятором, которые потом просто линкуются. Есть С хедеры к этим библиотекам и небольшая часть С библиотек в виде исходного кода, которые компилируется С++ компилятором. Есть С++ код, написанный в стиле С, потому что так его получается заметно меньше, с учетом что он все равно в основном работает с библиотеками на С. И есть чуть чуть С++ кода в стиле С++, где-то на уровне рисование менюшек в интерфейсе пользователя.
з.ы. Кстати интересный момент, языки разные, компиляторы тоже вроде как разные, а низкоуровневая библиотека поддержки libC для них общая :-)
Так что из этого "суржик"? Как по мне, так под это понятие попадает разве что Сишный код, который скармливается именно что C++ному компилятору.
Что такое "С++ код, написанный в стиле С" для меня не очень понятно. Скажем, вот это:
struct my_data {...};
void process(my_data * data) // В Си потребовалось бы struct my_data *.
{...}
все еще C++?
А вот это:
struct my_data {...};
void process(my_data * data) // В Си потребовалось бы struct my_data *.
{...}
void process(int * data); // Перегрузки в Си нет.
все еще C++?
"Суржиком" я назвал все что не является ANSI C в чистом виде и не вписывается в рекомендуемые патерны С++. Код выше я бы отнес к такому типу. С точки зрения С++, код выше выглядит подозрительно из-за использование отдельно структуры и функции вместо класса и метода, передача аргумента по указателю, а не по ссылке. Но в общем надо смотреть больший фрагмент, чтобы понять оправданное такое решение или нет.
не вписывается в рекомендуемые патерны С++
А что значит "рекомендованные паттерны"?
Вроде бы объективный критерий один: если для компиляции нужен именно C++ный компилятор, значит у нас С++. Если достаточно Сишного компилятора -- значит Си.
С точки зрения С++, код выше выглядит подозрительно из-за использование отдельно структуры и функции вместо класса и метода, передача аргумента по указателю, а не по ссылке.
А что, в C++ уже стало обязательно использовать ООП "во все поля"? Вроде как C++ мультипарадигменный язык, он не заставляет использовать весь набор фич, можно брать только то, что нужно для задачи. Если классы мне не нужны, то можно обойтись процедурным подходом.\
Да и голые невладеющие указатели -- это вполне себе норм. Хотя бы потому, что указатели могут иметь значение nullptr и это валидно. В отличии от ссылок.
Можно подходить для оценки по разному: 1) пропустит ли этот код компилятор, и будет ли он работать, 2) пропустят ли такой код на ревью товарищи, которые "знают что такое правильный С++". Все возможно, это дело вкуса, о котором сейчас здесь много спорят.
Мне было интересно что вы понимаете под суржиком.
То, что в проекте может быть какой-то code style, за нарушение которого бьют по рукам, вовсе не означает, что несоответствующий code style код перестает быть C++ и становится суржиком.
Я использовал такой термин потому что С и С++ на столько близки с точки зрения поддержки компиляторами и тулчейнами, что становится возможным писать код не соответствующий строгим подходам каждого из языков, но при этом вполне работоспособный. И иногда такое решение лучше, с точки зрения конечной цели разработки, хоть и не удовлетворяет полностью сторонников одного или другого подходов.
Я не очень понимаю смысл. Если у вас есть код, который должен использоваться и в чисто Сишных проектах и в С++ных, то вроде как проще держать его в виде чистого Си, разве что озадачившись extern "C" в заголовочных файлах. Поскольку .c файл и в C++ном проекте (по нормальному) должен бы компилироваться именно как Си-шный файл.
Опять же не понятно, что значит "не соответствующий строгим подходам". Если вы в коде на Си запросто манипулируете членами union-а, а потом этот же код компилируете как C++, то вы тупо вносите в программу UB. Это просто-напросто ошибка, а не "несоответствие".
Действительно, С код лучше компилировать родным компилятором, но бывают библиотеки - прослойки, типа HAL (hardware abstraciton layer) которые изначально рассчитаны на компиляцию как одним так и другим.
В Си манипулирование членами union-а тоже строго говоря UB, просто лучше изученное :-), если вам очень надо было это сделать, а потом повторить в С++, - напишите Юнит-тест, который будет это проверять. Ну или найдите время на переделку. И не смотря на то что С++ снимает часть забот с разработчика, он не гарантирует что в программе не будет ошибок приводящих к UB.
Подпишите, где С и где С++.
Вопрос со звездочкой. * Водители каких автомобилей более предсказуемо, для других участников движения, ведут себя на дороге / на каком языке пишутся программы, легко читаемые не знакомым с проектом программистом?
Фокус в том, что езда по загруженной трассе с постоянными обгонами в любом случае не повышает безопасности. Ну и большинство покупателей БУ БМВ, не в состоянии их нормально обслуживать. В юности подрабатывал на станции СТО, так вот БМВ там в обслуживание не брали, из за специфичности их владельцев, а вот ВАЗы начиная с 10ки - были Ок. Так и с плюсами, большинство компаний не в состоянии нанять достаточное количество квалифицированных С++ программистов, чтобы в итоге получился крепкий, однородный по стилю проект. В реальном бизнесе почти не увидишь таких марок, потому что большинству нужно ехать, а не "шашечки".
Таки интересно, в каком бизнесе все еще используется С++, и это происходит без боли? Слышал что в геймдеве С++ все еще популярен, но там, по слухам, много овертаймят. Еще слышал про браузерные движки и офисные пакеты, но таких проекты можно пересчитать по пальцам одной руки. Какие еще есть на сегодняшний день "success story" с С++?
Да СУБД хотя бы. Или CAD-ы.
Гейм дев поголовно, всё что связано с рендерингом Cycles, Prman, Arnold, RedShift, Karma и т.д. И вообще всё что связанно с графикой Maya, Max, Blender, Houdini, Katana, Adobe и т.д. Браузеры. Честно говоря мне гораздо трудней вспомнить хоть 1 программу где не используется С++ или С. VSCode, Atom да и те поверх хромиума да ноды которые юзают С++
Ребят, зачем вы пишите на C++?
Для важных, быстрых вещей, которые будут использоваться и тут и там, есть Си. Язык прекрасен. Посмотрите вокруг, оно так и есть. Ядро Linux, libxml, libpng, графическое апи и так далее. Всё написано на чистом C.
Для остального есть Java или Kotlin. Тут вам прекрасные фреймворки, куча библиотек. Скорость создания приложений уже в 10 раз выше, чем на C++.
И да, Java уже не тормозит, если для вас это важно. Она уже несколько лет компилируется в нативный код полностью.
C++ устарел и устал под своей сложностью. Вы попробуйте подключить к проекту на C++ 50 сторонних библиотек. Легче застрелиться. А в других языках это всего несколько строк. Переносимость между платформами нулевая. Перенести большое приложение просто не возможно. И так далее
Как я понимаю, под "C/C++" имеется ввиду, что проект на C++, но он загажен кодом в стиле Си. С кучей sprintf
, int** err
, куча сокращений до трех букв, и т.п.
Языка С/C++ не существует