haker_fox 61 20 октября, 2021 Опубликовано 20 октября, 2021 · Жалоба P.S. Резюмирую. Да, я немного плыву в теме накладных расходов при использовании классов и виртуальных методов. И ещё много в чём. Но не я один плыву. Например, тут утверждалось постоянно, что будет использоваться VTABLE при использовании виртуальных методов. Чтож, будет. Но только при определённых обстоятельствах, котоыре не уточнялись. А не всегда. Так что... вывод каждый может делать сам. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 224 20 октября, 2021 Опубликовано 20 октября, 2021 · Жалоба 2 часа назад, haker_fox сказал: Например, тут утверждалось постоянно, что будет использоваться VTABLE при использовании виртуальных методов. Чтож, будет. Но только при определённых обстоятельствах, котоыре не уточнялись. А не всегда. Так что... вывод каждый может делать сам. Если Вы о том, что оно используется только при вызове через указатель на базовый класс, так вроде это должно быть само собой очевидным. Как и само собой должно быть очевидным, что если вызовы методов через указатели на базовый класс не используются, то и нет никакого смысла в виртуальных членах. О чём Вам компилятор как бы косвенно и намекает, удаляя обращения к VTABLE (да и саму VTABLE наверняка удаляет, если нигде больше в коде нет таких вызовов). Так что - если нет нигде вызовов например write() через указатель на базовый класс, то зачем её сделали virtual? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 21 октября, 2021 Опубликовано 21 октября, 2021 · Жалоба 8 hours ago, jcxz said: если нет нигде вызовов например write() через указатель на базовый класс Сейчас нет. Завтра появятся. Что же мне добавлять virtual в спешном порядке? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 165 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба Вот что интересно. Допустим, разрабатывается класс FIFO для UART, который будет работать в DMA-режиме. Для разных UART-модулей, во-первых, разные базовые адреса периферии, во-вторых, существуют зависимости, которые напрямую к UART не относятся (однако класс должен получать информацию на основе этих зависимостей). Как пример такой зависимости - регистры статуса DMA: там флаги всех потоков кучкой распиханы в пару регистров. У меня на ум приходит только добавлять в класс приватные поля с нужными масками, адресами регистров и т.д. и инициализировать их при объявлении конкретного объекта (ведь у меня может быть, например, 5 разных каналов UART). Это нормальный подход или кривой? Если это нормальный подход, то вопрос номер два: указатель на базовый адрес периферии (например, DMA-потока) объявлять в классе обобщенным указателем void * или типом структуры регистров? Как по мне void * имеет то преимущество, что не нужно в hpp-файле подключать stm32f4xx.h со всем его жирным содержимым ради лишь определения DMA_Stream_TypeDef. А в нужном месте просто приводить void * к этому DMA_Stream_TypeDef. Где-то видел такой прием, вопрос в адекватности такого подхода. P.S. Да, я понимаю, что если структура управляющих битов для всех периферийных узлов регулярна (т.е. на основе лишь номера периферийного узла или того же DMA-потока можно рассчитать все положения уже конкретных управляющих регистров/битов/масок и т.д.), то можно обойтись передачей в конструктор класса лишь одного числа. Но это а) не гибко, т.к. бывают не регулярные структуры и б) выполняется в run-time, что плохо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 133 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 1 час назад, Arlleex сказал: Как пример такой зависимости - регистры статуса DMA: там флаги всех потоков кучкой распиханы в пару регистров. У меня на ум приходит только добавлять в класс приватные поля с нужными масками, адресами регистров и т.д. Я храню номер канала ПДП, маски и адреса регистров расчитываются на лету - в кортексах это почти бесплатно. 1 час назад, Arlleex сказал: вопрос номер два: указатель на базовый адрес периферии (например, DMA-потока) объявлять в классе обобщенным указателем void * или типом структуры регистров? Перефразируя закон Мерфи "Если в функцию можно передать указатель на что угодно, в нее обязательно передадут указатель на какую-нибудь гадость. 1 час назад, Arlleex сказал: Как по мне void * имеет то преимущество, что не нужно в hpp-файле подключать stm32f4xx.h со всем его жирным содержимым ради лишь определения DMA_Stream_TypeDef. uart_dma(struct DMA_Stream_TypeDef * dma), т.е. forward declaration не спасет? Вы же саму структуру в класс не засовываете, только указатель на нее. Добавлено: Я многие функции делаю встраиваемыми, поэтому подключать stm32f4xx приходится и я даже не беру в голову его размер - он же не в выходной файл добавляется. Более того, я подключаю не stm32f4xx, а свой stm32.h, в который подключается нужный stm32xxxxx,h в зависимости от выбранного в проекте целевого контроллера, ибо у f4 и f2 ПДП одинаковый. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 224 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 1 час назад, Arlleex сказал: Как по мне void * имеет то преимущество, что не нужно в hpp-файле подключать stm32f4xx.h со всем его жирным содержимым ради лишь определения DMA_Stream_TypeDef. Просто в .h перед объявлением функции напишите (неполное объявление структуры): struct DMA_Stream_TypeDef; Этого достаточно. Подключать ничего не нужно. Везде так делаю: struct SocketTarget; int HttpBuild(char const *url, char *buf, uint sizbuf, char const *httpTail, SocketTarget * = NULL); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 165 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба Про forward declaration запамятовал, точно. Спасибо Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 165 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба Возможны ли еще какие-либо способы инициализации констант-членов класса, кроме как через список инициализации? В классе, например, объявлен константный указатель на некий u32 class MyClass { volatile u32 *const ptr; public: MyClass(u32 N); }; Теперь на основе N я хочу создать некий указатель, который должен лечь в ptr. Т.е. формирование ptr будет осуществляться в каком-то (может даже не столь тривиальном) коде исходя из N, что был передан в конструктор. Это можно было бы сделать в теле самого конструктора, не будь const у ptr. Копать в сторону constexpr()-функций? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 224 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 12 минут назад, Arlleex сказал: Возможны ли еще какие-либо способы инициализации констант-членов класса, кроме как через список инициализации? А почему не объявить/определить просто отдельную структуру для const данных? И указатель на неё положить в класс. У меня например так (только я делаю наоборот: главный - const-класс, а в нём содержится указатель на структуру с переменными): Скрытый текст struct UartHndVar { enum { //спец.состояния eposTx TX_START = 65533, TX_DMA, TX_STOP}; u16 volatile rposTx, wposTx, eposTx; u16 volatile rposRx, wposRx; u16 volatile rxNDTR; u16 txd; u8 lcnt; #if UART_CHK_RDMA_OVER u8 nmar; #endif OsFlag txNoFull, rxNoEmpty; }; struct UartHnd { struct IoIsrIfcr { u32 volatile ISR; u32 unuse; u32 volatile IFCR; }; UartHndVar *var; HwRegsUART *uart; char *bufTx, *bufRx; u16 sizTx; u16 sizTxFlush; u16 sizRx; //должно быть >= UART_RDMA_CHUNK*3 и кратно UART_RDMA_CHUNK u8 nDMASTRtx, nDMASTRrx; u8 uartNvic, txDmaNvic, rxDmaNvic; u8 rccUart; u8 trapRegion; u8 overlock; u8 dmaTxStat, dmaRxStat; IoIsrIfcr *dmaTxISR, *dmaRxISR; HwRegsDMA::T_STREAM *streamTx, *streamRx; u32 streamTxCR, streamRxCR; void Init() const; void Isr() const; void IsrTDMA() const; void IsrRDMA() const; void StartTDMA() const; void StartTx() const; void StartTx(int) const; u32 StartRx(u32) const; void Stop() const; void Timer() const; void TimerFast() const; void Tx(int) const; void Tx(void const *, uint) const; int Rx() const; int Rx(u32, u32) const; int Rx(void *, uint) const; int NoEmptyTx() const { int i = var->rposTx; return i - var->wposTx; } int NoEmptyRx() const { int i = var->rposRx; i -= var->wposRx; return i | var->rxNDTR >> 15; } private: void Clear() const; }; Внутри членов UartHnd всё удобно: __no_init struct { char tx[400]; char rx[UART_RDMA_CHUNK_ALIGN(150)]; } static bufTerm @ ".dma"; static __no_init UartHndVar termHndVar; static UartHnd const termUartHnd = { .var = &termHndVar, .bufTx = &bufTerm.tx[0], .bufRx = &bufTerm.rx[0], .sizTx = sizeof(bufTerm.tx), .sizTxFlush = sizeof(bufTerm.tx) / 3, .sizRx = sizeof(bufTerm.rx), UartHnd_SET(term, TRAPR_TERMINAL, ms2tkt(200)) }; ... void UartHnd::Timer() const { CPU_SR_ALLOCATE(); ENTR_CRT_SECTION(); uint i = var->rposTx; if (i != var->wposTx) if (var->eposTx < (uint)var->TX_START) { var->eposTx = var->TX_START; EXIT_CRT_SECTION(); StartTx(); return; } EXIT_CRT_SECTION(); } PS: Если я правильно понял о чём Вы... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 165 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба Идея в том, что я хочу объявить в классе указатели на нужные мне регистры (или вовсе на блок регистров нужной периферии), основываясь лишь на номере этой периферии (или номере DMA-потока). Маски/биты (при необходимости) тоже. И получится, что каждый экземпляр класса будет содержать нужные конкретно ему регистры периферии и т.д. Ну а const, как и в любых подобных случаях, для невозможности явного присвоения в коде. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 22 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 5 minutes ago, Arlleex said: Идея в том, что я хочу объявить в классе указатели на нужные мне регистры (или вовсе на блок регистров нужной периферии), основываясь лишь на номере этой периферии (или номере DMA-потока). Маски/биты (при необходимости) тоже. И получится, что каждый экземпляр класса будет содержать нужные конкретно ему регистры периферии и т.д. Ну а const, как и в любых подобных случаях, для невозможности явного присвоения в коде. Я это делаю через шаблоны, один из параметров - базовый адрес соотв. периферии. Инстанцирование на этапе объявления. Вся общая для всех портов одного вида реализация - в классе родителе этих шаблонов. Скрыта в cpp, поэтому не "захламляет" hpp файл. В ходе мучений и проб пришел только к этой схеме. Она меня устраивает. Извиняюсь за сумбур в описании )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
esaulenka 7 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 18 minutes ago, Arlleex said: Копать в сторону constexpr()-функций? Да, можно так: https://godbolt.org/z/E1djrKEdb Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 165 8 декабря, 2021 Опубликовано 8 декабря, 2021 · Жалоба 3 часа назад, Сергей Борщ сказал: ...т.е. forward declaration не спасет?... Вот попробовал сейчас, кстати... Не спасает(( Тут, видимо, дело в том, что в h-файле этот DMA_Stream_TypeDef определен как typedef struct {...}DMA_Stream_TypeDef; (то есть в стиле пуре-Си). Соответственно, либо менять на C++-стиль (struct DMA_Stream_TypeDef {};), либо приписывать тэг структуре (а этот хидер менять не хочется, вообще говоря). Значит придется в hpp подключать общий stm32f4xx.h. Страшного тут, конечно, ничего нет, но и полезного мало: теперь везде, где подключается fifo.hpp будет виден "мусор" из stm32f4xx.h и мозолить глаза. Не знаю, отчего у меня такая мания стараться отделять мух от котлет Просто хочется, чтобы пробираясь к top-level коду (там, где бизнес-логика), ничего лишнего "видно" не было - никаких битовых масок периферии процессора и т.д. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 165 9 декабря, 2021 Опубликовано 9 декабря, 2021 · Жалоба Использую C++11. Задал несколько enum class внутри класса cUARTDMARx, чтобы пользователь класса выбирал из них namespace nsFIFO { class cUARTDMARx { ... public: enum class eDMAMod { DMA1n = 1, DMA2n }; enum class eDMAStr { STR0n = 0, STR1n, STR2n, STR3n, STR4n, STR5n, STR6n, STR7n }; cUARTDMARx(eDMAMod mod, eDMAStr str); ... }; } Можно ли теперь монструозный синтаксис using namespace nsFIFO; cUARTDMARx fifo(cUARTDMARx::eDMAMod::DMA1n, cUARTDMARx::eDMAStr::STR0n); заменить как-то на нечто покороче ... cUARTDMARx fifo(DMA1n, STR0n); , т.е. каким-то образом раскрыть пространство имен для этих enum class так же, как это было с namespace nsFIFO? Беглый поиск в интернете говорит, что так нельзя сделать в C++11, а можно только начиная с C++20. Или врут? Перетаскивать эти enum class в nsFIFO (вытащив из cUARTDMARx) очень не хочу - эти перечисления живут только для конкретного класса (в данном примере, конечно, они несколько универсальные, но в общем случае там могут быть нужные только этому классу константы). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 22 9 декабря, 2021 Опубликовано 9 декабря, 2021 · Жалоба 2 hours ago, Arlleex said: Можно ли теперь монструозный синтаксис Сокращать можно только внутри этого самого метода, снаружи - нет, нужно конкретизировать области видимости в каждом объявлении. Напрмер, делаю так: RCC.hpp: namespace stm32g0 { .... class Clock { public: struct PLL { using Source = enum { HSI, HSE }; using Multiplier = uint8_t; using Prescaler = uint8_t; using Divider = uint8_t; // Postscaler static void setClockSource(Source); static void setPrescaler(Prescaler); ..... struct HSI { ... Application.cpp: stm32g0::Clock::initialize(); stm32g0::Clock::PLL::setClockSource(stm32g0::Clock::PLL::HSI); // 16 MHz stm32g0::Clock::PLL::setPrescaler(1); // 16 Mhz = 16 MHz stm32g0::Clock::PLL::setMultiplier(8); // 16 MHz x 8 = 128 MHz .... Чтобы избежать конкретизации области видимости, придется выносить объявление в глобальную область. Кстати, а почему в C++11 используете такую странную форму объявления? enum class eDMAMod { от себя рекомендую переходить на using. Благодаря ей, отказался от typedef как от страшного кошмара )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться