2016-11-11 71 views
0

我目前正试图想出一个漂亮的解决方案,基于结构生成基于整数的状态。基于结构成员函数的值生成状态

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; 

}; 

该函数需要接受一个struct的实例,并根据成员变量生成一个唯一的状态。

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

+1

所以你想'散列'这个'结构'? – NathanOliver

+0

嗯...有趣...好吧是一个散列函数..如果生成的密钥是一个整数.. –

+1

只是一个建议,而不是你的问题的答案。当'true'和'false'需要解释并且只有有限的速度值时,可以使用枚举。像“FORWARD”和“BACKWARDS”这样的名称在代码中易于阅读,人们无需查询哪个值是哪个。 – Hayt

回答

3

您可以使用位集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

+0

当我将'std :: bitset'转换回'int8'时会发生任何不匹配吗? –

+1

@CarltonBanks:应该没有不匹配。有关转换示例,请参阅[此问题](http://stackoverflow.com/questions/19583720/convert-bitset-to-int-in-c)。或者,您可以使用相同的逻辑直接在'std :: uint8_t'上进行按位运算。 –

0

不如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通过直接设置位来安全地生成散列。
  • 位域比较慢。