{
    static const uint8_t MAX_BITS = sizeof(T)*8;
    assert(bits <= MAX_BITS);
    return std::bitset<sizeof(T) * 8>(static_cast<uint64_t>(value))
            .to_string().substr(MAX_BITS - bits, bits);
}
template <typename T>
const char* type_name()
{
#define MAKE_TYPE_NAME(TYPE) \
    if constexpr (std::is_same_v<TYPE, T>) return #TYPE
    MAKE_TYPE_NAME(UInt8);
    MAKE_TYPE_NAME(UInt16);
    MAKE_TYPE_NAME(UInt32);
    MAKE_TYPE_NAME(UInt64);
    MAKE_TYPE_NAME(Int8);
    MAKE_TYPE_NAME(Int16);
    MAKE_TYPE_NAME(Int32);
    MAKE_TYPE_NAME(Int64);
    MAKE_TYPE_NAME(Float32);
    MAKE_TYPE_NAME(Float64);
#undef MAKE_TYPE_NAME
    return typeid(T).name();
}
template <typename T>
DataTypePtr makeDataType()