"Группировка" значений перечисления в 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)
.