Carlton Banks
Carlton Banks

Reputation: 365

Generate state based on values of struct member function

I am currently trying to come up with a pretty solution that generates an integer based state, based on a 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;

};

The function need to take an instance of the struct in, and generate a unique state based on member variables.

What is a pretty solution that doesn't involve manually checking, or setting up all the possibilities an thereby generate the state?

Upvotes: 0

Views: 66

Answers (2)

wally
wally

Reputation: 11012

Not as good as std::bitset, but you could store the entire struct in a single byte using a bit field:

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
};

Full program:

#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;
}

Produces:

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

Some points to keep in mind:

  • Bit fields are not portable. Another implementation may:
    • Use more than one byte.
    • Reverse the bits.
    • Introduce padding to align the bits differently.
  • 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.
  • Bit fields are slower.

Upvotes: 0

Vittorio Romeo
Vittorio Romeo

Reputation: 93324

You can use a bitset (either std::bitset or an unsigned numerical type) to represent your unique state.

You will need:

  • 1 bit for busy.
  • 1 bit for powered.
  • 1 bit for direction.
  • 2 bits for speed.

In total, you will need 5 bits to represent all possible combinations.

Example:

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

Upvotes: 3

Related Questions