#include <iostream>
// N is the number of address bits you want to use
template<std::size_t N>
struct address_t
{
public:
explicit constexpr address_t(std::uintptr_t value) :
address{ checked_value(value) }
{
}
operator std::uintptr_t() const noexcept
{
return address;
}
void operator=(std::uintptr_t value)
{
address = checked_value(value);
}
private:
constexpr std::uintptr_t checked_value(std::uintptr_t value)
{
if (value >= (1 << N)) throw std::invalid_argument("address value too large");
return value;
}
std::uintptr_t address;
};
int main()
{
//addres_t<4> means an address with max. 4 bits
static constexpr address_t<4> compile_time_address{15}; // will not compile if value too big
std::cout << compile_time_address << "\n"; // implicit conversion to std::uint8_t
address_t<4> runtime_address{15};
std::cout << runtime_address << "\n";
runtime_address = 12;
std::cout << runtime_address << "\n";
try
{
// at runtime assigning an invalid value will throw.
address_t<4> runtime_address_fail{16};
}
catch (const std::invalid_argument&)
{
std::cout << "invalid addres, value too large\n";
}
return 0;
}