Вот что приходит мне на ум, когда я думаю о D: быстрый, выразительный, легкий и... драйвовый? Именно так, я вожу машину вместе с D.
Вот мой почтенный автомобиль Holden VZ Ute. С завода он поставлялся с убогой четырехступенчатой автоматической коробкой передач. За 18 месяцев владения автомобилем я сломал четыре коробки передач. В то время я не мог позволить себе новый автомобиль, поэтому мне пришлось творчески подойти к делу. Я купил железобетонную, дуракоустойчивую шестиступенчатую автоматическую коробку передач от другого автомобиля. Но на этом я уперся в тупик. Чтобы заставить ее работать, мне пришлось собрать собственную печатную плату, систему управления и прошивку для управления соленоидами, гидравликой и сцеплениями внутри коробки передач, обработки команд водителя, принятия решений о переключении передач и взаимодействия с автомобилем, притворяясь четырехступенчатым автоматом.
Я очень горжусь своим решением. Оно может выполнять переключение за 250 миллисекунд, что очень удобно для гонок. У него крутая первая передача, что обеспечивает быстрый старт. Оно дало фору некоторым более мощным автомобилям. У него есть удобные подрулевые лепестки, диагностические данные на экране и возможность изменить принцип работы, когда я захочу.
Вот очень старое видео работы системы. Оно не является показательным для нынешней системы - этот ужасный синий экран исчез, спидометр работает, а переключение передач улучшилось.
Компьютер разделен на две части: плата пользовательского интерфейса, которая управляет OLED-дисплеем и использует STM32F042, и основная плата, которая управляет всем остальным, используя STM32F407. Эти два устройства взаимодействуют через шину CAN (Controller Area Network). Вся микропрограмма для управления этим оборудованием написана на языке D.
Я выбрал D (в версии -betterC) из-за его гениального унифицированного синтаксиса вызова функций (UFCS), метапрограммирования, простоты взаимодействия с C, юнит-тестирования, переносимости, shared
и @safe
. Еще один бонус - это отзывчивое, гостеприимное сообщество. Мне было очень приятно обсуждать D на форуме, а также с основателями и лидерами сообщества.
Преимущества D
Универсальный синтаксис вызова функций (UFCS)
Благодаря этому мой код стал значительно чище. Мой код может точно следовать потоку данных, не загрязняя стек одноразовыми переменными, вложением множества вызовов функций или другими видами нагромождений. Например, вот код, который я использую в своем новом проекте ECU (блок управления двигателем):
immutable injectorTime = airStoich(100.kpa, 25.degCelsius)
.airMass
.fuelMass((14.7f).afr)
.fuelMol
.calculateInjectorWidth;
Не нужно читать код задом наперед или считать скобки, не нужно использовать газиллион одноразовых переменных. Он лаконичен.
Принцип "Не повторяй себя" (DRY) часто пропагандируется программистами. Метапрограммирование в D является невероятным инструментом для достижения этой цели. Я использую его в своей реализации шины CAN. Например:
struct CANPacket(ushort ID) {
enum id = ID;
ubyte[8] data;
}
alias HeartbeatPacket = CANPacket!10;
alias BeepHornPacket = CANPacket!140;
У меня есть специальные псевдонимы типа HeartbeatPacket
и BeepHornPacket
, но мне не нужно повторять никакой код. Все они следуют одной и той же базовой структуре, поэтому если я изменяю CANPacket
, каждый псевдоним также обновляется.
Мне часто приходится общаться с HAL и RTOS моего микроконтроллера; интерфейс D's C сделал это проще простого. Просто добавьте extern(C)
, и все будет нормально.
extern(C) c_setPwm(int solenoid, void* userData); // declaration
c_setPwm(4, null); // usage, pretty easy!
Встроенное в D юнит-тестирование несколько раз спасало меня от отстрела ног. Я могу запустить все свои модульные тесты в Windows, чтобы гарантировать логическую корректность, а затем собрать конечную версию для микроконтроллера.
В продолжение вышесказанного, D поддерживает удивительно большое количество платформ с помощью GDC и LDC. Если бы не переносимость D, мне пришлось бы писать свой проект на C++ (ох). Я использую LDC, и кросс-компиляция может быть выполнена простым изменением аргументов командной строки.
Shared - это способ защиты D от многопоточного доступа(гонки) к данным. Он не идеален, но я использую его как есть, и думаю, что он работает достаточно хорошо. В моей программе несколько потоков, и им нужно синхронизировать данные. Я помечаю определенные переменные как общие, что означает, что я должен быть особенно осторожен при доступе к этим данным. Это работает с системными блокировками и мьютексами. При блокировании я могу отбросить shared от переменной приведением типа и использовать ее как обычную переменную. Это удобно при работе со структурами и классами.
shared int sensorValue;
sensorValue = 4; // using it like a single-thread variable, error
atomicStore(sensorValue, 4); // works with atomics
@safe
существует для того, чтобы запрещать небрежные действия с памятью и обеспечивать наилучшее поведение. Мне еще не приходилось бороться с @safe
, потому что я не делаю ничего дурного со своей памятью, но мне комфортно знать, что если я собираюсь совершить ошибку, компилятор поможет мне остановить ее.
Адам Д. Руппе выразил это кратко: D имеет низкий уровень умственного трения. Гибкость и выразительность языка позволяют легко переводить свои мысли в письменный код и сохранять свою продуктивность. Мне не приходится бороться с компилятором D. Это мое личное мнение, но я чувствую, что D - это язык, на котором я наиболее продуктивен.
Заключительные мысли
D идеально подходит для такого рода проектов - думаю, его ждет блестящее будущее в мире встраиваемых систем. Я собираюсь и дальше использовать D в своих проектах. У меня в разработке находится еще один автомобильный проект на базе D, который я надеюсь показать в будущем.
Дополнения от переводчика:
Такая модификация автомобиля законна в месте проживания автора, он это уточнял.
В режиме -BetterC, который использует автор, сборщика мусора (GC) нет.
Далее, автор разработал для своих эмбеддед задач и режима BetterC минимальный рантайм, описание его с форума переведено ниже.
Чем D лучше С для эмбеддед? - Прежде всего надежностью и безопасностью.
Чем D хуже С для эмбеддед? - Более сложен и бинарник будет существенно тяжелее.
Легковесный рантайм для D
LWDR (Light Weight D Runtime) v0.3.0 - оригинал форума
Описание предыдущей версии (устар)
LWDR (Light Weight D Runtime) - это реализация с нуля среды выполнения D, ориентированная на микроконтроллеры ARM Cortex-M и другое голое железо. Она работает, предоставляя ряд базовых API хуков (как определено в rtoslink.d, которые вы должны реализовать и/или указать на вашу реализацию в RTOS).
Это V0.3.0 LWDR. Начиная с предыдущей версии V0.2.3, была проведена следующая работа:
Поддержка Локальных Переменных Потоков (Thread Local Storage, TLS)
Примитивное отслеживание памяти для аллокаций памяти внутри Phobos, которые обычно зависят от GC
Переход к системе опциональной функциональности (opt-in)
Замена
delete
наLWDR.free(...)
в связи с устареваниемУлучшение документации по исходному коду
Реализации
RefCount!T
иUnique!T
, специфичные для LWDR
Thread Local Storage
Эта возможность довольно абстрактна, и она является опцией при LWDR_TLS
. Вы должны обеспечить поддержку секций tdata
и tbss
в своем скрипте линкера. Она работает, используя реализацию TLS в базовой RTOS (пример). При вызове LWDR.registerCurrentThread()
выделяется блок памяти D, содержащий переменные TLS для текущего потока, а указатель на блок хранится в TCB (Thread Control Block) потока. При обращении к переменной TLS (т.е. статической переменной) вызывается __aeabi_read_tp
, выдающая указатель.
Отслеживание памяти
Это очень примитивно. Оно предназначен только для того, чтобы помочь остановить утечку GC-зависимых аллокаций из stdlib (Phobos). Оно ведет себя практически так же, как определено здесь.
Опциональная функциональность (opt-in)
Чтобы иметь возможность уменьшать размер TypeInfo
vtables и тому подобного, LWDR использует систему opt-in, которая опирается на функцию version языка D. В настоящее время в список опций входят:
LWDR_TLS
- Включает поддержку TLSLWDR_DynamicArray
- Включает динамические массивыLWDR_TrackMem
- Включает бардак с отслеживанием (см.выше)
Замена delete
delete
была отменена. Вместо нее реализована LWDR.free
для предотвращения предупреждений компилятора.
Документация по исходному коду
Рантайм - страшная и муторная штука, поэтому я начинаю прилагать больше усилий для документирования того, как все работает. Пока что это только комментарии ddoc.
RefCount!T и Unique!T
Чтобы компенсировать неудобства от отсутствия сборщика мусора (GC), я реализовал специфическое для LWDR решение, вдохновленное automem.
Что реализовано
Class allocations and deallocations (via
new
andLWDR.free
)Struct heap allocations and deallocations (via
new
andLWDR.free
)Invariants
Asserts
Contract programming
Basic RTTI (via
TypeInfo
stubs)Interfaces
Static Arrays
Virtual functions and overrides
Abstract classes
Static classes
Allocation and deallocation of dynamic arrays (opt in by version
LWDR_DynamicArray
)Concatenate an item to a dynamic array (opt in by version
LWDR_DynamicArray
)Concatenate two dynamic arrays together (opt in by version
LWDR_DynamicArray
)Dynamic array resizing (opt in by version
LWDR_DynamicArray
)Thread local storage (opt in by version
LWDR_TLS
)
Что не работает (пока)
Exceptions and Throwables (экспериментальная реализация удалена)
Module constructors and destructors
Static constructors and destructors
Shared static constructors and destructors
Module info
There is no GC implementation (primitive memory tracking is now available with
LWDR_TrackMem
,RefCount!T
andUnique!T
are now available)Delegates/closures
Associative arrays
Object monitors
shared
/synchronised
Object hashing
Другие вещи, которые я не могу вспомнить с ходу.
Это все еще бета-версия - так что ожидайте ошибок и недостатков. Некоторые части были тщательно протестированы, другие - не очень.
Поскольку рантайм разросся слишком быстро, я хочу немного приостановить разработку, чтобы начать использовать LWDR в соответствующем проекте и найти и устранить ошибки. Проект является практически преемником моей статьи Driving with D (это автомобильный проект) (прим.пер - верхняя часть статьи). Я также думаю подать заявку с LWDR на участие в проекте Autumn of Code.
Как только LWDR станет достаточно стабильным, я хочу, чтобы следующая версия включала информацию о модулях, поддержку статических ctor/dtor и рассматривала поддержку монитора объектов и других инструментов многопоточности (мьютексы, условия и т.д.).
beduin01
Интересно было бы почитать сравнение D с Zig.