Комментарии 12
Объектно-ориентированное программирование подразумевает наличие в программе классов, взаимодействующих между собой.
Объектов, а не классов. Оно так и называется объектно-ориентированное.
+1
То есть код, в котором в классах только статические методы (соответственно, объекты создавать смысла нет) не относится к ООП?
0
Нет. Просто объектно-ориентированный код, написанный без классов, не перестаёт быть объектно-ориентированным. И в программе взаимодействуют объекты, а классы — это один из способов описать поведение объектов.
+2
Да, код в котором не создаётся ни одного объекта, а используются только статические методы — это не ООП.
0
Если объект статический, то будет )))
А вообще, программирование с помощью ключевых слов class, virtual и т.д. не всегда объектно-ориентированное. Либо у вас есть какие-то сущности, инкапсулирующие свое состояние и как-то взаимодействующие, либо нет. Как это технологически сделано вопрос десятый. А иногда, как сказал Роб Пайк, данные — это просто данные.
А вообще, программирование с помощью ключевых слов class, virtual и т.д. не всегда объектно-ориентированное. Либо у вас есть какие-то сущности, инкапсулирующие свое состояние и как-то взаимодействующие, либо нет. Как это технологически сделано вопрос десятый. А иногда, как сказал Роб Пайк, данные — это просто данные.
0
Стоит ли из-за 1-2% от общего кода разводить всё это?
Делается один раз (HAL/LL/CMSIS) за 2-3 часа и больше не меняется.
Делается один раз (HAL/LL/CMSIS) за 2-3 часа и больше не меняется.
+3
Стоит.
Во-первых это по-своему красиво.
Во-вторых отличное применение шаблонам.
А, напоследок, отлично объясняет зачем в кресты добавили constexpr/consteval и отучает использовать шаблоны в более сложных местах :)
Во-первых это по-своему красиво.
Во-вторых отличное применение шаблонам.
А, напоследок, отлично объясняет зачем в кресты добавили constexpr/consteval и отучает использовать шаблоны в более сложных местах :)
+2
Возможно, Вы правы, если бы это было целесообразно, промышленность (или даже сами производители) пользовалась бы. Мне всё это дело просто интересно, помогает в более интересном виде изучать нововведения C++, который я преподаю.
0
Шаблонное программирование — это не просто кодогенерация, а программирование в пространстве типов. Поэтому вместо страшного кода с кучей параметров проще создать новый тип:
Похожим образом сделана стандартная библиотека с++, хотя мой пример упрощен. Обрати внимание на наличие списка констант — это очень удобная фича.
Далее, необходимо четко разделять сущности. uart — это uart, gpio — это gpio. Мой опыт показывает, что смешивать разные сущности по возможности не стоит, гораздо проще сгруппировать родственную периферию, а потом настроить её одной строкой:
Этот класс довольно туповат и неоптимален, по-крайней мере для stm32 (есть микроконтроллеры, у которых каждый gpio настраивается своей группой регистров, там быстрее не будет)
Оптимизириванный вариант, если таковой возможен, должен использовать маски для группы gpio (пример)
А применение этого добра классическое:
Uart же более правильно проинициализировать отдельно, включение прерываний также стоит сделать отдельно. Исключения, конечно бывают, Clock Tree и Flash в тех же stm-ках зависят друг от друга, поэтому при натройки ClockTree должен знать о конфиге флеша.
Что касается статей lamerok, в них много интересного, но шаблонная обертка над каждым битом просто лишена смысла. Технически, она может привести к проблемам компиляции при многоуровневой специализации шаблонов.
Результат эквивалентен, тем более он будет упакован в функцию Init, вызываемую нами в коде. У меня в этом случае четкая ассоциация с блоками unsafe из Rust — я знаю, что здесь может быть ошибка, и найти её легко, тем более библиотечный код интенсивно тестируется.
Гораздо эффективнее сделать и отладить библиотеку для работы с ip-ядрами, тем более генерация обертки над каждым битом не всегда возможна (TI иногда несколько отходит от стандарта CMSIS).
struct ExampleTrait {
constexpr static std::uint32_t kParam1 = 0;
constexpr static std::uint32_t kParam2 = 1;
};
template < class Trait >
class Bar {
constexpr static std::uint32_t kParam1 = Trait::kParam1;
constexpr static std::uint32_t kParam2 = Trait::kParam2;
// Далее идут другие константы и набор статических функций
};
Похожим образом сделана стандартная библиотека с++, хотя мой пример упрощен. Обрати внимание на наличие списка констант — это очень удобная фича.
Далее, необходимо четко разделять сущности. uart — это uart, gpio — это gpio. Мой опыт показывает, что смешивать разные сущности по возможности не стоит, гораздо проще сгруппировать родственную периферию, а потом настроить её одной строкой:
template < class... dev >
class DeviceSet
{
public:
inline constexpr static void Init() { ( dev::Init(), ... ); };
};
template < class... IO >
class IoSet final: public DeviceSet< IO... >
{
public:
inline constexpr static void Set() { ( IO::Set(), ... ); };
inline constexpr static void Reset() { ( IO::Reset(), ... ); };
inline constexpr static void Toggle(){ ( IO::Toggle(), ... ); };
};
Этот класс довольно туповат и неоптимален, по-крайней мере для stm32 (есть микроконтроллеры, у которых каждый gpio настраивается своей группой регистров, там быстрее не будет)
Оптимизириванный вариант, если таковой возможен, должен использовать маски для группы gpio (пример)
А применение этого добра классическое:
using Leds = mpp::gpio::IoGroup < LedBlue, LedRed, LedOrange, LedGreen >;
bsp::Leds::Init();
bsp::Leds::Toggle();
Uart же более правильно проинициализировать отдельно, включение прерываний также стоит сделать отдельно. Исключения, конечно бывают, Clock Tree и Flash в тех же stm-ках зависят друг от друга, поэтому при натройки ClockTree должен знать о конфиге флеша.
Что касается статей lamerok, в них много интересного, но шаблонная обертка над каждым битом просто лишена смысла. Технически, она может привести к проблемам компиляции при многоуровневой специализации шаблонов.
_Regs::CR1Pack<_Regs::CR1::UE, _Regs::CR1::RE, _Regs::CR1::TE>::Set();
// Еще какие-то настройки.
UART->CR1 = UART->CR1 | UART_CR1_UE | UART_CR1_RE | UART_CR1_TE;
Результат эквивалентен, тем более он будет упакован в функцию Init, вызываемую нами в коде. У меня в этом случае четкая ассоциация с блоками unsafe из Rust — я знаю, что здесь может быть ошибка, и найти её легко, тем более библиотечный код интенсивно тестируется.
Гораздо эффективнее сделать и отладить библиотеку для работы с ip-ядрами, тем более генерация обертки над каждым битом не всегда возможна (TI иногда несколько отходит от стандарта CMSIS).
+3
Что касается статей lamerok, в них много интересного, но шаблонная обертка над каждым битом просто лишена смысла.
Поправлю, там обертка не над каждым битом, а над значением поля регистра. Смысл был в том, чтобы запретить ставить значения не описанные спецификацией. В случае с CMSIS можно поставить что угодно, и какой угодно define, который может быть вообще не иметь отношения к регистру. В обертке де будет ошибка компиляции.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Снова про шаблоны C++ в микроконтроллерах