根据结构成员函数的值生成状态

Generate state based on values of struct member function

我目前正在尝试提出一个漂亮的解决方案,它基于一个结构生成一个基于整数的状态。

struct status{
public:
    status();
    /**
     * @brief busy
     * true =  Currently handling a message in manual mode
     * false = Not handling 
     */
    bool busy;
    /**
     * @brief speed
     * Variable containing the current speed 
     * Speed possibilities [FAST;MEDIUM;SLOW]
     */
    int speed;
    /**
     * @brief powered
     * A boolean determining whether it is powered or not.
     * true = ON
     * false = OFF
     */
    bool powered;
    /**
     * @brief direction
     * A boolean determing the direction 
     * true = FORWARD
     * false = BACKWARDS
     */
    bool direction;

};

该函数需要获取结构体的一个实例,并根据成员变量生成一个唯一的状态。

什么是不涉及手动检查或设置所有可能性从而生成状态的漂亮解决方案?

您可以使用位集 std::bitset 或无符号数字类型) 来表示您的独特状态。

您将需要:

  • 1 位用于 busy
  • 1 位用于 powered
  • 1 位用于 direction
  • 2 位用于 speed

总共需要 5 位来表示所有可能的组合。

示例:

auto status::hash() const noexcept
{
    std::bitset<5> b;
    b |= speed; // assumes only the last two bits in `speed` are used
    b.set(4, busy);
    b.set(3, powered);
    b.set(2, direction);
    return b;
}

wandbox example

不如 std::bitset,但您可以使用位字段将整个结构存储在单个字节中:

struct status {
public:
    status(Busy busy, Speed speed, Powered powered, Direction direction)
        : busy{busy}, speed{speed}, powered{powered}, direction{direction}, pad{0} {};

    Busy busy : 1;
    Speed speed : 2;
    Powered powered : 1;
    Direction direction : 1;
    unsigned char pad : 3; // pad to 8 bits
};

完整节目:

#include <bitset>
#include <iostream>

#define ENUM_MACRO3(name, v1, v2, v3)\
    enum class name : unsigned char { v1, v2, v3};\
    std::ostream& operator<<(std::ostream& os, name var) {\
        switch (var){\
            case name::v1: return os << #v1;\
            case name::v2: return os << #v2;\
            case name::v3: return os << #v3;\
        }\
        return os;\
    }

#define ENUM_MACRO2(name, v1, v2)\
    enum class name : unsigned char { v1, v2};\
    std::ostream& operator<<(std::ostream& os, name var) {\
        switch (var){\
            case name::v1: return os << #v1;\
            case name::v2: return os << #v2;\
        }\
        return os;\
    }

ENUM_MACRO3(Speed, fast, medium, slow)
ENUM_MACRO2(Busy, handling, not_handling)
ENUM_MACRO2(Powered, on, off)
ENUM_MACRO2(Direction, forwards, backwards)

struct status {
public:
    status(Busy busy, Speed speed, Powered powered, Direction direction)
        : busy{busy}, speed{speed}, powered{powered}, direction{direction}, pad{0} {};

    Busy busy : 1;
    Speed speed : 2;
    Powered powered : 1;
    Direction direction : 1;
    unsigned char pad : 3; // pad to 8 bits
};

int main()
{
    status s{Busy::not_handling,Speed::slow,Powered::off,Direction::backwards};

    std::cout << "Data has size of " << sizeof(status) << '\n';
    std::cout << "busy :" << s.busy << '\n';
    std::cout << "speed :" << s.speed << '\n';
    std::cout << "powered :" << s.powered << '\n';
    std::cout << "direction :" << s.direction << '\n';

    unsigned char val = reinterpret_cast<unsigned char&>(s);
    unsigned int num{val};
    std::cout << num << '\n';
    std::bitset<8> bs{num};
    std::cout << bs << '\n';
    return 0;
}

生产:

Data has size of 1
busy :not_handling
speed :slow
powered :off
direction :backwards
29
00011101

需要牢记的几点:

  • Bit fields 不可移植。另一个实现可能:
    • 使用多于一个字节。
    • 反转位。
    • 引入填充以不同方式对齐位。
  • 上面的程序打破了strict aliasing rule
    • 因此,最好通过以安全的方式直接设置位来使用 std::bitset 生成散列。
  • 位域较慢。