Forger 26 15 сентября, 2023 Опубликовано 15 сентября, 2023 · Жалоба 4 hours ago, Arlleex said: Вы используете какие-то архитектурные паттерны проектирования? Никогда не вникал, какие именно паттерны подходят под мой код, но синглтоны, например, точно использовал. У меня исключительно модульная структура проекта, где каждый модуль ничего друг о друге не знает. Для связи модулей используется схема slot-signal, позаимствованная из Qt, но тут на плюсах используется для их реализации делегаты. Модули между собой связываются перед их запуском в неком строгом порядке, который железно зашит в базовую реализацию абстрактного класса AbstractApplication. Это нужно для правильной инициализации слотов и сигналов и их привязки. Этот базовый для всех моих существующих проектов одинаков. Поддержка RTOS сюда "вшита", но есть такая же реализация без RTOS (для крохотных камней). Вся эта конструкция отлаживалась и вылизывалась на одном из проектов, другие схемы построения мне более неинтересны. В такой схеме не нужно с нуля проектировать структуру любого нового проекта перед его созданием, поскольку база уже есть и достаточно лишь подключить к проекту ее исходники, лежащие в общей для всех проектов папке Common/Application. Там же лежат кстати, библиотеки для работы с железом под разные камни, разные мат. библиотеки и пр. по мелочи, что подойдет под любой проект без привязки к его железу. Вот, например, как выглядит главный по сути исходник Application.cpp одного из проектов: Spoiler //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Project: xxxxxx // Description: Application //////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include <AbstractApplication.hpp> #include <Memory.hpp> #include <RCC.hpp> // Project #include "ProjectSettings.hpp" #include "Inputs.hpp" #include "UserInterface.hpp" #include "Settings.hpp" #include "Service.hpp" //////////////////////////////////////////////////////////////////////////////////////////////////////////////// constexpr auto kHeapSize = 122 * 1024; constexpr auto kStackSize = 1024; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Application // //////////////////////////////////////////////////////////////////////////////////////////////////////////////// class SomeApplication final : public Application<kHeapSize, kStackSize> { virtual void initializeCore() final; virtual void connectModules() final; virtual void installModules() final; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SomeApplication::connectModules() { UserInterface::getInstance().signal.inputs.configurate = Inputs::getInstance().slot.configurate; UserInterface::getInstance().signal.inputs.synchronize = Inputs::getInstance().slot.synchronize; UserInterface::getInstance().signal.inputs.getWarningCounter = Inputs::getInstance().slot.getWarningCounter; UserInterface::getInstance().signal.settings.getInt = Settings::getInstance().slot.getInt; UserInterface::getInstance().signal.settings.getString = Settings::getInstance().slot.getString; ...... Inputs::getInstance().signal.settings.getInt = Settings::getInstance().slot.getInt; Inputs::getInstance().signal.settings.getString = Settings::getInstance().slot.getString; Inputs::getInstance().signal.settings.isDemoModeEnabled = Settings::getInstance().slot.isDemoModeEnabled; .... Settings::getInstance().signal.getUsbVoltage = Inputs::getInstance().slot.getUsbVoltage; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SomeApplication::installModules() { install(Settings::getInstance()); install(Service::getInstance()); install(Inputs::getInstance()); install(UserInterface::getInstance()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SomeApplication::initializeCore() { Hardware::Power::initialize(); Hardware::Flash::initialize(); Hardware::Clock::initialize(); Hardware::Clock::HSE::setQuartzFrequencyHz(HSE_FREQUENCY_HZ); Hardware::Clock::HSE::enable(); Hardware::Clock::HSE::waitForReady(); Hardware::Clock::HSE::setAsClockSource(); Hardware::Clock::AHB::setPrescaler(Hardware::Clock::AHB::Prescaler::NONE); Hardware::Clock::APB1::setPrescaler(Hardware::Clock::APB1::Prescaler::PRE_4); // Max 42 MHz Hardware::Clock::APB2::setPrescaler(Hardware::Clock::APB2::Prescaler::PRE_2); // Max 84 MHz Hardware::Clock::PLL::setClockSource(Hardware::Clock::PLL::HSE); Hardware::Clock::PLL::setPrescaler(HSE_FREQUENCY_MHZ / PLL_INPUT_FREQUENCY_MHZ); Hardware::Clock::PLL::setMultiplier(PLL_OUTPUT_FREQUENCY_MHZ / PLL_INPUT_FREQUENCY_MHZ); Hardware::Clock::PLL::setSysClockDevider(PLL_OUTPUT_FREQUENCY_MHZ / CORE_FREQUENCY_MHZ); Hardware::Clock::PLL::setupUsbClock(); // 48 MHz only Hardware::Clock::PLL::enable(); Hardware::Clock::PLL::waitForReady(); Hardware::Clock::PLL::setAsClockSource(); Hardware::Flash::setLatency(); Hardware::Flash::Cache::enable(); // Setup OS settings osSettings.coreFrequencyHz = Hardware::Clock::getSystemFrequencyHz(); osSettings.systemTimerFrequencyHz = RTOS_SYSTEM_TIMER_FREQUENCY_HZ; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // MAIN // /////////////////////////////////////////////////////////////////////////////////////////////////////////////// int main() { static SomeApplication application; application.initialize(); application.run(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// Там кстати внизу исходника весь main(), причем он практически одинаковый для всех проектов. В этом проекте используется нештатная работа с кучей (на базе TLSF, кто знает), показала себя довольно неплохо. Разумеется, подобная конструкция - огромный труд и к нему даже делал для себя небольшую документация в виде схем связи, что там внутри и как работает, без этого сложно наглядно понимать почему и куда. Но в этом огромный плюс - я один раз отладил и довольно трудоемкий кусок кода, который всегда есть в каждом проекте, но к нему уже не нужно более возвращаться. Это экономит огромную кучу времени! И позволяет переносить из проекта в проект целые модули с небольшими правками без разрушения остальной части кода. Заранее скажу хейтерам, который любят экономить каждый байт, каждый бит где надо и не надо. Здесь я себе поставил задачу, что это вторично, на деле так и выходило - все мои проекты не требовали супер ущербного камня, да и его стоимость составляет от проектов доли процента. Для проектов где нужно было ужаться, существует решение такой же но очень урезанной схемы, там нет ртос. Тоже на делегатах и слотах сигналах, оказалось что это не так уж страшно, зато код предельно структурирован и потому прост в сопровождении и отладке (по крайней мере для меня). 4 hours ago, Arlleex said: Возможен ли какой-то контроль-указание к жесткой девиртуализации (а если это не возможно - ошибка компиляции) виртуальных вызовов через интерфейсы? А для чего это нужно? Какой-нибудь наглядный пример Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
x893 60 16 сентября, 2023 Опубликовано 16 сентября, 2023 · Жалоба 23 hours ago, Forger said: А для чего это нужно? Больше букв - больше денег. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 17 сентября, 2023 Опубликовано 17 сентября, 2023 · Жалоба ...это признак "индусского кода" - по древнему преданию, индусские программисты получали оплату пропорционально количеству строк. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 17 сентября, 2023 Опубликовано 17 сентября, 2023 · Жалоба 1 hour ago, EdgeAligned said: это признак "индусского кода" - по древнему преданию а нынче это "придание" уже не работает? )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
x893 60 17 сентября, 2023 Опубликовано 17 сентября, 2023 · Жалоба 2 hours ago, Forger said: а нынче это "придание" уже не работает? )) Конечно работает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 183 17 сентября, 2023 Опубликовано 17 сентября, 2023 · Жалоба 15 часов назад, x893 сказал: Больше букв - больше денег. Как то, о чем я писал, связано с "больше букв"? В 15.09.2023 в 21:03, Forger сказал: А для чего это нужно? Какой-нибудь наглядный пример Чтобы, например, писать обработчики очень быстрых и частых прерываний в ООП-стиле. У меня есть пара-тройка проектов, где по наследству достались, например ногодрыжные UART-ы с одновременными программными ШИМ-ами. Дергать лапы и считывать с них уровни виртуальными функциями на 8-битной ATMega сломает текущую возможность реализации проекта вовсе. Если мы говорим об универсализации подходов к написанию программы, то я хочу уметь как-то сказать компилятору, мол, хоть у тебя тут все абстрактно и написано, ты виртуальные вызовы разверни в обычные у конкретных объектов, т.к. конечные типы объектов, на которые ссылаются интерфейсы, компилятор прекрасно видит. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 17 сентября, 2023 Опубликовано 17 сентября, 2023 · Жалоба Мы раньше на 8-битках на ассемблере писали, а нынче С×× мастрячат на них... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 17 сентября, 2023 Опубликовано 17 сентября, 2023 · Жалоба 8 hours ago, Arlleex said: Чтобы, например, писать обработчики очень быстрых и частых прерываний в ООП-стиле. у меня для этой цели используются делегаты, а точнее вместо таблицы прерываний таблица делегатов, оверхед крайне ничтожный, это - вызов делегата обработчика прерывания: Обычное, классическое прерываний чуть короче (на две инструкции). Вот например как я объявляю некоторые примитивные обработчики как лямбда-функции: class SettingsLib : public Settings { virtual void connectSlots() final; virtual void run() final; .... class ThreadUSB : public OS::Timer<kStackSize> { private: virtual void initialize() final; virtual void body() final; .... private: Hardware::Interrupt usbInterrupt; ....... extern "C" void OTG_FS_IRQHandler(); void SettingsLib::ThreadUSB::initialize() { static auto interrupt = [this]() { OTG_FS_IRQHandler(); }; Delegate<void()> vector = interrupt; usbInterrupt.installVector(vector); usbInterrupt.initialize(OTG_FS_IRQn); usbInterrupt.enable(); ... вот и все, больше ничего не нужно делать в коде, никаких виртуальных функций для обработчика, более того тут он вообще внутри одного из методов класса и при этом вызывает внешнюю чужую библиотечную функцию но самый цимус таких обработчиков прерываний - все они являются по сути обычными методами класса и поэтому имеют все нужные преемущества, в отличие от внешних с-функций, где приходится им давать видимость до самых дебрей экземпляров того или иного класса Кстати, именно гемор с обработчиками прерываний и стал причиной поиска нормального решения, которое меня полностью устраивало бы. Такое решение прекрасно устраивает, использую даже в самых убогих и тщедушных МК )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
std 8 17 сентября, 2023 Опубликовано 17 сентября, 2023 · Жалоба On 9/15/2023 at 8:19 PM, Arlleex said: Возможен ли какой-то контроль-указание к жесткой девиртуализации (а если это не возможно - ошибка компиляции) виртуальных вызовов через интерфейсы? ИМХО, в программах под МК компилятор при доступе к объекту по указателю на интерфейс практически всегда может "прощупать" точный тип объекта и заменить виртуальный вызов обычным, выкидывая таблицу виртуальных функций и, соответственно, накладные расходы на вызов. Компилятор может вывести и применить девиртуализацию только двумя способами. 1. Виртуальный метод или весь класс помечен final. 2. Класс определен в анонимном пространстве имен и не имеет производных классов в единице трансляции. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 44 18 сентября, 2023 Опубликовано 18 сентября, 2023 · Жалоба 10 hours ago, Forger said: у меня для этой цели используются делегаты А я предпочитаю удобство, поэтому обработчик прерывания это виртуальная функция. Быстродействия и памяти хватает, почему бы не упростить себе жизнь? class IRQ { ..... // Обработчик прерывания. virtual void IRQ_Handler( void ) = 0; IRQ* installInterruptHandler ( #ifdef __STM32__ IRQn_Type IRQn // See "stm32fxxx.h", enum IRQn_Type. #endif ); IRQ* uninstallInterruptHandler ( #ifdef __STM32__ IRQn_Type IRQn // See "stm32fxxx.h", enum IRQn_Type. #endif ); ..... }; class SWTimer : protected IRQ { ..... protected: // Обработчик прерывания. virtual void IRQ_Handler( void ); ..... }; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 18 сентября, 2023 Опубликовано 18 сентября, 2023 · Жалоба ...а как всё просто было на Си 🙂 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 18 сентября, 2023 Опубликовано 18 сентября, 2023 · Жалоба 3 hours ago, tonyk_av said: поэтому обработчик прерывания это виртуальная функция чтобы он правильно вызывался, нужно в вершину стека положит this, что даст доступ к таблице виртуальных функций и уже оттуда вызовется сам обработчик, посмотрите disasm, результат может расстроить, ибо это работает намного дольше, чем через делегат у вас должна быть где-то большая таблица оберток вокруг каждого обработчика, которая будет класть туда нужный this соотв. класса, т.е. таблица эта должна иметь доступ к экземплярам всех классов, внутри которых есть обработчик прерываний, либо использовать "менеджер" этих обработчиков, который будет осуществлять это отложенное связывание, ибо статическое связывание (на этапе компиляции) вообще не имеет смысла и мало чем отличается от обычных c-обработчиков короче, покажите ваш полный пример с содержимым этого обработчика и как он вызывается через вектора прерываний (которые по сути указатели на обычные с-функции) ? 3 hours ago, tonyk_av said: А я предпочитаю удобство удобство, это когда просто объявляешь самый обычный не виртуальный метод в полях класса и где-то помечаешь что это обработчик, но в МК к сожалению аппаратно предусмотрена таблица прерываний только на С-функции, без доп. таблицы для this с++ методов, т.е. без костылей никак (( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 18 сентября, 2023 Опубликовано 18 сентября, 2023 · Жалоба С точки зрения микроконтроллера, таблица векторов прерываний - это просто адреса для перехода на начало исполняемого кода прерывания. А уж каким образом этот адрес получается - задача программист и компилчтора Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 18 сентября, 2023 Опубликовано 18 сентября, 2023 · Жалоба 5 minutes ago, EdgeAligned said: это просто адреса для перехода на начало исполняемого кода прерывания. о чем выше и написал, это таблица указателей на С-функции, а для плюсового кода нужны указатели с++ функции, это и создает существенные проблемы при использовании обработчиков прерываний в качестве методов классов мы не рассматриваем убогий способ вызова этих обработчиков явной из обычных с-функций, это - самое бестолковое решение, которое кардинально нарушает структуру проекта, я прошел через эти грабли, намучался изрядно и пришлось искать решение, простое и при этом очень быстрое если у кого есть другое решение, публикуйте ) кому как, но мне вот было бы очень интересно изучить ) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrYuran 27 18 сентября, 2023 Опубликовано 18 сентября, 2023 · Жалоба On 9/18/2023 at 6:53 AM, EdgeAligned said: ...а как всё просто было на Си 🙂 До некоторого уровня - да. А далее - "обманчивая красота подобных систем, однако, сильно воздействует на неискушенных пользователей " - и далее по тексту )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться