2016-11-11 79 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通過直接設置位來安全地生成散列。
  • 位域比較慢。