|
С++ Объявить вариантное перечисление., Шаблонами. |
|
|
|
Jul 1 2013, 10:49
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Есть у меня файл, типа spi.h, в нём - перечисление всех имеющихся SPI: Код enum SpiNum { SPI_1, #if (SPI_COUNT>1) SPI_2, #elif (SPI_COUNT>2) SPI_3 #endif }; Захотелось мне избавиться от дефайнов, и переделать это на шаблоны. Делаю так: Код template<int SPI_Count> struct SpiEnumTraits; template<> struct SpiEnumTraits<1> { enum SpiNum { SPI_1 }; }; template<> struct SpiEnumTraits<2> { enum SpiNum { SPI_1, SPI_2 }; }; template<> struct SpiEnumTraits<3> { enum SpiNum { SPI_1, SPI_2, SPI_3 }; }; template<> struct SpiEnumTraits<4> { enum SpiNum { SPI_1, SPI_2, SPI_3, SPI_4 }; };
typedef typename SpiEnumTraits<chip::spi_count>::SpiNum SpiNum; На саму эту конструкцию компилятор не ругается, а вот на любые упоминания в тексте программы элементов перечисления (SPI_1 и т. д.) - ругается, мол, символ неопределён. Видимо, я что-то делаю неправильно, но вот что?
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jul 1 2013, 18:50
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Это я уже от безысходности добавил. И без typename - тоже не работает. На собственно тип SpiNum - не ругается, вот это объявление проглатывает нормально: Код template<SpiNum spiNum> struct SpiPins; А на элементы перечисления SpiNum - ругается. Вот тут например: Код template<> struct SpiPins<SPI_1> { typedef Pin<'A', 5> PinSCK; ... - говорит, мол, "error: ‘SPI_1’ was not declared in this scope". scope - тот же файл, никаких namespace-ов, ничего. Забыл написать, компилятор - gcc ( arm-embedded, если точнее).
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jul 2 2013, 08:29
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(AHTOXA @ Jul 1 2013, 14:49)  Делаю так: Код template<int SPI_Count> struct SpiEnumTraits; template<> struct SpiEnumTraits<1> { enum SpiNum { SPI_1 }; }; typedef typename SpiEnumTraits<chip::spi_count>::SpiNum SpiNum; На саму эту конструкцию компилятор не ругается, а вот на любые упоминания в тексте программы элементов перечисления (SPI_1 и т. д.) - ругается, мол, символ неопределён. Правильно ругается - у вас enum получился локальным в классе, а typedef наружу вынес сам enum, а не его элементы. Делайте так Код typedef SpiEnumTraits<chip::spi_count> SPI;
... SPI::SPI_1 ...
|
|
|
|
|
Jul 2 2013, 08:51
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(AHTOXA @ Jul 2 2013, 12:49)  Так я тоже пробовал, и это тоже не работает: Код if (spiNum == SpiNum::SPI_1) ... error: 'SpiNum' is not a class or namespace.  SpiNum должен быть typedef'ом на весь класс, а не на его внутренний enum
|
|
|
|
|
Jul 5 2013, 19:29
|
Местный
  
Группа: Свой
Сообщений: 459
Регистрация: 30-03-06
Из: Москва
Пользователь №: 15 600

|
Цитата(AHTOXA @ Jul 1 2013, 14:49)  Код enum SpiNum { SPI_1, #if (SPI_COUNT>1) SPI_2, #elif (SPI_COUNT>2) SPI_3 #endif }; Как-то сама идея выглядит не очень. А если в контроллере есть SPI1 и SPI3, но отсутствует SPI2?
|
|
|
|
|
Apr 11 2015, 07:25
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Недавно вернулся к этой теме, но немного по другому поводу. Повод такой: бывает так, что, в зависимости от номера периферийного модуля, он может иметь разные свойства. Например, каждый канал DMA на STM32L0xx может быть подключен только к определённому перечню периферийных устройств. (Канал 1 может быть подключен к ADC, TIM2_CH3 и AES_IN, канал 2 - к SPI2_RX, USART2_RX, LPUART1_RX и I2C1_TX. И так далее). Хотелось иметь у каждого канала функцию SelectChannel(), которая бы принимала в качестве аргумента только допустимые для данного канала значения. И я решил посмотреть, не поможет ли тут C++11. Вот что получилось. Объявляем шаблонный класс свойств канала: Код template<DmaChannelNum chNum> struct DmaChannelTraits; Определяем свойства для каждого канала: Код template<> struct DmaChannelTraits<DMA1_CH1> { enum { CHANNEL_NO = 1 }; enum { CSELR_SHIFT = (CHANNEL_NO-1)*4 }; enum class ChannelSelection : uint32_t // possible CSELR values { CH_SEL_ADC = (0x00 << CSELR_SHIFT), CH_SEL_TIM2_CH3 = (0x08 << CSELR_SHIFT), CH_SEL_AES_IN = (0x0B << CSELR_SHIFT), CH_SEL_MASK = (0x0F << CSELR_SHIFT), }; }; И так для всех каналов: Код template<> struct DmaChannelTraits<DMA1_CH6> { enum { CHANNEL_NO = 1 }; enum { CSELR_SHIFT = (CHANNEL_NO-1)*4 }; enum class ChannelSelection : uint32_t // possible CSELR values { CH_SEL_SPI2_RX = (0x02 << CSELR_SHIFT), CH_SEL_USART2_RX = (0x04 << CSELR_SHIFT), CH_SEL_LPUART1_RX = (0x05 << CSELR_SHIFT), CH_SEL_I2C1_TX = (0x06 << CSELR_SHIFT), CH_SEL_MASK = (0x0F << CSELR_SHIFT), }; }; И затем, уже в шаблоне канала: Код template<DmaChannelNum chNum> class DmaChannel { private: typedef DmaChannelTraits<chNum> ChannelTraits; public: using ChannelSelection = typename ChannelTraits::ChannelSelection; static void SelectChannel(ChannelSelection cs) { DMA->CSELR = (DMA->CSELR & ~static_cast<uint32_t>(ChannelSelection::CH_SEL_MASK)) | static_cast<uint32_t>(cs); } } Теперь, если мы попробуем выбрать для канала неверный источник: Код typedef DmaChannel<DMA1_CH1> Dma1Channel1; Dma1Channel1::SelectChannel(Dma1Channel1::ChannelSelection::CH_SEL_SPI2_RX); ,то получим ошибку времени компиляции. Кроме того, парсер эклипсы настолько умён, что в автоподстановке показывает правильные возможные значения для параметра. ЗЫ. Результат целиком можно посмотреть здесь.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Apr 30 2015, 10:33
|

Профессионал
    
Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877

|
Цитата(AHTOXA @ Apr 11 2015, 10:25)  Недавно вернулся к этой теме, но немного по другому поводу. ЗЫ. Результат целиком можно посмотреть здесь. Во-первых, спасибо. Получилось довольно аккуратно, микро-классы для пинов - вообще отличные. А во-вторых, как всем этим пользоваться? Код namespace STM32 { namespace SPI {
struct SpiProps { static const SpiNum NUMBER = SPI_1; static const Remap REMAP = REMAP_NONE; static const Divisor InitialDivisor = SPI_DIV_2; static const Cpol InitialCPOL = CPOL_L; static const Cpha InitialCPHA = CPHA_1; }; } }
STM32::SPI::Spi<STM32::SPI::SpiProps> MySPI; Работает, но выглядит некрасиво. Сделать using namespace не получилось - почему-то из области видимости пропадают enum'ы. Цитата(AHTOXA @ Apr 11 2015, 10:25)  Кроме того, парсер эклипсы настолько умён, что в автоподстановке показывает правильные возможные значения для параметра. Это тоже не работает, даже в вышеприведённом варианте - предлагает всё подряд. Где-то есть галка?
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Apr 30 2015, 11:08
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(esaulenka @ Apr 30 2015, 15:33)  Во-первых, спасибо. Получилось довольно аккуратно, микро-классы для пинов - вообще отличные. Пожалуйста. Рад, что пригодилось. Цитата(esaulenka @ Apr 30 2015, 15:33)  А во-вторых, как всем этим пользоваться? Ну, я так и пользуюсь: Код struct AdcSpiProps { static const STM32::SPI::SpiNum NUMBER = STM32::SPI::SPI_1; static const STM32::SPI::Remap REMAP = STM32::SPI::REMAP_FULL; static const STM32::SPI::Divisor InitialDivisor = STM32::SPI::SPI_DIV_2; static const STM32::SPI::Cpol InitialCPOL = STM32::SPI::CPOL_H; static const STM32::SPI::Cpha InitialCPHA = STM32::SPI::CPHA_1; };
typedef STM32::SPI::Spi<AdcSpiProps> SpiAdc; extern SpiAdc spiAdc; - это в файле hw.h. Там я описываю всю периферию проекта. К нему есть файл hw.cpp - там объявляю переменные периферийных модулей и прописываю обработчики прерываний. Необходимость писать везде STM32::SPI:: конечно немного напрягает, но я к этому отношусь как к этапу конфигурирования проекта, который делается только один раз, в самом начале. Зато эти имена гарантированно не пересекутся с именами ST. Цитата(esaulenka @ Apr 30 2015, 15:33)  Это тоже не работает, даже в вышеприведённом варианте - предлагает всё подряд. Где-то есть галка? Ещё раз проверил, работает. Вот картинка:
Возможно, у вас не натроен поиск инклюдов в эклипсе. Посмотрите вот эту тему: тынц.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Apr 30 2015, 12:54
|

Профессионал
    
Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877

|
Это некорректный пример, так у меня тоже работает :-) Ведь если "CH_SEL_" затереть, там появится ещё с полсотни вариантов.
У меня, правда, в обратном порядке всё работает - проект конфигурируется галками, и эклипс сам генерирует make-файлы. Какой-то супер-гибкости мне не надо, а потешить самолюбие (я тут всё контролирую!) и так можно - make-файлы лежат себе на видном месте.
Я просто думал, что есть какая-то галка, чтобы эклипс подставлял для enum'а только значения, которые в нём разрешены. Ну, нет так нет...
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Apr 30 2015, 20:01
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(esaulenka @ Apr 30 2015, 17:54)  Это некорректный пример, так у меня тоже работает :-) Ведь если "CH_SEL_" затереть, там появится ещё с полсотни вариантов. У меня нет никаких полусотни вариантов  CH_SEL_ Появляется само, потому что это общая часть среди всех имеющихся вариантов. То есть, если я напишу вот так: Код #include "stm32_dma.h"
void Test() { typedef STM32::DMA::Dma1Channel1 DmaCh; DmaCh::SelectChannel(DmaCh::ChannelSelection:: } , помещу курсор после двоеточия, и нажму Ctrl+Space, то эклипса сама дописывает "CH_SEL_", и выдаёт строго то, что нужно. Если не нажимать Ctrl+Space, а немного подождать, то CH_SEL_ не появляется, и варианты выдаются целиком. Но тоже строго те, которые надо. Вот, записал мультик:
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|