Originally I had this class template declaration:
Register.h -- Original
#pragma once
#include <assert.h>
#include <bitset>
#include <cstdint>
#include <limits>
#include <type_traits>
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
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 == '\0' || idx < sizeof(P) / sizeof(T));
}
template <typename P>
Register(const Register<P>& reg) : data(static_cast<T>(reg.data)), value(data), bits(data) {}
};
using Reg8 = Register<u8>;
using Reg16 = Register<u16>;
using Reg32 = Register<u32>;
using Reg64 = Register<u64>;
And I was able to construct these as such:
void someFunc() {
// by const value
Reg64 r64{ 0x0123456789ABCDEF };
Reg32 r32{ 0x89ABCDEF };
Reg16 r16{ 0xCDEF };
Reg8 r8{ 0xEf };
// unsigned type
u8 byte = 8;
u16 word = 16;
u32 dword = 32;
u64 qword = 64;
Reg8 r8a{byte};
Reg16 r16a{word};
Reg32 r32a{dword};
Reg64 r64a{qword};
// Or from any other Register<T> type.
}
Now I converted my class template's constructors to SFINAE and they now look like this:
#pragma once
#include <assert.h>
#include <bitset>
#include <cstdint>
#include <limits>
#include <type_traits>
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
template<typename T>
struct Register {
T data;
T value;
std::bitset<sizeof(T)* CHAR_BIT> bits;
Register() : data{ 0 }, value{ 0 }, bits{ 0 } {}
template<typename P, std::enable_if_t<(sizeof(P) > sizeof(T))>* = nullptr>
Register(const P val, const u8 idx) :
data{ static_cast<T>((val >> std::size(bits) * idx) &
std::numeric_limits<std::make_unsigned_t<T>>::max()) },
value{ data },
bits{ data }
{
constexpr u16 sizeT = sizeof(T);
constexpr u16 sizeP = sizeof(P);
assert((idx >= 0) && (idx <= ((sizeP / sizeT) - 1)) );
}
template<typename P, std::enable_if_t<(sizeof(P) < sizeof(T))>* = nullptr>
Register(const P val, const u8 idx) :
data{ /*static_cast<T>((val >> std::size(bits) * idx) &
std::numeric_limits<std::make_unsigned_t<T>>::max()) },*/
static_cast<T>(val)
},
value{ data },
bits{ data }
{
constexpr u16 sizeT = sizeof(T);
constexpr u16 sizeP = sizeof(P);
assert((idx >= 0) && (idx <= ((sizeT / sizeP) - 1)) );
}
template<typename P, std::enable_if_t<(sizeof(P) == sizeof(T))>* = nullptr>
Register(const P val, const u8 idx = 0) :
data{ static_cast<T>( val ) }, value{ data }, bits{ data }
{}
template<typename P>
Register(const Register<P>& reg, const u8 idx = 0) : Register(reg.data, idx) {}
};
using Reg8 = Register<u8>;
using Reg16 = Register<u16>;
using Reg32 = Register<u32>;
using Reg64 = Register<u64>;
And now when I try to construct them as I did before, I am not getting compiler errors: NOTE*, these compiler errors are not from the dummy function someFunc
above, this is the full list of errors regenerated from my testing
functions that previously compile and ran before without error. I can post those functions below these error if needed; just make a request and I'll add them.
1>------ Build started: Project: TestRegister, Configuration: Debug x64 ------
1>main.cpp
1>c:\***\main.cpp(54): error C2440: 'initializing': cannot convert from 'initializer list' to 'vpc::Register<vpc::u16>'
1>c:\***\main.cpp(54): note: No constructor could take the source type, or constructor overload resolution was ambiguous
1>c:\***\main.cpp(55): error C2440: 'initializing': cannot convert from 'initializer list' to 'vpc::Register<vpc::u8>'
1>c:\***\main.cpp(55): note: No constructor could take the source type, or constructor overload resolution was ambiguous
1>c:\***\main.cpp(195): error C2665: 'vpc::Register<vpc::u8>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u8>::Register(vpc::Register<vpc::u8> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u8>::Register(const vpc::Register<vpc::u8> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u8>::Register(void)'
1>c:\***\main.cpp(195): note: while trying to match the argument list '(u16)'
1>c:\***\main.cpp(196): error C2665: 'vpc::Register<vpc::u8>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u8>::Register(vpc::Register<vpc::u8> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u8>::Register(const vpc::Register<vpc::u8> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u8>::Register(void)'
1>c:\***\main.cpp(196): note: while trying to match the argument list '(u32)'
1>c:\***\main.cpp(197): error C2665: 'vpc::Register<vpc::u8>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u8>::Register(vpc::Register<vpc::u8> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u8>::Register(const vpc::Register<vpc::u8> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u8>::Register(void)'
1>c:\***\main.cpp(197): note: while trying to match the argument list '(u64)'
1>c:\***\main.cpp(238): error C2665: 'vpc::Register<vpc::u16>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u16>::Register(vpc::Register<vpc::u16> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u16>::Register(const vpc::Register<vpc::u16> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u16>::Register(void)'
1>c:\***\main.cpp(238): note: while trying to match the argument list '(u8)'
1>c:\***\main.cpp(239): error C2665: 'vpc::Register<vpc::u16>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u16>::Register(vpc::Register<vpc::u16> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u16>::Register(const vpc::Register<vpc::u16> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u16>::Register(void)'
1>c:\***\main.cpp(239): note: while trying to match the argument list '(u8)'
1>c:\***\main.cpp(241): error C2665: 'vpc::Register<vpc::u16>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u16>::Register(vpc::Register<vpc::u16> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u16>::Register(const vpc::Register<vpc::u16> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u16>::Register(void)'
1>c:\***\main.cpp(241): note: while trying to match the argument list '(u32)'
1>c:\***\main.cpp(242): error C2665: 'vpc::Register<vpc::u16>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u16>::Register(vpc::Register<vpc::u16> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u16>::Register(const vpc::Register<vpc::u16> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u16>::Register(void)'
1>c:\***\main.cpp(242): note: while trying to match the argument list '(u64)'
1>c:\***\main.cpp(267): error C2665: 'vpc::Register<vpc::u32>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u32>::Register(vpc::Register<vpc::u32> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u32>::Register(const vpc::Register<vpc::u32> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u32>::Register(void)'
1>c:\***\main.cpp(267): note: while trying to match the argument list '(u8)'
1>c:\***\main.cpp(268): error C2665: 'vpc::Register<vpc::u32>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u32>::Register(vpc::Register<vpc::u32> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u32>::Register(const vpc::Register<vpc::u32> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u32>::Register(void)'
1>c:\***\main.cpp(268): note: while trying to match the argument list '(u8)'
1>c:\***\main.cpp(269): error C2665: 'vpc::Register<vpc::u32>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u32>::Register(vpc::Register<vpc::u32> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u32>::Register(const vpc::Register<vpc::u32> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u32>::Register(void)'
1>c:\***\main.cpp(269): note: while trying to match the argument list '(u16)'
1>c:\***\main.cpp(271): error C2665: 'vpc::Register<vpc::u32>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u32>::Register(vpc::Register<vpc::u32> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u32>::Register(const vpc::Register<vpc::u32> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u32>::Register(void)'
1>c:\***\main.cpp(271): note: while trying to match the argument list '(u64)'
1>c:\***\main.cpp(285): error C2665: 'vpc::Register<vpc::u64>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u64>::Register(vpc::Register<vpc::u64> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u64>::Register(const vpc::Register<vpc::u64> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u64>::Register(void)'
1>c:\***\main.cpp(285): note: while trying to match the argument list '(u8)'
1>c:\***\main.cpp(286): error C2665: 'vpc::Register<vpc::u64>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u64>::Register(vpc::Register<vpc::u64> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u64>::Register(const vpc::Register<vpc::u64> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u64>::Register(void)'
1>c:\***\main.cpp(286): note: while trying to match the argument list '(u8)'
1>c:\***\main.cpp(287): error C2665: 'vpc::Register<vpc::u64>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u64>::Register(vpc::Register<vpc::u64> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u64>::Register(const vpc::Register<vpc::u64> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u64>::Register(void)'
1>c:\***\main.cpp(287): note: while trying to match the argument list '(u16)'
1>c:\***\main.cpp(288): error C2665: 'vpc::Register<vpc::u64>::Register': none of the 3 overloads could convert all the argument types
1>c:\***\register.h(73): note: could be 'vpc::Register<vpc::u64>::Register(vpc::Register<vpc::u64> &&)'
1>c:\***\register.h(73): note: or 'vpc::Register<vpc::u64>::Register(const vpc::Register<vpc::u64> &)'
1>c:\***\register.h(34): note: or 'vpc::Register<vpc::u64>::Register(void)'
1>c:\users\skilz99\source\repos\testregister\testregister\main.cpp(288): note: while trying to match the argument list '(u32)'
1>Done building project "TestRegister.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
So now I'm getting a couple of repeating type errors, first it can not convert from initializer list and then I'm getting ambiguous constructors calls...
You dropped the default arguments from two of your constructor templates.
template<typename P, std::enable_if_t<(sizeof(P) > sizeof(T))>* = nullptr>
Register(const P val, const u8 idx) : // ...
template<typename P, std::enable_if_t<(sizeof(P) < sizeof(T))>* = nullptr>
Register(const P val, const u8 idx) : // ...
should be:
template<typename P, std::enable_if_t<(sizeof(P) > sizeof(T))>* = nullptr>
Register(const P val, const u8 idx = 0) : // ...
template<typename P, std::enable_if_t<(sizeof(P) < sizeof(T))>* = nullptr>
Register(const P val, const u8 idx = 0) : // ...
So the compiler wasn't able to convert to a Register<T>
from a P
with different sizeof
, since it only saw one argument and didn't consider the other two constructor templates.