首页 > 解决方案 > C、正确使用枚举、联合和函数返回

问题描述

我的目标是实现最佳代码/设计实践:

我有返回 2 个可能的枚举的函数,它们可以是(状态枚举)或(错误枚举)。

为了区分两个枚举,它们必须是彼此相距很远的值并且永远不会重叠,例如:

其他具有功能的模块期待(错误枚举)。而且我不想使从状态到错误的映射功能。所以我必须在(枚举状态)内包含(枚举错误)。

Enum eSomeState{
    enum1Value = 0,
    ...
    enum1Value99 = 99, // <<max
};

//defined in different module:
Enum eSomeError{
    enum2Value   = 100, // <<min
    ...
    enum2Value99 = 199,
};

就好像它们重叠一样,不可能知道从被调用函数返回了哪一个(没有额外的标志)。

所以,在我的代码的前面,我曾经定义两个枚举,(将错误枚举中的所有错误组合)到(状态枚举)中......就像下面

版本 1:

/* used by this module */
enum eSomeState{
    eSomeState_A    = 1,
    eSomeState_B    = 2,
    eSomeState_C    = 3,
    eSomeState_D    = 4,
    /* do not reach 100 */

   /* DO NOT GO BELOW 100 */ <------------- copied from other module's enum
   /* every time i update eSomeErrorFromOtherModule, i have to come here and update this as well */
   eSomeStateError_ErrA    = 100,
   eSomeStateError_ErrB    = 101,
   eSomeStateError_ErrC    = 102,
};

/* used by this module and other module that cares about error handling */
/* defined in different scope */
enum eSomeErrorFromOtherModule{
   eSomeError_none    = 0,
   /* DO NOT GO BELOW 100 */
   eSomeError_ErrA    = 100,
   eSomeError_ErrB    = 101,
   eSomeError_ErrC    = 102,
};


bool doSomthingA( enum eSomeState* const state){
    Assert(*state == eSomeState_A);

    //do stuff

    if (success){
        *state = eSomeState_B;
        return true;
    }else{
        *state = err;
    }
    return false;
}

来自其他模块的函数需要 (Error Enum) 类型,所以我曾经将 (State Enum) 转换为 (Error Enum)

一段时间后,我注意到每次我需要修改其中一个时,很难将(错误枚举)复制到(状态枚举)并保持它们相同。

所以我搬到了第 2 版:

    /* used by this module */
    enum eSomeState{
        eSomeState_A    = 1,
        eSomeState_B    = 2,
        eSomeState_C    = 3,
        eSomeState_D    = 4,
        /* do not reach 100 */
    }

    union uSomeState{
        enum eSomeState state;
        enum eSomeErrorFromOtherModule err;
    }

    /* functions updated to */
    bool doSomthingA( union eSomeState* const state){
        Assert(state->state == eSomeState_A);

        //do stuff

        if (success){
            state->state = eSomeState_B;
            return true;
        }else{
            state->err = err;
        }
        return false;
    }

版本 2 [我没有测试它,因为我几乎没有使用 union,但我希望它能够工作] 减少了我将枚举从另一个模块复制到这个模块的工作.. 但我仍然必须确保(状态枚举)不重叠(错误枚举)..

标签: coopenumssoftware-design

解决方案


我喜欢将枚举视为一组(列表)常量。如果我必须存储一个状态,那么我会引入一个新类型,例如bitset_tstate_t

例子:

typedef int state_t;

enum someStates {};
enum someErrorStates {};

bool doSomething(state_t state) {}

因为有时有必要将两个状态“组合”在一起(例如,通过运算符“|”),这不在任一枚举的集合中。

只是一个意见。

补充:

基本上,枚举是一个 int(无符号?),您可以在一组中存储最多 INT_MAX 值。但是,如果您想要一个“位集”,那么您将被限制为 32 个(如果 int 是 32 位)不同的值。如果你想拥有更多,那么你必须实现一个位集,一个单词数组。

例子:

#define word_size sizeof(unsigned int)
#define word_width (word_size * CHAR_BIT) // defined in limits.h
#define word_index(Pos) ((Pos) / word_width)
#define bit_index(Pos) ((Pos) % word_width)

unsigned int bitset[N];

bool isSet(unsigned int bs[N], unsigned int pos)
{
    return ((bs[word_index(pos)] >> bit_index(pos)) & 1);
}

void setBit(unsigned int bs[N], unsigned int pos)
{
    bs[word_index(pos)] |= (1 << bit_index(pos));
}

void clearBit(unsigned int bs[N], unsigned int pos)
{
    bs[word_index(pos)] &= ~(1 << bit_index(pos));
}

void toggleBit(unsigned int bs[N], unsigned int pos)
{
    bs[word_index(pos)] ^= (1 << bit_index(pos));
}

然后你可以像这样定义你的枚举:

enum someState {
    state1 = 1,
    state2 = 2
    //...
};

setBit(bitset, state2);
isSet(bitset, state2);
// ... and so on

基本上,枚举常量将描述位集中的(位)位置。


推荐阅读