Экспериментируя с Unions и Bitfields в структурах и шаблонах - PullRequest
1 голос
/ 05 мая 2019

Чтобы лучше понять и понять, как работают битовые поля, объединения и байтовое выравнивание структур, я моделирую структуру регистра шаблона.

Требования моего регистра следующие:

  • Размер или ширина регистра по умолчанию - 8 бит или 1 байт
  • Регистры большего размера должны быть кратны 8
  • Регистры меньше или равны 64 битамили 8 байтов.

У меня есть набор структур, которые строятся друг от друга в каскадном эффекте, начиная с базовой единицы байта и заканчивая QWord.

Мои регистрыявляются шаблонными специализациями.

Вот мой код:

-main.cpp-

#include <iostream>
#include "Register.h"

int main() {
    Register r1;
    r1.value.value_ = 8;

    Register<16> r2;
    r2.value.value_ = 16;

    Register<32> r3;
    r3.value.value_ = 32;

    Register<64> r4;
    r4.value.value_ = 64;

    std::cout << static_cast<std::uint8_t>( r1.value.value_) << "\n";
    std::cout << static_cast<std::uint16_t>(r2.value.value_) << "\n";
    std::cout << static_cast<std::uint32_t>(r3.value.value_) << "\n";
    std::cout << static_cast<std::uint64_t>(r4.value.value_) << "\n";

    return EXIT_SUCCESS;
}

-Register.h-

#pragma once

#include <vector> // include for typedefs below.
typedef std::int8_t  i8;
typedef std::int16_t i16;
typedef std::int32_t i32;
typedef std::int64_t i64;

struct MyByte {
    union {
        i8 value_;
        struct {
            i8 b0 : 1;
            i8 b1 : 1;
            i8 b2 : 1;
            i8 b3 : 1;
            i8 b4 : 1;
            i8 b5 : 1;
            i8 b6 : 1;
            i8 b7 : 1;
        };
    };
};

struct MyWord {        // same as short or i16  
    union {
        i16 value_;
        union {
            MyByte byte_[2];
            struct {
                MyByte b0_;
                MyByte b1_;
            };
        };
    };
};

struct MyDWord {       // same as int or i32
    union {
        i32 value_;

        struct {
            MyWord w0_;
            MyWord w1_;
        };

        union {
            MyByte byte_[4];
            struct {
                MyByte b0_;
                MyByte b1_;
                MyByte b2_;
                MyByte b3_;
            };
        };
    };
};

struct MyQWord {     // same as long or i64
    union {
        i64 value_;
        struct {
            MyDWord d0_;
            MyDWord d1_;
        };
        struct {
            MyWord w0_;
            MyWord w1_;
            MyWord w2_;
            MyWord w3_;
        };
        union { 
            MyByte byte_[8];
            struct {
                MyByte b0_;
                MyByte b1_;
                MyByte b2_;
                MyByte b3_;
                MyByte b4_;
                MyByte b5_;
                MyByte b6_;
                MyByte b7_;
            };
        };
    };
};

template<size_t N = 8>
struct Register {
    MyByte value;
    Register() {
        static_assert(
         ((N % 8) == 0) &&
         (N >= 8) &&
         (N <= 64)

        );
    }
};

template<>
struct Register<16> {
    MyWord value;
    Register() = default;
};

template<>
struct Register<32> {
    MyDWord value;
    Register() = default;
};

template<>
struct Register<64> {
    MyQWord value;
    Register() = default;
};

Приведенный выше код компилируется, запускается и завершается с кодом 0 в Visual Studio 2017 с компилятором, настроенным на последний набросок стандарта.

Теперь, когда у вас естьвидел код, я понимаю битовые поля и союзы в некоторой степени, но чтоProjectSyndicate ru Я не использую их все так часто, что они могут немного меня поднять.Я знаю, что при их использовании, особенно в сочетании, это может привести к тому, что код не будет переносимым, особенно между различными компиляторами, операционными системами и архитектурами (endian).

Однако это всего лишь экспериментальный код, на котором можно попрактиковатьсяони как хороший переподготовка.

Проблема, с которой я столкнулся, заключается в моей работе.Все регистры более высокого порядка, кажется, работают нормально, я только протестировал получение его непосредственно через внутреннюю переменную-член value_ напрямую.Тем не менее, я получаю результаты из стандартного или самого основного регистра 8-битного размера.Со значениями, установленными как они есть в настоящее время.Я получаю это как вывод:

--

16
32
64

И если я изменю свой основной на это:

#include <iostream>
#include "Register.h"

int main() {
    Register r;
    for (i8 i = 0; i < 21; i++) {
        1.value.value_ = i;
        std::cout << static_cast<std::uint8_t>(r.value.value_) << "\n";
    }
    return EXIT_SUCCESS;
}

Я получаю этот вывод с звуковым сигналом, где-то в миксе его:

☺
☻
♥
♦
♣
♠





♂
♀

♫
☼
►
◄
↕
‼
¶

Это как-то связано с тем, как определяется std::int8_t?Он основан на типе значения char вместо типа int?Это все еще должно быть целым, хотя ... если это так, то каким образом он должен иметь дело со значениями без знака в объединениях или битовых полях и т. Д.?Что приводит к выводу символов ASCII на консоль.

1 Ответ

2 голосов
/ 05 мая 2019

Это основано на типе значения типа char вместо типа int?

char * - это целочисленный тип. Стандарт позволяет std::int8_t быть typedef до char.

std::cout << static_cast<std::uint64_t>(r.value.value_) << "\n";

FTW.

...