AHTOXA 18 1 июля, 2013 Опубликовано 1 июля, 2013 · Жалоба Есть у меня файл, типа 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 и т. д.) - ругается, мол, символ неопределён. Видимо, я что-то делаю неправильно, но вот что? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
neiver 0 1 июля, 2013 Опубликовано 1 июля, 2013 · Жалоба Если строчка: typedef typename SpiEnumTraits<chip::spi_count>::SpiNum SpiNum; объявлена вне шаблонного класса, то ключевое слово typename лишнее. А так работает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 1 июля, 2013 Опубликовано 1 июля, 2013 · Жалоба Это я уже от безысходности добавил. И без 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, если точнее). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 2 июля, 2013 Опубликовано 2 июля, 2013 · Жалоба Делаю так: 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 ... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 2 июля, 2013 Опубликовано 2 июля, 2013 · Жалоба Так я тоже пробовал, и это тоже не работает: if (spiNum == SpiNum::SPI_1) ... error: 'SpiNum' is not a class or namespace. :( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 2 июля, 2013 Опубликовано 2 июля, 2013 · Жалоба Так я тоже пробовал, и это тоже не работает: if (spiNum == SpiNum::SPI_1) ... error: 'SpiNum' is not a class or namespace. :( SpiNum должен быть typedef'ом на весь класс, а не на его внутренний enum Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 2 июля, 2013 Опубликовано 2 июля, 2013 · Жалоба А, пардон, я невнимательно прочитал. Да, так работает, но это не то, что я хотел:) Писать (дописывать) в куче мест лишний префикс - неохота. Хотелось найти полный эквивалент варианту с define. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Tahoe 0 5 июля, 2013 Опубликовано 5 июля, 2013 · Жалоба enum SpiNum { SPI_1, #if (SPI_COUNT>1) SPI_2, #elif (SPI_COUNT>2) SPI_3 #endif }; Как-то сама идея выглядит не очень. А если в контроллере есть SPI1 и SPI3, но отсутствует SPI2? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 11 апреля, 2015 Опубликовано 11 апреля, 2015 · Жалоба Недавно вернулся к этой теме, но немного по другому поводу. Повод такой: бывает так, что, в зависимости от номера периферийного модуля, он может иметь разные свойства. Например, каждый канал 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); ,то получим ошибку времени компиляции. Кроме того, парсер эклипсы настолько умён, что в автоподстановке показывает правильные возможные значения для параметра. ЗЫ. Результат целиком можно посмотреть здесь. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 141 11 апреля, 2015 Опубликовано 11 апреля, 2015 · Жалоба Интересно, взял на заметку. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ashr 0 20 апреля, 2015 Опубликовано 20 апреля, 2015 · Жалоба ОФФ. А мне вот интересно, IAR будет воплощать у себя хоть что-то из C++11/14 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
esaulenka 7 30 апреля, 2015 Опубликовано 30 апреля, 2015 · Жалоба Недавно вернулся к этой теме, но немного по другому поводу. ЗЫ. Результат целиком можно посмотреть здесь. Во-первых, спасибо. Получилось довольно аккуратно, микро-классы для пинов - вообще отличные. А во-вторых, как всем этим пользоваться? 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 18 30 апреля, 2015 Опубликовано 30 апреля, 2015 · Жалоба Во-первых, спасибо. Получилось довольно аккуратно, микро-классы для пинов - вообще отличные. Пожалуйста. Рад, что пригодилось. А во-вторых, как всем этим пользоваться? Ну, я так и пользуюсь: 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 7 30 апреля, 2015 Опубликовано 30 апреля, 2015 · Жалоба Это некорректный пример, так у меня тоже работает :-) Ведь если "CH_SEL_" затереть, там появится ещё с полсотни вариантов. У меня, правда, в обратном порядке всё работает - проект конфигурируется галками, и эклипс сам генерирует make-файлы. Какой-то супер-гибкости мне не надо, а потешить самолюбие (я тут всё контролирую!) и так можно - make-файлы лежат себе на видном месте. Я просто думал, что есть какая-то галка, чтобы эклипс подставлял для enum'а только значения, которые в нём разрешены. Ну, нет так нет... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 30 апреля, 2015 Опубликовано 30 апреля, 2015 · Жалоба Это некорректный пример, так у меня тоже работает :-) Ведь если "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_ не появляется, и варианты выдаются целиком. Но тоже строго те, которые надо. Вот, записал мультик: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться