Различные битовые отображения с анонимными структурами

Я хотел бы реализовать различные отображения для некоторого количества битов. Я пришел с этим решением, которое работает достаточно хорошо для меня:

union myBits{
 // constructors

 struct{ // Mapping 1
   uint16_t a : 8, b : 8;
 };
 struct{ // Mapping 2
   uint16_t c : 10, d : 6;
 };
};

Затем я могу получить доступ к различным битовым сопоставлениям просто

myBits mb(/**/);
mb.c = 10;
mb.a = 2;

Я думаю, что эта функциональность очень элегантна, хотя она не считается стандартной C++. g++ и clang выдают предупреждения (сообщая, что в стандарте анонимные структуры недопустимы), Visual Studio компилирует код без каких-либо предупреждений.

Мой вопрос: существует ли современный C++1x способ написания того же кода с той же функциональностью, который не включает в себя только именование структур?

1 ответ

Решение

Из-за ограничений, которые имеет C++ по сравнению с C, я не вижу эффективного, легального и элегантного способа сделать это в стандарте C++. Вы можете выбрать только два из трех атрибутов:)

Эффективно и законно, но не элегантно

struct myBits
{
    void set_bits(size_t start_bit, size_t end_bit, uint16_t value)
    {
        size_t len = end_bit - start_bit + 1;
        uint16_t mask = ((1 << len) - 1) << (start_bit - 1);
        word = (word & ~mask) | ((value << (start_bit - 1)) & mask);
    }
    uint16_t get_bits(size_t start_bit, size_t end_bit)
    {
        size_t len = end_bit - start_bit + 1;
        return (word >> (start_bit - 1)) & ((1 << len) - 1);
    }
    uint16_t word;
};

Чтобы использовать его как в вашем сценарии:

myBits mb;
mb.set_bits(1, 10, 10);
mb.set_bits(1, 8, 2);

Это, возможно, не так эффективно, как код в вопросе, это действительно зависит от того, как компилятор генерирует код для доступа к битовым полям по сравнению с кодом выше. Преимущество этого по сравнению с первоначальным решением заключается в том, что вы можете получить доступ к любому подмножеству битов, не определяя для него новые структуры и битовые поля.

Легально и элегантно, но не эффективно

template<size_t start_bit, size_t end_bit>
struct bit_field
{
    bit_field(uint16_t& w) : word(w) {}

    operator uint16_t() const
    {
        return (word >> (start_bit - 1)) & ((1 << len) - 1);
    }

    bit_field& operator=(uint16_t value)
    {
        uint16_t mask = ((1 << len) - 1) << (start_bit - 1);
        word = (word & ~mask) | ((value << (start_bit - 1)) & mask);
        return *this;
    }

    const size_t len = end_bit - start_bit + 1;
    uint16_t &word;
};

struct Word
{
    Word(uint16_t w) : word(w), a(word), b(word), c(word), d(word) {}

    uint16_t word;
    bit_field<1, 8> a;
    bit_field<9, 16> b;
    bit_field<1, 10> c;
    bit_field<11, 16> d;
};

Использование в этом случае так же элегантно, как в вашем примере:

Word w(0);
w.c = 10;
w.a = 2;

Проблема этого подхода в том, что он действительно неэффективен в пространстве. Вы должны иметь структуру, объявленную для каждого битового поля, по сравнению с первоначальным подходом просто хранения битов.

Эффективно и элегантно, но не законно

Это будет в основном так, как вы описали в вопросе.

Другие вопросы по тегам