Иван Савватеев @SIISII
Микроконтроллеры, цифровая электроника, ОС…
Information
- Rating
- 1,754-th
- Location
- Солнечногорск, Москва и Московская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Embedded Software Engineer
Lead
Микроконтроллеры, цифровая электроника, ОС…
Правильней было бы сказать, что не всякий старый код можно собрать в новом компиляторе. И до всяких стандартов можно было писать, не полагаясь на поведение конкретной версии конкретного компилятора, а там, где без этого обойтись невозможно, предпринимать специальные меры, упрощающие перенос проекта. Скажем, разрядность целых типов данных была хоть как-то стандартизирована лишь в C++11, но ничто не мешало и за 20 лет до этого определить с помощью typedef'ов свои INT8, INT16 и т.д. и в проекте использовать только их, а не всякие там short int, которые непонятно во что превратятся при трансляции на произвольной платформе; соответственно, при "переезде" пришлось бы изменить лишь эти typedef'ы в одном заголовке, а не перелопачивать весь проект.
Так что дело, конечно, в том числе и в руках, но не столько собирающего сейчас, сколько пишущего тогда.
Ну, речь о том, что поддержку всяких достаточно высокоуровневых вещей вроде сети и гуйни хотят не просто в какой-то библиотеке, а в стандартной библиотеке. Если с сетью ещё можно согласиться (в конце концов, TCP/IP -- он везде, и предусмотреть разумное множество стандартизированных функций по работе с входящими в него протоколами можно), то с гуйнёй -- сомнительно, так скажем.
Посмотрите здесь: https://en.cppreference.com/w/cpp/freestanding
Реализовать на микроконтроллере сие можно. Но это будет реализация специально под конкретный даже не микроконтроллер, а проект на микроконтроллере, а соответственно, не может поставляться как часть библиотеки компилятора.
Нет. Обязательной базой является лишь малое подмножество, почти всё остальное может отсутствовать в конкретной реализации библиотеки.
Насколько помню, прямо в стандарте указано, что на "полноценных" платформах реализуется всё, а на "неполноценных", типа тех же микроконтроллеров, обязательной является лишь малая часть библиотеки.
DSP -- весьма специфическая ниша, и ориентироваться на неё в универсальном языке... Ну, такое себе. Если так уж хочется реализовать в нём те же операции с насыщением, правильней, как по мне, делать специальные типы данных для подобных целей, а не пытаться всё вогнать в один и тот же int. Тогда можно будет наиболее естественным образом и обычную арифметику считать, и всякие специальные виды -- и всё это безо всяких UB.
MEL, кажись, был в Майе.
Причём модули в C++20 прикрутили, но, как обычно, через такую задницу, что они... не шибко хороши, скажем так.
А вот ввод-вывод нормально не стандартизируешь, поскольку это сильно завязано на ОС. В Винде всю жизнь был асинхронный ввод-вывод, а в Унихе его нет. Стандарт POSIX предлагает его, но это стандарт, а не конкретная ОС, и далеко не все ему следуют. В Линух сравнительно недавно добавили IO_URING, но он какой-то странный и переусложнённый, как по мне. Ну и как делать нормальную стандартную библиотеку в таких условиях? printf как консольная печать хорош тем, что он действительно может быть стандартным: весьма сложно найти систему, где его невозможно реализовать. А что-то сложней -- всё, приехали, здесь так, там эдак...
Ну, мне вот нужен был ФАТ -- но асинхронный, а не fatfs, который насквозь синхронный. Пришлось своё писать. Ну и попробуй такое стандартизировать, чтоб удовлетворить всех и каждого на все случаи жизни :) Не говоря о том, что файловые системы -- это, вообще говоря, забота ОС, а не прикладного кода, и лишь во всяких микроконтроллерных вещах приходится этим заниматься самому.
Так что стандартные библиотеки -- это хорошо, но всё же не стоит валить в одну кучу собственно язык и библиотеки. Это вещи связанные, но не одно и то же.
Соглашусь, но лишь частично. Проблема не в том, что чем-то можно злоупотребить (злоупотребить, пожалуй, можно вообще всем), а в том, что некоторые языки провоцируют злоупотребления, а другие -- наоборот, препятствуют. В качестве крайне примитивного примера -- "оформление" в C (а из него и в C++) присваивания как операции (operator), а не как оператора (statement), из-за чего можно писать (и нередко пишут) что-то вроде:
Запись более короткая, чем, скажем, паскалевское:
Однако сишная запись провоцирует ошибки (= вместо == или наоборот), а нередко и читабельность уменьшает, поскольку поощряет впихивание кучи операций в одну строку -- и усугубляет это практически полным отсутствием контроля типов. Поэтому лично я так никогда не пишу -- предпочитаю паскалевский стиль.
Сейчас, правда, проблема с такими вещами стоит не так остро, как 30 лет назад: если включить все предупреждения компилятора и превратить их в ошибки, бОльшая часть подобных проблем будет отловлена. Но вот во времена ДОС...
Если программу нет возможности транслировать, значит, совместимость на уровне языка уже абсолютно безразлична, поскольку на этом уровне она использована быть не может. Ну а совместимость на уровне соглашений о связях (правила передачи параметров и т.п.) к собственно языку программирования прямого отношения не имеют и вполне могут быть одинаковыми для совершенно разных языков (не говоря уж о возможности написать, если припрёт, небольшие переходники-"прокладки" на ассемблере).
Новому коду она не нужна, а старый код можно транслировать старыми версиями. Для возможности вызова одного из другого достаточно соблюдать низкоуровневые соглашения о связях, что к языку вообще, по большому счёту, не привязано, о чём я сказал выше.
Я вот сопрограммами пользуюсь -- для асинхронного ввода-вывода (на микроконтроллерах).
На самом деле, нет никакой реальной проблемы с выбрасыванием старья и ломкой совместимости. Зачем тянуть совместимость со старьём в новые версии стандарта? Транслируйте программу, написанную на C++93, в соответствующем режиме компилятора, и очистите современный язык от всякого хлама в новых версиях.
На самом деле, впечатление такое, что комитет по стандартизации старается впихнуть в язык как можно больше фишек, причём как можно более извращёнными способами, а реальные проблемы не только не решает, но ещё временами и усугубляет. Особенно это касается UB: в 95% случаев поведение вполне себе определённое, но, поскольку его не желают объявить таковым, компилятор волен творить любую дичь. Например, почему не убрать UB из случая переполнения целочисленной переменной со знаком? На любой платформе, использующей для представления целых чисел дополнительный код (а сколько-нибудь современные платформы все такие), самое большое положительное число переходит в самое большое отрицательное, и наоборот, и правила перехода одного в другое совершенно чётко определены логикой операций сложения/вычитания на уровне "железа". Единственный вопрос: будет ли исключение при переполнении? Так запросто можно и нужно стандартизировать, и самый лучший вариант -- определить, что это зависит от стандартной прагмы: если включено, компилятор обязан обнаруживать переполнение и кидать исключение, а если выключено -- просто позволить процессору формировать значение, как он это всегда и делает.
То же самое касается и нулевых указателей, и выходов за границу массива, и т.д.: никакого UB с точки зрения языка (компилятора) там быть не должно. Если обращение по нулевому указателю -- выполняй обращение по адресу 0, и всё, остальное -- не твоё дело. Если выход за границы массива -- вычисляй адрес, как обычно вычисляешь, и выполняй обращение, и не твоё дело, что при этом произойдёт. А заодно следовало бы добавить стандартные прагмы, управляющие обнаружением таких случаев во время выполнения: если они включены -- компилятор должен генерировать код, обеспечивающий обнаружение и выдачу исключений, если выключен -- не обнаруживать эти случаи и дать программе работать, как она работает.
Тоже неверно. Микропроцессор К1801ВМ1, на которых сделана ДВК-1 (и БК-0010) -- да, архитектура LSI-11 (в частности, нет разных уровней прерываний, как у полноценной PDP-11). А вот К1801ВМ3, на котором сделана ДВК-3, -- это уже полноценная PDP-11, включая MMU (а FPU был сделан в виде сопроцессора К1801ВМ4 -- правда, его никто не видел).
То есть на 5 миллигерц? ;)
*** сбой ***
Нет, не триггерит.
Вообще говоря, названия методов адресации относятся, в первую очередь, к адресации данных, а не к адресам команд переходов, и все рассуждения выше (о невозможности прямо указать 64-разрядный адрес в AMD64/Intel64 и т.п.) относятся именно к адресам операндов в памяти, а не к переходам.
В принципе, могли и гуйню переписать под новый API, хотя смысла в этом для подобных программ нулевой. Но вот насчёт размера файлов выглядит логично.
А версия командной строки, вроде б, официально бесплатна со всех точек зрения? Я-то только ей и пользуюсь, мне удобней с архивами работать из FAR или прямо из комстроки (в зависимости от того, что нужно).
Обычно в английском immediate означает непосредственную адресацию, когда в команде задано значение операнда, а не его адрес. Например, в современном описании архитектуры Intel64:
Такое же использование термина immediate имеет место и в других архитектурах -- как древних (скажем, PDP-11, System/360, 6502, 8080), так и современных, вроде ARM. Вот, например, из описания на 6502:
Хотя мы видим, что названо "immediate addressing", но из описания видно, что имеется в виду операнд, являющийся частью кода команды (в данном случае -- её вторым байтом), а не лежащий где-то ещё в памяти.
Почему в той статье решили использовать это слово для обозначения адреса, а не значения операнда, мне неизвестно, но это явно не устоявшаяся терминология, а её нарушение.
Indirect повсеместно используется для случаев, когда в коде команды указывается не адрес операнда, а местоположение адреса (адрес адреса). Вот, например, в том же 6502 обе его косвенные адресации (довольно специфические :) ):
Direct же -- противоположность indirect, т. е. команда прямо задаёт адрес операнда. Хотя в разных архитектурах могут использоваться разные термины. Скажем, в PDP-11 и в 6502 используют термин "абсолютная адресация".
Ну и, в любом случае, в 64-разрядном режиме прямо в команде указать адрес операнда нельзя, только через регистры -- именно об этом говорилось.
1) Встроенный ассемблер -- расширение конкретного компилятора, а не стандарт языка, что не всегда приемлемо.
2) Написать две команды на встроенном ассемблере -- нормально, сам пишу временами. Написать целый драйвер в несколько сотен строк -- уже нет. Во всяком случае, если есть более-менее нормальный транслятор ассемблера, а не один ублюдский ГНУсный as, который заточен не на ручную работу, а исключительно на трансляцию выхлопа GCC.
3) Макросы на С/С++? Не смешите, они исключительно убоги по сравнению с макросредствами, которые бывают у развитых макроассемблеров. Не говоря о том, что макросы в языке высокого уровня способствуют запутыванию кода и возникновению проблем, и применять их нужно с осторожностью (а по возможности -- не применять вообще).
4) А ещё компиляторы любят заниматься агрессивной оптимизацией, которая временами ломает логику. Ну а набивать текст барьерами -- это и ухудшать читаемость, и ухудшать оптимизацию, ведь барьеры достаточно неизбирательные. Ну а в случае с ассемблером транслятор подобной самодеятельностью не занимается.
В общем, наличие возможности впихнуть несколько ассемблерных команд прямо в текст на С/С++ не отменяет пользы от настоящих ассемблеров.