0

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?

6
  • 1
    So you want to hash this struct? Commented Nov 11, 2016 at 13:41
  • hmmm... Interesting... Well yeah a hash function.. If the key generated is an integer.. Commented Nov 11, 2016 at 13:43
  • 1
    Just a suggestion, not an answer to your questions. When true and false need explanation and you only have a limited number of values for speed you can use enums. Names like FORWARD and BACKWARDS are easier readable in code and people do not need to look up which value is which. Commented Nov 11, 2016 at 13:45
  • Ohh.. yeah I made it as defines. Commented Nov 11, 2016 at 13:48
  • @BaummitAugen Padding bytes are not initialized so memcpy will be error-prone Commented Nov 11, 2016 at 17:58

2 Answers 2

3

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

Sign up to request clarification or add additional context in comments.

2 Comments

Will any mismatch occur when i convert the std::bitset back to int8?
@CarltonBanks: there should be no mismatch. Refer to this question for a conversion example. Alternatively, you can use the same logic to do the bitwise arithmetic directly on an std::uint8_t.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.