将一组 classes 转换为 class 模板并避免构造函数歧义
Converting a set of classes to class templates and avoiding constructor ambiguity
我会尽量让这个问题尽可能简短,但是为了让人们了解我正在努力实现的目标以及如何解决我当前的问题,需要展示大量代码。
这是我的原始 class 声明及其所有构造函数:
Register.h - 原版
#include <bitset>
#include <cassert>
#include <cstdint>
#include <iostream>
typedef std::uint8_t u8;
typedef std::uint16_t u16;
typedef std::uint32_t u32;
typedef std::uint64_t u64;
const u16 BYTE = 0x08, WORD = 0x10, DWORD = 0x20, QWORD = 0x40;
typedef std::bitset<BYTE> Byte;
typedef std::bitset<WORD> Word;
typedef std::bitset<DWORD> DWord;
typedef std::bitset<QWORD> QWord;
template<typename T>
void getByteFrom(T val, u8 idx, u8& res) {
res = ((val >> (idx * 8) & 0xff));
}
template<typename T>
void getWordFrom(T val, u8 idx, u16& res) {
res = ((val >> (idx * 16) & 0xffff));
}
template<typename T>
void getDWordFrom(T val, u8 idx, u32& res) {
res = ((val >> (idx * 32) & 0xffffffff));
}
template<typename T>
struct Register {
T data;
Register() = default;
};
struct Reg8 : public Register<u8> {
u8 value; // must be declared before std::bitset<T>
Byte bits;
// Default 0 Initialized Constructor
Reg8() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg8(u8 val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg8(u16 val) : value{ static_cast<u8>( val ) }, bits{ value } {
this->data = value;
}
explicit Reg8(u32 val) : value{ static_cast<u8>( val ) }, bits{ value } {
//this->data = value;
}
explicit Reg8(u64 val) : value{ static_cast<u8>( val ) }, bits{ value } {
//this->data = value;
}
Reg8(u16 val, u8 idx ) {
assert( idx == 0 || idx == 1 );
getByteFrom(val, idx, this->value);
bits = value;
this->data = value;
}
Reg8(u32 val, u8 idx) {
assert(idx <= 0 && idx >= 3);
getByteFrom(val, idx, this->value);
bits = value;
this->data = value;
}
Reg8(u64 val, u8 idx) {
assert(idx <= 0 && idx >= 7);
getByteFrom(val, idx, this->value);
bits = value;
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg8(Register<T>* reg) {
this->value = static_cast<u8>( reg->data );
this->bits = value;
}
};
struct Reg16 : public Register<u16> {
u16 value; // must be declared before std::bitset<T>
Word bits;
// Default 0 Initialized Constructor
Reg16() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg16(u16& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg16( u8& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg16(u32& val) : value{ static_cast<u16>(val) }, bits{ value } {
this->data = value;
}
explicit Reg16(u64& val) : value{ static_cast<u16>(val) }, bits{ value } {
this->data = value;
}
Reg16( u32 val, u8 idx) {
assert(idx == 0 || idx == 1);
getWordFrom(val, idx, this->value);
bits = value;
this->data = value;
}
Reg16(u64 val, u8 idx) {
assert(idx <= 0 || idx <= 3);
getWordFrom(val, idx, this->value);
bits = value;
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg16(Register<T>* reg) {
this->value = static_cast<u16>(reg->data);
this->bits = value;
}
};
struct Reg32 : public Register<u32> {
u32 value; // must be declared before std::bitset<T>
DWord bits;
// Default 0 Initialized Constructor
Reg32() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg32(u32& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg32( u8& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg32(u16& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg32(u64& val) : value{ static_cast<u32>(val) }, bits{ value } {
this->data = value;
}
Reg32(u64 val, u8 idx) {
assert(idx == 0 || idx == 1);
getDWordFrom(val, idx, this->value);
bits = value;
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg32(Register<T>* reg) {
this->value = static_cast<u32>(reg->data);
this->bits = value;
}
};
struct Reg64 : public Register<u64> {
u64 value; // must be declared before std::bitset<T>
QWord bits;
// Default 0 Initialized Constructor
Reg64() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg64(u64& val) : value{ val }, bits{ value }{
this->data = value;
}
explicit Reg64( u8& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg64(u16& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg64(u32& val) : value{ val }, bits{ value } {
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg64(Register<T>* reg) {
this->value = static_cast<u64>(reg->data);
this->bits = value;
}
};
std::ostream& operator<<(std::ostream& os, const Reg8& r);
std::ostream& operator<<(std::ostream& os, const Reg16& r);
std::ostream& operator<<(std::ostream& os, const Reg32& r);
std::ostream& operator<<(std::ostream& os, const Reg64& r);
现在我将它们变成模板 classes 以减少大量代码重复。这就是我目前所拥有的:
Register.h - 更新版本
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 U>
explicit Register_t(Register_t<U>& r) {
this->currentValue = static_cast<Ty>(r->currentValue);
this->bits = r->bits;
}
};
template<typename Ty>
struct Register : public Register_t<Ty> {
Register() = default;
explicit Register(Ty val) : Register_t<Ty>( val ) {}
// Reg8
template<typename U>
Register( u16 val, u8 idx) {
assert(idx == 0 || idx == 1);
getByteFrom(val, idx, currentValue);
this->bits = this->currentValue;
}
Register(u32 val, u8 idx) {
assert(idx <= 0 && idx >= 3);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
Register(u64 val, u8 idx) {
assert(idx <= 0 && idx <= 7);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg16
Register(u32 val, u8 idx) {
assert(idx == 0 || idx == 1);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
Register(u64 val, u8 idx) {
assert(idx <= 0 && idx <= 3);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg32
Register(u64 val, u8 idx) {
assert(idx == 0 || idx == 1);
getDWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
};
using Reg8 = Register<u8>;
using Reg16 = Register<u16>;
using Reg32 = Register<u32>;
using Reg64 = Register<u64>;
现在谈到将采用 std::uintx_t
类型和索引值的构造函数。一些构造函数声明匹配,例如:
原版中Reg8
有Reg8(u32 val, u8 idx)
,Reg16
有Reg16(u32 val, u8 idx)
。如果你仔细观察 Reg8(...)
断言 idx <= 0 && idx >= 3
而 Reg16(...)
断言 idx == 0 || idx == 1
.
然而,当我尝试将这些 classes 模板化并移植到构造函数上时,它们现在变得模棱两可。我不知道如何确定使用哪个断言来区分它是 Reg8
、Reg16
、Reg32
等...
如果您想确定类型并做出适当的反应,那么为什么还要使用通用模板呢?有两个选项,确定类型和分支(通过完全不使用模板,通过专门化您的模板或可能通过使用 SFINAE)或编写 real 通用代码。前者不是很有用,因为你最终会得到比没有模板更多的boiler-plate。
后者取决于您的要求,但它可能看起来像这样:
template <typename T>
T getXFrom(T val, std::uint8_t idx) {
return val >> (idx * CHAR_BIT);
}
template <typename T>
class Register {
public:
template <typename U>
Register(U val, std::uint8_t idx) {
static_assert(std::is_integral_v<U>);
assert(idx >= 0 && idx < sizeof(U));
currentValue = getXFrom(val, idx);
}
private:
T currentValue;
};
根据要求,这里有一个关于如何专业化的例子(使用基数 class 来减少冗余):
template <typename T>
class Register {
protected:
T currentValue;
};
template <typename>
class RegisterImpl;
template <>
class RegisterImpl<uint8_t> : Register<uint8_t> {
public:
template <typename U>
RegisterImpl(U val, std::uint8_t idx) {
// uint8_t asserts...
}
};
template <>
class RegisterImpl<uint16_t> : Register<uint16_t> {
public:
template <typename U>
RegisterImpl(U val, std::uint8_t idx) {
// uint16_t asserts...
}
};
在我看来,您 class 中的所有内容都归结为使用您的类型的未签名版本的 sizeof
and numeric_limits::max
。
我已经为您写下了我认为 class 的外观的粗略草稿:
template <typename T>
struct Register {
T data;
T value;
bitset<sizeof(T) * CHAR_BIT> bits;
Register() : data(), value() {}
template <typename P>
explicit Register(const P val) : data(static_cast<T>(val)), value(data), bits(data) {}
template <typename P>
Register(const P val, const unsigned char idx) : data(static_cast<T>((val >> std::size(bits) * idx) & numeric_limits<make_unsigned_t<T>>::max())), value(data), bits(data) {
assert(idx == '[=10=]' || idx < sizeof(P) / sizeof(T));
}
template <typename P>
Register(const Register<P>& reg) : data(static_cast<T>(reg.data)), value(data), bits(data) {}
};
template <typename T>
ostream& operator<<(ostream& os, const Register<T>& r) {
os << "Reg" << size(r.bits) << '(' << r.data << ")\nhex: 0x" << uppercase << setfill('0') << setw(sizeof(T) * 2) << hex << r.data << dec << "\nbin: ";
for(std::size_t i = 0; i < size(r.bits); ++i) {
cout.put('0' + r.bits[i]);
}
return os << endl << endl;
}
template <>
ostream& operator<<<unsigned char>(ostream& os, const Register<unsigned char>& r) {
os << "Reg" << size(r.bits) << '(' << static_cast<int>(r.data) << ")\nhex: 0x" << uppercase << setfill('0') << setw(sizeof(unsigned char) * 2) << hex << static_cast<int>(r.data) << dec << "\nbin: ";
for(std::size_t i = 0; i < size(r.bits); ++i) {
cout.put('0' + r.bits[i]);
}
return os << endl << endl;
}
我想我已经解决了我的问题;我能够编译、构建和 运行 并且我得到了一些预期的结果,但是我没有做足够的单元测试来验证是否所有情况都按预期工作,但这是我到目前为止想出的。我已将我的代码分别分成两个文件以使其工作...
Register.h
#pragma once
#include <algorithm>
#include <assert.h>
#include <bitset>
#include <cstdint>
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;
// Helper Functions
template<typename T>
void getByteFrom(T val, u8 idx, u8& res) {
res = ((val >> (idx * 8) & 0xff));
}
template<typename T>
void getWordFrom(T val, u8 idx, u16& res) {
res = ((val >> (idx * 16) & 0xffff));
}
template<typename T>
void getDWordFrom(T val, u8 idx, u32& res) {
res = ((val >> (idx * 32) & 0xffffffff));
}
template<typename T>
struct Register_t {
static constexpr u16 BitCount = sizeof(T) * CHAR_BIT;
T currentValue;
T previousValue;
std::bitset<BitCount> bits;
Register_t() :
currentValue{ 0 },
previousValue{ 0 },
bits{ 0 }
{}
template<typename U>
explicit Register_t(U val) :
currentValue{ static_cast<T>(val) },
previousValue{ 0 },
bits{ currentValue }
{}
template<typename U>
explicit Register_t(Register_t<U>& r) : previousValue{ 0 }
{
this->currentValue = static_cast<T>(r.currentValue);
this->bits = currentValue;
}
};
template<typename T>
struct Register : public Register_t<T> {
Register() : Register_t<T>() {}
explicit Register(T val) : Register_t<T>( val ) {}
template<typename U>
explicit Register(Register_t<U>& r) : Register_t<T>( r ) {}
// These are the constructors with matching declarations
// that were giving me trouble with ambiguous calls
Register(u16 val, u8 idx);
Register(u32 val, u8 idx);
Register(u64 val, u8 idx);
};
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
中以防止 LNK Error 2005 - object already defined
.
Register.cpp
#include "Register.h"
#include <iostream>
#include <iomanip>
namespace vpc {
template<>
Register<u8>::Register(u16 val, u8 idx) {
assert(idx == 0 || idx == 1);
getByteFrom(val, idx, currentValue);
this->bits = this->currentValue;
}
template<>
Register<u8>::Register(u32 val, u8 idx) {
assert(idx >= 0 && idx <= 3);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
template<>
Register<u8>::Register(u64 val, u8 idx) {
assert(idx >= 0 && idx <= 7);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg16
template<>
Register<u16>::Register(u32 val, u8 idx) {
assert(idx == 0 || idx == 1);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
template<>
Register<u16>::Register(u64 val, u8 idx) {
assert(idx >= 0 && idx <= 3);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg32
template<>
Register<u32>::Register(u64 val, u8 idx) {
assert(idx == 0 || idx == 1);
getDWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
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
现在我的应用程序:
main.cpp
#include <iostream>
#include "Register.h"
int main() {
using namespace vpc;
u16 val = 1420;
Reg16 r16(val); // used to show a 16 bit register
Reg8 r8A(val, 0); // construct an 8 bit register from low byte of val
Reg8 r8B(val, 1); // construct an 8 bit register from high byte of val
std::cout << r16 << r8A << r8B;
return EXIT_SUCCESS;
}
我得到这个输出:
Reg16(1420)
hex: 0x058C
bin: 0000010110001100
Reg8(140)
hex: 0x8C
bin: 10001100
Reg8(5)
hex: 0x05
bin: 00000101
我会尽量让这个问题尽可能简短,但是为了让人们了解我正在努力实现的目标以及如何解决我当前的问题,需要展示大量代码。
这是我的原始 class 声明及其所有构造函数:
Register.h - 原版
#include <bitset>
#include <cassert>
#include <cstdint>
#include <iostream>
typedef std::uint8_t u8;
typedef std::uint16_t u16;
typedef std::uint32_t u32;
typedef std::uint64_t u64;
const u16 BYTE = 0x08, WORD = 0x10, DWORD = 0x20, QWORD = 0x40;
typedef std::bitset<BYTE> Byte;
typedef std::bitset<WORD> Word;
typedef std::bitset<DWORD> DWord;
typedef std::bitset<QWORD> QWord;
template<typename T>
void getByteFrom(T val, u8 idx, u8& res) {
res = ((val >> (idx * 8) & 0xff));
}
template<typename T>
void getWordFrom(T val, u8 idx, u16& res) {
res = ((val >> (idx * 16) & 0xffff));
}
template<typename T>
void getDWordFrom(T val, u8 idx, u32& res) {
res = ((val >> (idx * 32) & 0xffffffff));
}
template<typename T>
struct Register {
T data;
Register() = default;
};
struct Reg8 : public Register<u8> {
u8 value; // must be declared before std::bitset<T>
Byte bits;
// Default 0 Initialized Constructor
Reg8() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg8(u8 val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg8(u16 val) : value{ static_cast<u8>( val ) }, bits{ value } {
this->data = value;
}
explicit Reg8(u32 val) : value{ static_cast<u8>( val ) }, bits{ value } {
//this->data = value;
}
explicit Reg8(u64 val) : value{ static_cast<u8>( val ) }, bits{ value } {
//this->data = value;
}
Reg8(u16 val, u8 idx ) {
assert( idx == 0 || idx == 1 );
getByteFrom(val, idx, this->value);
bits = value;
this->data = value;
}
Reg8(u32 val, u8 idx) {
assert(idx <= 0 && idx >= 3);
getByteFrom(val, idx, this->value);
bits = value;
this->data = value;
}
Reg8(u64 val, u8 idx) {
assert(idx <= 0 && idx >= 7);
getByteFrom(val, idx, this->value);
bits = value;
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg8(Register<T>* reg) {
this->value = static_cast<u8>( reg->data );
this->bits = value;
}
};
struct Reg16 : public Register<u16> {
u16 value; // must be declared before std::bitset<T>
Word bits;
// Default 0 Initialized Constructor
Reg16() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg16(u16& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg16( u8& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg16(u32& val) : value{ static_cast<u16>(val) }, bits{ value } {
this->data = value;
}
explicit Reg16(u64& val) : value{ static_cast<u16>(val) }, bits{ value } {
this->data = value;
}
Reg16( u32 val, u8 idx) {
assert(idx == 0 || idx == 1);
getWordFrom(val, idx, this->value);
bits = value;
this->data = value;
}
Reg16(u64 val, u8 idx) {
assert(idx <= 0 || idx <= 3);
getWordFrom(val, idx, this->value);
bits = value;
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg16(Register<T>* reg) {
this->value = static_cast<u16>(reg->data);
this->bits = value;
}
};
struct Reg32 : public Register<u32> {
u32 value; // must be declared before std::bitset<T>
DWord bits;
// Default 0 Initialized Constructor
Reg32() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg32(u32& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg32( u8& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg32(u16& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg32(u64& val) : value{ static_cast<u32>(val) }, bits{ value } {
this->data = value;
}
Reg32(u64 val, u8 idx) {
assert(idx == 0 || idx == 1);
getDWordFrom(val, idx, this->value);
bits = value;
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg32(Register<T>* reg) {
this->value = static_cast<u32>(reg->data);
this->bits = value;
}
};
struct Reg64 : public Register<u64> {
u64 value; // must be declared before std::bitset<T>
QWord bits;
// Default 0 Initialized Constructor
Reg64() : value{ 0 }, bits{ value } { this->data = 0; }
// Constructors by Register Sized Values
explicit Reg64(u64& val) : value{ val }, bits{ value }{
this->data = value;
}
explicit Reg64( u8& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg64(u16& val) : value{ val }, bits{ value } {
this->data = value;
}
explicit Reg64(u32& val) : value{ val }, bits{ value } {
this->data = value;
}
// Constructors by Register Types
template<typename T>
explicit Reg64(Register<T>* reg) {
this->value = static_cast<u64>(reg->data);
this->bits = value;
}
};
std::ostream& operator<<(std::ostream& os, const Reg8& r);
std::ostream& operator<<(std::ostream& os, const Reg16& r);
std::ostream& operator<<(std::ostream& os, const Reg32& r);
std::ostream& operator<<(std::ostream& os, const Reg64& r);
现在我将它们变成模板 classes 以减少大量代码重复。这就是我目前所拥有的:
Register.h - 更新版本
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 U>
explicit Register_t(Register_t<U>& r) {
this->currentValue = static_cast<Ty>(r->currentValue);
this->bits = r->bits;
}
};
template<typename Ty>
struct Register : public Register_t<Ty> {
Register() = default;
explicit Register(Ty val) : Register_t<Ty>( val ) {}
// Reg8
template<typename U>
Register( u16 val, u8 idx) {
assert(idx == 0 || idx == 1);
getByteFrom(val, idx, currentValue);
this->bits = this->currentValue;
}
Register(u32 val, u8 idx) {
assert(idx <= 0 && idx >= 3);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
Register(u64 val, u8 idx) {
assert(idx <= 0 && idx <= 7);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg16
Register(u32 val, u8 idx) {
assert(idx == 0 || idx == 1);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
Register(u64 val, u8 idx) {
assert(idx <= 0 && idx <= 3);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg32
Register(u64 val, u8 idx) {
assert(idx == 0 || idx == 1);
getDWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
};
using Reg8 = Register<u8>;
using Reg16 = Register<u16>;
using Reg32 = Register<u32>;
using Reg64 = Register<u64>;
现在谈到将采用 std::uintx_t
类型和索引值的构造函数。一些构造函数声明匹配,例如:
原版中Reg8
有Reg8(u32 val, u8 idx)
,Reg16
有Reg16(u32 val, u8 idx)
。如果你仔细观察 Reg8(...)
断言 idx <= 0 && idx >= 3
而 Reg16(...)
断言 idx == 0 || idx == 1
.
然而,当我尝试将这些 classes 模板化并移植到构造函数上时,它们现在变得模棱两可。我不知道如何确定使用哪个断言来区分它是 Reg8
、Reg16
、Reg32
等...
如果您想确定类型并做出适当的反应,那么为什么还要使用通用模板呢?有两个选项,确定类型和分支(通过完全不使用模板,通过专门化您的模板或可能通过使用 SFINAE)或编写 real 通用代码。前者不是很有用,因为你最终会得到比没有模板更多的boiler-plate。
后者取决于您的要求,但它可能看起来像这样:
template <typename T>
T getXFrom(T val, std::uint8_t idx) {
return val >> (idx * CHAR_BIT);
}
template <typename T>
class Register {
public:
template <typename U>
Register(U val, std::uint8_t idx) {
static_assert(std::is_integral_v<U>);
assert(idx >= 0 && idx < sizeof(U));
currentValue = getXFrom(val, idx);
}
private:
T currentValue;
};
根据要求,这里有一个关于如何专业化的例子(使用基数 class 来减少冗余):
template <typename T>
class Register {
protected:
T currentValue;
};
template <typename>
class RegisterImpl;
template <>
class RegisterImpl<uint8_t> : Register<uint8_t> {
public:
template <typename U>
RegisterImpl(U val, std::uint8_t idx) {
// uint8_t asserts...
}
};
template <>
class RegisterImpl<uint16_t> : Register<uint16_t> {
public:
template <typename U>
RegisterImpl(U val, std::uint8_t idx) {
// uint16_t asserts...
}
};
在我看来,您 class 中的所有内容都归结为使用您的类型的未签名版本的 sizeof
and numeric_limits::max
。
我已经为您写下了我认为 class 的外观的粗略草稿:
template <typename T>
struct Register {
T data;
T value;
bitset<sizeof(T) * CHAR_BIT> bits;
Register() : data(), value() {}
template <typename P>
explicit Register(const P val) : data(static_cast<T>(val)), value(data), bits(data) {}
template <typename P>
Register(const P val, const unsigned char idx) : data(static_cast<T>((val >> std::size(bits) * idx) & numeric_limits<make_unsigned_t<T>>::max())), value(data), bits(data) {
assert(idx == '[=10=]' || idx < sizeof(P) / sizeof(T));
}
template <typename P>
Register(const Register<P>& reg) : data(static_cast<T>(reg.data)), value(data), bits(data) {}
};
template <typename T>
ostream& operator<<(ostream& os, const Register<T>& r) {
os << "Reg" << size(r.bits) << '(' << r.data << ")\nhex: 0x" << uppercase << setfill('0') << setw(sizeof(T) * 2) << hex << r.data << dec << "\nbin: ";
for(std::size_t i = 0; i < size(r.bits); ++i) {
cout.put('0' + r.bits[i]);
}
return os << endl << endl;
}
template <>
ostream& operator<<<unsigned char>(ostream& os, const Register<unsigned char>& r) {
os << "Reg" << size(r.bits) << '(' << static_cast<int>(r.data) << ")\nhex: 0x" << uppercase << setfill('0') << setw(sizeof(unsigned char) * 2) << hex << static_cast<int>(r.data) << dec << "\nbin: ";
for(std::size_t i = 0; i < size(r.bits); ++i) {
cout.put('0' + r.bits[i]);
}
return os << endl << endl;
}
我想我已经解决了我的问题;我能够编译、构建和 运行 并且我得到了一些预期的结果,但是我没有做足够的单元测试来验证是否所有情况都按预期工作,但这是我到目前为止想出的。我已将我的代码分别分成两个文件以使其工作...
Register.h
#pragma once
#include <algorithm>
#include <assert.h>
#include <bitset>
#include <cstdint>
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;
// Helper Functions
template<typename T>
void getByteFrom(T val, u8 idx, u8& res) {
res = ((val >> (idx * 8) & 0xff));
}
template<typename T>
void getWordFrom(T val, u8 idx, u16& res) {
res = ((val >> (idx * 16) & 0xffff));
}
template<typename T>
void getDWordFrom(T val, u8 idx, u32& res) {
res = ((val >> (idx * 32) & 0xffffffff));
}
template<typename T>
struct Register_t {
static constexpr u16 BitCount = sizeof(T) * CHAR_BIT;
T currentValue;
T previousValue;
std::bitset<BitCount> bits;
Register_t() :
currentValue{ 0 },
previousValue{ 0 },
bits{ 0 }
{}
template<typename U>
explicit Register_t(U val) :
currentValue{ static_cast<T>(val) },
previousValue{ 0 },
bits{ currentValue }
{}
template<typename U>
explicit Register_t(Register_t<U>& r) : previousValue{ 0 }
{
this->currentValue = static_cast<T>(r.currentValue);
this->bits = currentValue;
}
};
template<typename T>
struct Register : public Register_t<T> {
Register() : Register_t<T>() {}
explicit Register(T val) : Register_t<T>( val ) {}
template<typename U>
explicit Register(Register_t<U>& r) : Register_t<T>( r ) {}
// These are the constructors with matching declarations
// that were giving me trouble with ambiguous calls
Register(u16 val, u8 idx);
Register(u32 val, u8 idx);
Register(u64 val, u8 idx);
};
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
中以防止 LNK Error 2005 - object already defined
.
Register.cpp
#include "Register.h"
#include <iostream>
#include <iomanip>
namespace vpc {
template<>
Register<u8>::Register(u16 val, u8 idx) {
assert(idx == 0 || idx == 1);
getByteFrom(val, idx, currentValue);
this->bits = this->currentValue;
}
template<>
Register<u8>::Register(u32 val, u8 idx) {
assert(idx >= 0 && idx <= 3);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
template<>
Register<u8>::Register(u64 val, u8 idx) {
assert(idx >= 0 && idx <= 7);
getByteFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg16
template<>
Register<u16>::Register(u32 val, u8 idx) {
assert(idx == 0 || idx == 1);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
template<>
Register<u16>::Register(u64 val, u8 idx) {
assert(idx >= 0 && idx <= 3);
getWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
// Reg32
template<>
Register<u32>::Register(u64 val, u8 idx) {
assert(idx == 0 || idx == 1);
getDWordFrom(val, idx, this->currentValue);
this->bits = this->currentValue;
}
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
现在我的应用程序:
main.cpp
#include <iostream>
#include "Register.h"
int main() {
using namespace vpc;
u16 val = 1420;
Reg16 r16(val); // used to show a 16 bit register
Reg8 r8A(val, 0); // construct an 8 bit register from low byte of val
Reg8 r8B(val, 1); // construct an 8 bit register from high byte of val
std::cout << r16 << r8A << r8B;
return EXIT_SUCCESS;
}
我得到这个输出:
Reg16(1420)
hex: 0x058C
bin: 0000010110001100
Reg8(140)
hex: 0x8C
bin: 10001100
Reg8(5)
hex: 0x05
bin: 00000101