Автоматизация аргумента размера шаблона класса на основе аргумента его типа - PullRequest
0 голосов
/ 13 мая 2019

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

#include <bitset>
#include <cstdint>

namespace xxx {

#define BYTE  8
#define WORD  16
#define DWORD 32
#define QWORD 64

    typedef std::uint8_t   u8;
    typedef std::uint16_t u16;
    typedef std::uint32_t u32;
    typedef std::uint64_t u64;

    template<typename Type, u16 BitCount>
    struct Register {
    };
}

Я не знаю, могу ли я использовать частичную специализацию или полную специализацию, и я не знаю, как поступитьделая это.Я хотел бы сделать следующее:

  • если Type == u8, то BitCount автоматически == BYTE или 8
  • если Type == u16, то BitCount автоматически == WORD или 16
  • если Type == u32, то BitCount автоматически == DWORD или32
  • если Type == u64, то BitCount автоматически == QWORD или 64

Причина заключается в том, чтокогда дело доходит до класса выше с его членами, которых я еще не показал, но покажу здесь, это то, что один из его членов является std::bitset

template<typename Type, u16 BitCount>
struct Register {
   Type value;
   std::bitset<BitCount> 
};

Да, я знаю, что могу создавать их какthis:

void someFunc() {
    Register<u8, 8> r8;
    Register<u16, 16> r16;
}

Но я бы хотел иметь возможность специализировать их, чтобы вам не приходилось передавать какие-либо типы аргументов, я надеялся, что они могут быть выведены из типов параметров, которыепередается в или, если просто передать тип, то часть размера является автоматической.Это было бы предпочтительным

void someFunc() {
    Register<u8> r8;
} 

// the class would automatically use `8` for its `std::bitset<8>` member.

Всегда будет однозначное соответствие его базового типа и размера в битах.

Этот тип создания недействителен:

Register<u32, 64> reg; // invalid type u32 can only be 32...

Есть ли какой-нибудь известный способ сделать это через специализацию, наследование, полиморфизм и т. Д .?

Ответы [ 3 ]

5 голосов
/ 13 мая 2019

Вы можете просто использовать:

using u8Register = Register<u8, BYTE>;
using u16Register = Register<u16, WORD>;
using u32Register = Register<u32, DWORD>;
using u64Register = Register<u64, QWORD>;

Или, если эти типы будут один на один со своим байтом, вы можете использовать sizeof:

#include <limits.h>
template<typename Type, u16 BitCount = sizeof(Type) * CHAR_BIT>
struct Register {
   Type value;
   std::bitset<BitCount> 
};

Или вы можете унаследовать от Register, чтобы специализироваться.

4 голосов
/ 13 мая 2019

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

template <typename Type>
struct Register {
    static constexpr auto Bits = sizeof(Type) * CHAR_BIT;
    // ...
};

Или, если хотите несколько обобщить эту логику:

template <typename> struct BitCount;
template <> struct BitCount<uint8_t>  : integral_constant<size_t, 8> {};
template <> struct BitCount<uint16_t> : integral_constant<size_t, 16> {};
// ...

template <typename Type>
struct Register {
    static constexpr auto Bits = BitCount<T>::value;
    // ...
};
0 голосов
/ 13 мая 2019

решить мою проблему; они оба дали похожие ответы на этот вопрос, но я думаю, что Барри более выразителен, поэтому мне придется отдать ему должное за его ответ.

Теперь, что касается Сомбреро, он помог мне с using Reg# = Register<T>, поэтому мне не нужно вызывать эти шаблоны, передавая их аргументы типа.

И это в основном сводилось к этому:

static constexpr auto Bits = sizeof(Type) * CHAR_BIT;

Я хочу поблагодарить вас за тех, кто предоставил мне эту помощь.


Используя отзывы пользователей: Barry и SombreroChicken Мне удалось сделать это:

main.cpp

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

int main() {
using namespace vpc;

    u8  valA = 8;
    u16 valB = 16;
    u32 valC = 32;
    u64 valD = 64;

    Reg8 r8A(valA);
    Reg8 r8B(valB);
    Reg8 r8C(valC);
    Reg8 r8D(valD);

    Reg16 r16A(valA);
    Reg16 r16B(valB);
    Reg16 r16C(valC);
    Reg16 r16D(valD);

    Reg32 r32A(valA);
    Reg32 r32B(valB);
    Reg32 r32C(valC);
    Reg32 r32D(valD);

    Reg64 r64A(valA);
    Reg64 r64B(valB);
    Reg64 r64C(valC);
    Reg64 r64D(valD);

    std::cout << r8A << r8B << r8C << r8D;
    std::cout << r16A << r16B << r16C << r16D;
    std::cout << r32A << r32B << r32C << r32D;
    std::cout << r64A << r64B << r64C << r64D;

    return EXIT_SUCCESS;
}

выход

Reg8(8)
hex: 0x08
bin: 00001000

Reg8(16)
hex: 0x10
bin: 00010000

Reg8(32)
hex: 0x20
bin: 00100000

Reg8(64)
hex: 0x40
bin: 01000000

Reg16(8)
hex: 0x0008
bin: 0000000000001000

Reg16(16)
hex: 0x0010
bin: 0000000000010000

Reg16(32)
hex: 0x0020
bin: 0000000000100000

Reg16(64)
hex: 0x0040
bin: 0000000001000000

Reg32(8)
hex: 0x00000008
bin: 00000000000000000000000000001000

Reg32(16)
hex: 0x00000010
bin: 00000000000000000000000000010000

Reg32(32)
hex: 0x00000020
bin: 00000000000000000000000000100000

Reg32(64)
hex: 0x00000040
bin: 00000000000000000000000001000000

Reg64(8)
hex: 0x0000000000000008
bin: 0000000000000000000000000000000000000000000000000000000000001000

Reg64(16)
hex: 0x0000000000000010
bin: 0000000000000000000000000000000000000000000000000000000000010000

Reg64(32)
hex: 0x0000000000000020
bin: 0000000000000000000000000000000000000000000000000000000000100000

Reg64(64)
hex: 0x0000000000000040
bin: 0000000000000000000000000000000000000000000000000000000001000000

Register.h

#pragma once

#include <algorithm>
#include <bitset>

#include <string>
#include <vector> // include for typedefs below.

namespace vpc {
    typedef std::int8_t  i8;
    typedef std::int16_t i16;
    typedef std::int32_t i32;
    typedef std::int64_t i64;

    typedef std::uint8_t u8;
    typedef std::uint16_t u16;
    typedef std::uint32_t u32;
    typedef std::uint64_t u64;

    const u16 BYTE = 0x08;
    const u16 WORD = 0x10;
    const u16 DWORD = 0x20;
    const u16 QWORD = 0x40;

    typedef std::bitset<BYTE>  Byte;
    typedef std::bitset<WORD>  Word;
    typedef std::bitset<DWORD> DWord;
    typedef std::bitset<QWORD> QWord;

    template<typename Ty>
    struct Register_t {
        static constexpr u16 BitCount = sizeof(Ty) * CHAR_BIT;
        Ty currentValue;
        Ty previousValue;
        std::bitset<BitCount> bits;

        Register_t() : currentValue{ 0 }, previousValue{ 0 }, bits{ 0 }{}

        template<typename U>
        explicit Register_t(U val) : currentValue{ static_cast<Ty>(val) }, previousValue{ 0 }, bits{ currentValue } {}
    };

    template<typename Ty>
    struct Register : public Register_t<Ty> {
        Register() = default;
        explicit Register(Ty val) : Register_t<Ty>( val ) {}
    };

    using Reg8  = Register<u8>;
    using Reg16 = Register<u16>;
    using Reg32 = Register<u32>;
    using Reg64 = Register<u64>;

    std::ostream& operator<<(std::ostream& os, const Reg8&  reg);
    std::ostream& operator<<(std::ostream& os, const Reg16& reg);
    std::ostream& operator<<(std::ostream& os, const Reg32& reg);
    std::ostream& operator<<(std::ostream& os, const Reg64& reg);

} // namespace vpc

Register.cpp

#include "Register.h"

#include <iostream>
#include <iomanip>

namespace vpc {

    std::ostream& operator<<(std::ostream& os, const Reg8& r) {
        os << "Reg8(" << +r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(2) << std::hex
            << +r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }
    std::ostream& operator<<(std::ostream& os, const Reg16& r) {
        os << "Reg16(" << r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(4) << std::hex
            << r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }
    std::ostream& operator<<(std::ostream& os, const Reg32& r) {
        os << "Reg32(" << r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(8) << std::hex
            << r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }
    std::ostream& operator<<(std::ostream& os, const Reg64& r) {
        os << "Reg64(" << r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(16) << std::hex
            << r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }

} // namespace vpc

И это то, что я ищу.

...