A Person
A Person

Reputation: 1350

"Grouping" enum values in C

If I had some enums like

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;

and

adc_pin_func_t a_particular_pin = ...

, would it be possible it check if the pin is part of a particular group, e.g pin is part of AN_MOTOR or part of AN_SENSOR, instead of having to check against each item in each possible group.

Or are there more efficient ways of doing this, other than using enums?

Thanks in advance

Upvotes: 15

Views: 1985

Answers (6)

sh1
sh1

Reputation: 4751

If you don't want to have to manually maintain non-overlapping values, then you can very nearly get it all handled for you automatically. The only thing you'll have to figure out is the maximum number of bits in a group:

#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;

To determine the type of an entry in the enum, use GET_PIN_GROUP(x), and compare it to whichever value of the adc_pin_group_t enum. You can even switch on the result, if that's helpful.

However, that AN_NOTHING entry makes me wonder if your enum is meant to line up with specific values for each entry. are specific values associated with the pins, which you may not be able to assign arbitrarily. In that case you might need to try something complicated (which I haven't tested):

#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,

Where you need to know the value that your original enum would have used, use GET_PIN_INDEX(x).

Upvotes: 0

Michael Pankov
Michael Pankov

Reputation: 3701

You can do a

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
}

and then in your code

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

This way you don't have to care about masking particular values. May be useful if groups contain a lot of values.

Upvotes: 3

trojanfoe
trojanfoe

Reputation: 122401

You could create masks for each of the groups:

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;

And then simply test a group against the mask using the & operator:

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

EDIT: As others have suggested using a range, then you could probably create a macro for the test, which would allow you to change how the test is performed without the need to change the code (always a good thing):

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

and then the test becomes:

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

If you then needed to change to using a range then only the macros need to change (and you'd obviously need to define the range min/max):

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

Upvotes: 22

lucasg
lucasg

Reputation: 11012

If you really like to have some modularity in your enum (the "hard coded" enum values are also a valid method), you can implement a struct with some OOP flavour. The design become more complicated, but the usage is still simple :

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

// Every enum will have its first value start at the value of the previous enum's 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) );
}

The usage of members functions like pin.isMotor(&pin) isn't very elegant (we repeat pin), but it is a shortcoming of C, which is not an OOP language.

Upvotes: 0

Atmaram Shetye
Atmaram Shetye

Reputation: 1013

You can give values to the enum in exponents of 2. Then you can simply use bitwise AND and OR masks. So you can assign values like 1,2,4,8,16,32... so on.


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;

Then for checking with motor type, you can AND with (32+16+8) = 56. So pin & 56, if non zero will mean it is of motor type.

Upvotes: 1

Bathsheba
Bathsheba

Reputation: 234785

You are free to choose your enum values, so you could do something like this

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;

Then you can compare bits to check categories. For example, a motor type is the only category that will have non-zero (AN_MOTOR_2 & 0x00F0)

Upvotes: 4

Related Questions