Генерация состояния на основе значений функции-члена struct

В настоящее время я пытаюсь найти красивое решение, которое генерирует целочисленное состояние на основе структуры.

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;

};

Функция должна принимать экземпляр структуры и генерировать уникальное состояние на основе переменных-членов.

Что такое симпатичное решение, которое не включает в себя ручную проверку или настройку всех возможностей для генерации состояния?

0

Решение

Вы можете использовать битсет (или 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

3

Другие решения

Не так хорошо, как 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

Некоторые моменты, которые следует иметь в виду:

  • Битовые поля не переносимы Другая реализация может:
    • Используйте более одного байта.
    • Поменять местами биты
    • Введите отступы для выравнивания битов по-разному.
  • Программа выше ломает строгое правило алиасинга.
    • Так что, вероятно, было бы лучше создать хеш с std::bitset установив биты прямо безопасным способом.
  • Битовые поля медленнее.
0

По вопросам рекламы [email protected]