#include <iostream>
template<int UB, int US>
struct Unit { // a unit in the SI/IEEE systems
enum { B=UB, s=US }; // bytes and seconds
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
explicit constexpr Value(double d) : val{d} {}
Value operator+(const Value& x) {
return Value{val + x.val};
}
};
using Byte = Unit<1, 0>; // unit: byte
using MemBlock = Value<Byte>; // shortcut
constexpr Value<Byte> operator"" _B(long double d) {
return Value<Byte>{d};
}
constexpr Value<Byte> operator"" _kB(long double d) {
return Value<Byte>{d * 1024};
}
constexpr Value<Byte> operator"" _MB(long double d) {
return Value<Byte>{d * 1024 * 1024};
}
constexpr double n_B(const Value<Byte>& b) { // free functions to be simple
return b.val;
}
constexpr double n_kB(const Value<Byte>& b) {
return b.val / 1024;
}
constexpr double n_MB(const Value<Byte>& b) {
return b.val / 1024 / 1024;
}
// Let's add something useful to the example
using Second = Unit<0, 1>; // unit: sec.
// using Second2 = Unit<0, 2>; // s^2
using Time = Value<Second>; // time in seconds by default
using TransferRate = Value<Unit<1, -1>>; // bytes/second type
constexpr Value<Second> operator"" _s(long double d) {
return Value<Second>{d};
}
constexpr double n_s(const Value<Second>& s) {
return s.val;
}
constexpr TransferRate operator/(const MemBlock& m, const Time& t) {
return TransferRate{n_B(m) / n_s(t)};
}
int main() {
MemBlock a = 1024.0_B;
//MemBlock b = 3.0; // NOT OK! unit needed
auto b = 3.0_kB; // OK
auto c = a + b;
std::cout << n_kB(a) << "+" << n_kB(b)
<< " = " << n_kB(c) << '\n'; // or define your operator<<()
// Another example..
TransferRate slow = c / 1.0_s;
auto fast = slow + slow;
// etc..
}