"Группировка" значений перечисления в C

Если у меня были некоторые перечисления вроде

typedef enum {
    AN_TRISTATE_0,
    AN_TRISTATE_1,
    AN_NOTHING,
    AN_MOTOR_1,
    AN_MOTOR_2,
    AN_MOTOR_3,
    AN_SENSOR_1,
    AN_SENSOR_2,
    AN_SENSOR_3,
    AN_SENSOR_4,
    AN_SENSOR_5
} adc_pin_func_t;

и

adc_pin_func_t a_particular_pin = ...

можно ли проверить, является ли штырь частью определенной группы, например, pin является частью AN_MOTOR или части AN_SENSOR вместо того, чтобы проверять каждый элемент в каждой возможной группе.

Или есть более эффективные способы сделать это, кроме использования перечислений?

Заранее спасибо

Ответы

Ответ 1

Вы можете создавать маски для каждой из групп:

typedef enum {
    AN_TRISTATE_0     = 0x00001,
    AN_TRISTATE_1     = 0x00002,
    AN_TRISTATE_MASK  = 0x0000f,

    AN_NOTHING        = 0x00010,    // Should this be 0x00000 ?

    AN_MOTOR_1        = 0x00100,
    AN_MOTOR_2        = 0x00200,
    AN_MOTOR_3        = 0x00400,
    AN_MOTOR_MASK     = 0x00f00,

    AN_SENSOR_1       = 0x01000,
    AN_SENSOR_2       = 0x02000,
    AN_SENSOR_3       = 0x04000,
    AN_SENSOR_4       = 0x08000,
    AN_SENSOR_5       = 0x10000,
    AN_SENSOR_MASK    = 0xff000
} adc_pin_func_t;

А затем просто протестируйте группу против маски с помощью оператора &:

if (a_particular_pin & AN_SENSOR_MASK)
{
    // it a sensor pin
}
else if (a_particular_pin & AN_MOTOR_MASK)
{
    // it a motor pin
}

РЕДАКТИРОВАТЬ: Как другие предложили использовать диапазон, вы могли бы создать макрос для теста, который позволит вам изменить способ проведения теста без необходимости изменения кода (всегда хорошо):

#define IS_AN_SENSOR(x) (((x) & AN_SENSOR_MASK) != 0)
#define IS_AN_MOTOR(x) (((x) & AN_MOTOR_MASK) != 0)
// etc.

а затем тест будет выглядеть следующим образом:

if (IS_AN_SENSOR(a_particular_pin))
{
    // it a sensor pin
}
else if (IS_AN_MOTOR(a_particular_pin))
{
    // it a motor pin
}
// etc

Если вам тогда понадобится перейти на использование диапазона, тогда необходимо изменить только макросы (и вам, очевидно, нужно будет определить диапазон min/max):

#define IS_AN_SENSOR(x) ((x) >= AN_SENSOR_START && (x) <= AN_SENSOR_END)
// etc

Ответ 2

Вы можете выбрать свои значения перечисления, чтобы вы могли сделать что-то вроде этого

typedef enum {
    AN_TRISTATE_0 = 0x0001,
    AN_TRISTATE_1 = 0x0002,
    AN_NOTHING = 0x0000,
    AN_MOTOR_1 = 0x0010,
    AN_MOTOR_2 = 0x0020,
    AN_MOTOR_3 = 0x0030,
    AN_SENSOR_1 = 0x0100,
    AN_SENSOR_2 = 0x0200,
    AN_SENSOR_3, /*and so on*/
    AN_SENSOR_4,
    AN_SENSOR_5
} adc_pin_func_t;

Затем вы можете сравнивать биты для проверки категорий. Например, тип двигателя является единственной категорией, которая будет иметь ненулевое значение (AN_MOTOR_2 & 0x00F0)

Ответ 3

Вы можете сделать

typedef enum {
    AN_TRISTATE_START,
    AN_TRISTATE_0 = AN_TRISTATE_START,
    AN_TRISTATE_1,
    AN_TRISTATE_END = AN_TRISTATE_1,

    AN_NOTHING,

    AN_MOTOR_START,
    AN_MOTOR_1 = AN_MOTOR_START,
    AN_MOTOR_2,
    AN_MOTOR_3,
    AN_MOTOR_END = AN_MOTOR_3,

    AN_SENSOR_START,
    AN_SENSOR_1 = AN_SENSOR_START,
    AN_SENSOR_2,
    AN_SENSOR_3,
    AN_SENSOR_4,
    AN_SENSOR_5,
    AN_SENSOR_END = AN_SENSOR_5
} adc_pin_func_t;

bool inline
is_sensor(int pin)
{
    return AN_SENSOR_START <= pin
                           && pin <= AN_SENSOR_END
}

а затем в вашем коде

if ( is_sensor(pin) )
{
    /* body */
}

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

Ответ 4

Вы можете указать значения для перечисления в показателях 2. Затем вы можете просто использовать побитовые маски И и ИЛИ. Таким образом, вы можете назначать значения, такие как 1,2,4,8,16,32... и так далее.


typedef enum {
    AN_TRISTATE_0 = 1,
    AN_TRISTATE_1 = 2,
    AN_NOTHING = 4,
    AN_MOTOR_1 = 8,
    AN_MOTOR_2 = 16,
    AN_MOTOR_3 = 32,
    AN_SENSOR_1 = 64,
    AN_SENSOR_2 = 128,
    AN_SENSOR_3 = 256,
    AN_SENSOR_4 = 512,
    AN_SENSOR_5 = 1024
} adc_pin_func_t;

Затем для проверки с типом двигателя вы можете И с (32 + 16 + 8) = 56. Таким образом, pin & 56, если ненулевой будет означать, что он имеет тип двигателя.

Ответ 5

Если вам действительно нравится иметь некоторую модульность в вашем перечислении (значения "перекодированные" "жестко закодированные" также являются допустимым методом), вы можете реализовать структуру с некоторым ароматом ООП. Дизайн становится более сложным, но использование все еще просто:

#include <stdio.h>
#include <string.h>

// Every enum will have its first value start at the value of the previous enum last member PLUS ONE 
#define OFFSET_ENUM_MOTOR  (                        sizeof(adc_pin_tristate_t) )
#define OFFSET_ENUM_SENSOR ( OFFSET_ENUM_MOTOR   +  sizeof(adc_pin_motor_t)    )


///////////////////////////////////////////////////////////////////////////////
// Enum
typedef enum {
    AN_TRISTATE_0,
    AN_TRISTATE_1,
    AN_NOTHING
} adc_pin_tristate_t;

typedef enum {
    AN_MOTOR_1 = OFFSET_ENUM_MOTOR,
    AN_MOTOR_2,
    AN_MOTOR_3
} adc_pin_motor_t;

typedef enum {
    AN_SENSOR_1 = OFFSET_ENUM_SENSOR,
    AN_SENSOR_2,
    AN_SENSOR_3,
    AN_SENSOR_4,
    AN_SENSOR_5
} adc_pin_sensor_t;
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Struct for abstraction
typedef struct adc_pin_func2_t{

    // our "enum value"
    unsigned int enum_id;

    // return is the enum is a motor one 
    int(*isMotor)(struct adc_pin_func2_t*);

} adc_pin_func2_t;
// Struct
///////////////////////////////////////////////////////////////////////////////


// Member methods : return if the enum is a motor one
int
PinFunc_isMotor(
    adc_pin_func2_t *This /* object */
    )
{
    return ( (This->enum_id>=OFFSET_ENUM_MOTOR) && (This->enum_id<OFFSET_ENUM_SENSOR) );
}

// Creation of the structure

// Initialization
static void 
PinFunc_Init(
        adc_pin_func2_t *This, /* output */
        unsigned int identifier /* old enum identifier */ 
            )
{
    // copy members
    This->enum_id = identifier;

    //copy methods (do not forget to do it !)
    This->isMotor = PinFunc_isMotor;


}

// Constructor
adc_pin_func2_t 
PinFunc_Create(
        unsigned int identifier /* old enum identifier */ 
              )
{
   adc_pin_func2_t This;
   PinFunc_Init(&This, identifier);

   return This;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
main()
{
   adc_pin_func2_t pin = PinFunc_Create(AN_NOTHING);
   printf("%d \n", pin );
   printf("%d \n", pin.isMotor(&pin) );

   adc_pin_func2_t pin2 = PinFunc_Create(AN_MOTOR_2);
   printf("%d \n", pin2 );
   printf("%d \n", pin2.isMotor(&pin2) );
}

Использование функций членов, таких как pin.isMotor(&pin), не очень элегантно (мы повторяем pin), но это недостаток C, который не является языком OOP.

Ответ 6

Если вы не хотите, чтобы вручную поддерживали неперекрывающиеся значения, вы можете почти полностью обработать все для вас автоматически. Единственное, что вам нужно выяснить, это максимальное количество бит в группе:

#define PIN_GROUP_SHIFT 4
#define GET_PIN_GROUP(x) (adc_pin_group_t)((y) >> PIN_GROUP_SHIFT)

#define PIN_GROUP_START(x) XX_GROUP_##x = ((GROUP_##x << PIN_GROUP_SHIFT) - 1),

enum {
    GROUP_TRISTATE,
    GROUP_NOTHING,
    GROUP_MOTOR,
    GROUP_SENSOR
} adc_pin_group_t;

typedef enum {
    PIN_GROUP_START(TRISTATE)
    AN_TRISTATE_0,
    AN_TRISTATE_1,

    PIN_GROUP_START(NOTHING)
    AN_NOTHING,

    PIN_GROUP_START(MOTOR)
    AN_MOTOR_1,
    AN_MOTOR_2,
    AN_MOTOR_3,

    PIN_GROUP_START(SENSOR)
    AN_SENSOR_1,
    AN_SENSOR_2,
    AN_SENSOR_3,
    AN_SENSOR_4,
    AN_SENSOR_5
} adc_pin_func_t;

Чтобы определить тип записи в перечислении, используйте GET_PIN_GROUP(x) и сравните его с любым значением перечисления adc_pin_group_t. Вы можете даже switch получить результат, если это полезно.

Однако, запись AN_NOTHING заставляет меня задаться вопросом, соответствует ли ваше перечисление соответствовать конкретным значениям для каждой записи. являются конкретными значениями, связанными с выводами, которые вы, возможно, не сможете назначить произвольно. В этом случае вам может потребоваться попробовать что-то сложное (которое я еще не тестировал):

#define GET_PIN_VALUE(x) ((x) & ((1 << PIN_GROUP_SHIFT) - 1)
#define PIN_GROUP_START(x) \
    WW_GROUP_##x, \
    XX_GROUP_##x = (GROUP_##x << PIN_GROUP_SHIFT) \
                 + GET_PIN_INDEX(WW_GROUP_##x) - 1,

Если вам нужно знать значение, которое использовало бы исходное перечисление, используйте GET_PIN_INDEX(x).