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

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

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;

};

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

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


person Carlton Banks    schedule 11.11.2016    source источник
comment
Итак, вы хотите hash этого struct?   -  person NathanOliver    schedule 11.11.2016
comment
хммм... Интересно... Ну да, хэш-функция.. Если сгенерированный ключ является целым числом..   -  person Carlton Banks    schedule 11.11.2016
comment
Просто предложение, а не ответ на ваши вопросы. Когда true и false нуждаются в объяснении, и у вас есть только ограниченное количество значений для скорости, вы можете использовать перечисления. Такие имена, как FORWARD и BACKWARDS, легче читаются в коде, и людям не нужно искать, какое значение есть какое.   -  person Hayt    schedule 11.11.2016
comment
Ооо.. да я сделал это как определяет.   -  person Carlton Banks    schedule 11.11.2016
comment
@BaummitAugen Байты заполнения не инициализированы, поэтому memcpy будет подвержено ошибкам   -  person SirGuy    schedule 11.11.2016
comment
@GuyGreer Верно, спасибо.   -  person Baum mit Augen    schedule 11.11.2016


Ответы (2)


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

person Vittorio Romeo    schedule 11.11.2016
comment
Возникнет ли несоответствие, когда я конвертирую std::bitset обратно в int8? - person Carlton Banks; 11.11.2016
comment
@CarltonBanks: не должно быть несоответствия. См. этот вопрос для примера преобразования. В качестве альтернативы вы можете использовать ту же логику для выполнения побитовых арифметических операций непосредственно над std::uint8_t. - person Vittorio Romeo; 11.11.2016

Не так хорошо, как 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 are not portable. Another implementation may:
    • Use more than one byte.
    • Переверните биты.
    • Введите заполнение, чтобы выровнять биты по-другому.
  • The program above is breaking the strict aliasing rule.
    • So it would probably be best to produce the hash with std::bitset by setting the bits directly in a safe way.
  • Битовые поля работают медленнее.
person wally    schedule 11.11.2016