Перейти к содержанию
    

С++ Объявить вариантное перечисление.

Есть у меня файл, типа 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 и т. д.) - ругается, мол, символ неопределён. Видимо, я что-то делаю неправильно, но вот что?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если строчка:

typedef typename SpiEnumTraits<chip::spi_count>::SpiNum SpiNum;

объявлена вне шаблонного класса, то ключевое слово typename лишнее. А так работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это я уже от безысходности добавил. И без 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, если точнее).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Делаю так:

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 ...

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так я тоже пробовал, и это тоже не работает:

if (spiNum == SpiNum::SPI_1)
...

error: 'SpiNum' is not a class or namespace.

:(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так я тоже пробовал, и это тоже не работает:

if (spiNum == SpiNum::SPI_1)
...

error: 'SpiNum' is not a class or namespace.

:(

SpiNum должен быть typedef'ом на весь класс, а не на его внутренний enum

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А, пардон, я невнимательно прочитал. Да, так работает, но это не то, что я хотел:)

Писать (дописывать) в куче мест лишний префикс - неохота. Хотелось найти полный эквивалент варианту с define.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

enum SpiNum {
    SPI_1,
#if (SPI_COUNT>1)
    SPI_2,
#elif (SPI_COUNT>2)
    SPI_3
#endif
};

Как-то сама идея выглядит не очень. А если в контроллере есть SPI1 и SPI3, но отсутствует SPI2?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Недавно вернулся к этой теме, но немного по другому поводу.

Повод такой: бывает так, что, в зависимости от номера периферийного модуля, он может иметь разные свойства.

Например, каждый канал 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);

,то получим ошибку времени компиляции.

Кроме того, парсер эклипсы настолько умён, что в автоподстановке показывает правильные возможные значения для параметра.

 

ЗЫ. Результат целиком можно посмотреть здесь.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Недавно вернулся к этой теме, но немного по другому поводу.

ЗЫ. Результат целиком можно посмотреть здесь.

Во-первых, спасибо. Получилось довольно аккуратно, микро-классы для пинов - вообще отличные.

 

А во-вторых, как всем этим пользоваться?

 

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'ы.

 

 

Кроме того, парсер эклипсы настолько умён, что в автоподстановке показывает правильные возможные значения для параметра.

Это тоже не работает, даже в вышеприведённом варианте - предлагает всё подряд. Где-то есть галка?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Во-первых, спасибо. Получилось довольно аккуратно, микро-классы для пинов - вообще отличные.

Пожалуйста. Рад, что пригодилось.

А во-вторых, как всем этим пользоваться?

Ну, я так и пользуюсь:

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.

Это тоже не работает, даже в вышеприведённом варианте - предлагает всё подряд. Где-то есть галка?

Ещё раз проверил, работает. Вот картинка:

post-29684-1430391875_thumb.png

 

Возможно, у вас не натроен поиск инклюдов в эклипсе. Посмотрите вот эту тему: тынц.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это некорректный пример, так у меня тоже работает :-)

Ведь если "CH_SEL_" затереть, там появится ещё с полсотни вариантов.

 

У меня, правда, в обратном порядке всё работает - проект конфигурируется галками, и эклипс сам генерирует make-файлы. Какой-то супер-гибкости мне не надо, а потешить самолюбие (я тут всё контролирую!) и так можно - make-файлы лежат себе на видном месте.

 

 

Я просто думал, что есть какая-то галка, чтобы эклипс подставлял для enum'а только значения, которые в нём разрешены. Ну, нет так нет...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это некорректный пример, так у меня тоже работает :-)

Ведь если "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_ не появляется, и варианты выдаются целиком. Но тоже строго те, которые надо.

Вот, записал мультик:

post-29684-1430423980_thumb.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...