0
votes

As of C++17 there are the following types representing an 8-bit unit of storage:

  1. char, unsigned char
  2. int8_t, uint8_t
  3. std::byte

As I understand it the intent is that char's should now be used when the byte is actually representing a character, for example, in a string. int8_t should be used when the byte is representing a numeric value that happens to be an integer only needing 8-bits of storage. And std::byte should be used for everything else, essentially as a unit of binary data -- if you read a binary file from the file system, say.

But what about bit fields? bit flags ... whatever you want to call them. Like say you are writing a random maze generator and you want to represent the maze as an std::vector of bytes where each byte contains flags as bits specifying which walls of a cell are present. If you wanted to convert back and forth between a struct of bools and byte representation of such a cell you would have to use something like the following syntax as far as I call tell:

const std::byte kTop{0b0001};
const std::byte kRight{0b0010};
const std::byte kBottom{0b0100};
const std::byte kLeft{0b1000};

Maze::Cell::Cell(std::byte b) :
    top( static_cast<bool>(b & kTop) ),
    right( static_cast<bool>(b & kRight) ),
    bottom( static_cast<bool>( b & kBottom) ),
    left( static_cast<bool>(b & kLeft) )
{
}

std::byte Maze::Cell::toByte() const
{
    return (top ? kTop : std::byte{0}) |
        (right ? kRight : std::byte{0}) |
        (bottom ? kBottom : std::byte{0}) |
        (left ? kLeft : std::byte{0});
}

This syntax seems to a little verbose to me, having to explicitly cast to bool, having no short literal for std::byte{0}, etc.

Is there some simpler syntax for this sort of thing or is std::byte the wrong choice of type?

2
“an integer only needing 8-bits of storage“ would be std::int_least8_t or std::int_fast8_t. std::int8_t doesn’t exist if the hardware doesn’t have an 8-bit type.Pete Becker

2 Answers

2
votes

Like say you are writing a random maze generator and you want to represent the maze as an std::vector of bytes where each byte contains flags as bits specifying which walls of a cell are present.

Consider using a std::vector of std::bitset<4> for that. You can access the individual bits using normal array syntax via its operator[]:

#include <bitset>

using wallSet = std::bitset<4>;

const std::size_t kTop = 0;
const std::size_t kRight = 1;
const std::size_t kBottom = 2;
const std::size_t kLeft = 3;

struct Cell
{
    wallSet walls;

    Cell(wallSet);
    Cell(std::byte);

    bool top() const;
    void top(bool);

    bool right() const;
    void right(bool);

    bool bottom() const;
    void bottom(bool);

    bool left() const;
    void left(bool);

    std::byte toByte() const;
}

Cell::Cell(wallSet ws)
    : walls(ws)
{
}

Cell::Cell(std::byte b)
    : Cell(wallSet(static_cast<unsigned long>(b)))
{
}

bool Cell::top() const { return walls[kTop]; }
void Cell::top(bool value) { walls[kTop] = value; }

bool Cell::right() const { return walls[kRight]; }
void Cell::right(bool value) { walls[kRight] = value; }

bool Cell::bottom() const { return walls[kBottom]; }
void Cell::bottom(bool value) { walls[kBottom] = value; }

bool Cell::left() const { return walls[kLeft]; }
void Cell::left(bool value) { walls[kLeft] = value; }

std::byte Cell::toByte() const
{
    return static_cast<std::byte>(walls.to_ulong());
}
2
votes

Why not use bit fields for bits?

struct Cell
   unsigned top : 1;
   unsigned bottom : 1;
   unsigned left : 1;
   unsigned right : 1;
};