261 lines
8.6 KiB
C++
261 lines
8.6 KiB
C++
#ifndef MATADOR_IDENTIFIER_HPP
|
|
#define MATADOR_IDENTIFIER_HPP
|
|
|
|
#include "matador/utils/basic_types.hpp"
|
|
#include "matador/utils/default_type_traits.hpp"
|
|
#include "matador/utils/error.hpp"
|
|
#include "matador/utils/field_attributes.hpp"
|
|
#include "matador/utils/result.hpp"
|
|
#include "matador/utils/types.hpp"
|
|
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <typeindex>
|
|
#include <variant>
|
|
|
|
namespace matador::utils {
|
|
class identifier_serializer {
|
|
public:
|
|
virtual ~identifier_serializer() = default;
|
|
|
|
virtual void serialize(int8_t &, const field_attributes &) = 0;
|
|
virtual void serialize(int16_t &, const field_attributes &) = 0;
|
|
virtual void serialize(int32_t &, const field_attributes &) = 0;
|
|
virtual void serialize(int64_t &, const field_attributes &) = 0;
|
|
virtual void serialize(uint8_t &, const field_attributes &) = 0;
|
|
virtual void serialize(uint16_t &, const field_attributes &) = 0;
|
|
virtual void serialize(uint32_t &, const field_attributes &) = 0;
|
|
virtual void serialize(uint64_t &, const field_attributes &) = 0;
|
|
virtual void serialize(std::string &, const field_attributes &) = 0;
|
|
virtual void serialize(null_type_t &, const field_attributes &) = 0;
|
|
};
|
|
|
|
template<typename Type, class Enabled = void>
|
|
struct identifier_type_traits;
|
|
|
|
template<typename Type>
|
|
struct identifier_type_traits<Type, std::enable_if_t<std::is_integral_v<Type> && std::is_signed_v<Type> > > {
|
|
static bool is_valid(Type value) { return value != 0; }
|
|
static std::string to_string(const Type value) { return std::to_string(value); }
|
|
};
|
|
|
|
template<typename Type>
|
|
struct identifier_type_traits<Type, std::enable_if_t<std::is_integral_v<Type> && !std::is_signed_v<Type> > > {
|
|
static bool is_valid(Type value) { return value > 0; }
|
|
static std::string to_string(const Type value) { return std::to_string(value); }
|
|
};
|
|
|
|
template<typename Type>
|
|
struct identifier_type_traits<Type, std::enable_if_t<std::is_same_v<Type, std::string> > > {
|
|
static bool is_valid(const Type &value) { return !value.empty(); }
|
|
static std::string to_string(const Type &value) { return value; }
|
|
};
|
|
|
|
template<>
|
|
struct identifier_type_traits<null_type_t, void> {
|
|
static bool is_valid() { return false; }
|
|
static std::string to_string() { return "null"; }
|
|
};
|
|
|
|
namespace detail {
|
|
template<typename Type>
|
|
size_t hash(const Type &value) {
|
|
return std::hash<Type>()(value);
|
|
}
|
|
|
|
size_t hash(const std::string &value);
|
|
}
|
|
|
|
template <class T>
|
|
struct is_identifier_supported : std::false_type {};
|
|
|
|
template <> struct is_identifier_supported<int8_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<int16_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<int32_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<int64_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<uint8_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<uint16_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<uint32_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<uint64_t> : std::true_type {};
|
|
template <> struct is_identifier_supported<std::string> : std::true_type {};
|
|
|
|
template <class T>
|
|
inline constexpr bool is_identifier_supported_v = is_identifier_supported<std::decay_t<T>>::value;
|
|
|
|
class identifier {
|
|
public:
|
|
using value_type = std::variant<
|
|
std::monostate,
|
|
int8_t, int16_t, int32_t, int64_t,
|
|
uint8_t, uint16_t, uint32_t, uint64_t,
|
|
std::string
|
|
>;
|
|
|
|
identifier();
|
|
|
|
template<typename Type, std::enable_if_t<is_identifier_supported_v<Type>, int> = 0>
|
|
explicit identifier(Type &&id)
|
|
: value_(std::forward<Type>(id)) {
|
|
}
|
|
|
|
identifier(const identifier &x) = default;
|
|
identifier &operator=(const identifier &x);
|
|
identifier(identifier &&x) noexcept = default;
|
|
identifier &operator=(identifier &&x) noexcept = default;
|
|
|
|
explicit identifier(std::nullptr_t);
|
|
explicit identifier(const char *value);
|
|
|
|
identifier& operator=(std::nullptr_t);
|
|
|
|
template<typename Type, std::enable_if_t<std::is_integral_v<std::decay_t<Type>> && !std::is_same_v<std::decay_t<Type>, bool>, int> = 0>
|
|
identifier& operator=(Type value) {
|
|
assign_integral(value);
|
|
return *this;
|
|
}
|
|
|
|
identifier& operator=(const std::string &value);
|
|
|
|
identifier& operator=(const char *value);
|
|
|
|
template<typename Type, std::enable_if_t<is_identifier_supported_v<Type> && std::is_same_v<std::decay_t<Type>, bool>, int> = 0>
|
|
identifier &operator=(Type &&value) {
|
|
value_ = std::forward<Type>(value);
|
|
return *this;
|
|
}
|
|
|
|
~identifier() = default;
|
|
|
|
bool operator==(const identifier &x) const;
|
|
bool operator!=(const identifier &x) const;
|
|
bool operator<(const identifier &x) const;
|
|
bool operator<=(const identifier &x) const;
|
|
bool operator>(const identifier &x) const;
|
|
bool operator>=(const identifier &x) const;
|
|
|
|
[[nodiscard]] std::string str() const;
|
|
[[nodiscard]] const std::type_index &type_index() const;
|
|
[[nodiscard]] basic_type type() const;
|
|
|
|
[[nodiscard]] bool is_integer() const;
|
|
[[nodiscard]] bool is_varchar() const;
|
|
[[nodiscard]] bool is_null() const;
|
|
|
|
[[nodiscard]] bool is_valid() const;
|
|
void clear();
|
|
|
|
void serialize(identifier_serializer &s) const;
|
|
|
|
[[nodiscard]] size_t hash() const;
|
|
|
|
template <typename Type, std::enable_if_t<is_identifier_supported_v<Type>, int> = 0>
|
|
result<Type, error> as() const;
|
|
|
|
template <typename Type, std::enable_if_t<std::is_integral_v<Type> && !std::is_same_v<Type, bool>, int> = 0>
|
|
result<Type, error> convert() const;
|
|
|
|
friend std::ostream &operator<<(std::ostream &out, const identifier &id);
|
|
|
|
private:
|
|
template<typename Type>
|
|
void assign_integral(Type value) {
|
|
if constexpr (std::is_signed_v<Type>) {
|
|
if (sizeof(Type) <= sizeof(int8_t)) {
|
|
value_ = static_cast<int8_t>(value);
|
|
} else if (sizeof(Type) <= sizeof(int16_t)) {
|
|
value_ = static_cast<int16_t>(value);
|
|
} else if (sizeof(Type) <= sizeof(int32_t)) {
|
|
value_ = static_cast<int32_t>(value);
|
|
} else {
|
|
value_ = static_cast<int64_t>(value);
|
|
}
|
|
} else {
|
|
if (sizeof(Type) <= sizeof(uint8_t)) {
|
|
value_ = static_cast<uint8_t>(value);
|
|
} else if (sizeof(Type) <= sizeof(uint16_t)) {
|
|
value_ = static_cast<uint16_t>(value);
|
|
} else if (sizeof(Type) <= sizeof(uint32_t)) {
|
|
value_ = static_cast<uint32_t>(value);
|
|
} else {
|
|
value_ = static_cast<uint64_t>(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t type_rank(const value_type &value);
|
|
static std::type_index type_index_from_value(const value_type &value);
|
|
static basic_type type_from_value(const value_type &value);
|
|
|
|
private:
|
|
value_type value_{std::monostate{}};
|
|
};
|
|
|
|
template <typename Target, typename Source>
|
|
bool in_range(Source value) {
|
|
if constexpr (std::is_signed_v<Source> == std::is_signed_v<Target>) {
|
|
return value >= static_cast<Target>(std::numeric_limits<Source>::min()) &&
|
|
value <= static_cast<Target>(std::numeric_limits<Source>::max());
|
|
} else if constexpr (std::is_signed_v<Source> && !std::is_signed_v<Target>) {
|
|
if (value < 0) {
|
|
return false;
|
|
}
|
|
using UnsignedSource = std::make_unsigned_t<Source>;
|
|
return static_cast<UnsignedSource>(value) <= std::numeric_limits<Target>::max();
|
|
} else {
|
|
return value <= static_cast<std::make_unsigned_t<Target>>(std::numeric_limits<Target>::max());
|
|
}
|
|
}
|
|
|
|
template <typename Type, std::enable_if_t<is_identifier_supported_v<Type>, int>>
|
|
result<Type, error> identifier::as() const {
|
|
return std::visit([](const auto &v) -> result<Type, error> {
|
|
using StoredType = std::decay_t<decltype(v)>;
|
|
|
|
if constexpr (std::is_same_v<StoredType, std::monostate>) {
|
|
return failure(error{});
|
|
} else if constexpr (std::is_same_v<StoredType, Type>) {
|
|
return ok<Type>(v);
|
|
} else {
|
|
return failure(error{});
|
|
}
|
|
}, value_);
|
|
}
|
|
|
|
template <typename Type, std::enable_if_t<std::is_integral_v<Type> && !std::is_same_v<Type, bool>, int>>
|
|
result<Type, error> identifier::convert() const {
|
|
return std::visit([](const auto &v) -> result<Type, error> {
|
|
using Stored = std::decay_t<decltype(v)>;
|
|
|
|
if constexpr (std::is_same_v<Stored, std::monostate>) {
|
|
return failure(error{});
|
|
} else if constexpr (std::is_integral_v<Stored>) {
|
|
if (!in_range<Type>(v)) {
|
|
return failure(error{});
|
|
}
|
|
return ok<Type>(static_cast<Type>(v));
|
|
} else {
|
|
return failure(error{});
|
|
}
|
|
}, value_);
|
|
}
|
|
|
|
static identifier null_identifier{};
|
|
|
|
/// @cond MATADOR_DEV
|
|
struct id_pk_hash {
|
|
size_t operator()(const identifier &id) const;
|
|
};
|
|
|
|
/// @endcond
|
|
}
|
|
|
|
template<>
|
|
struct std::hash<matador::utils::identifier> {
|
|
size_t operator()(const matador::utils::identifier &id) const noexcept {
|
|
return id.hash();
|
|
}
|
|
}; // namespace std
|
|
|
|
#endif //MATADOR_IDENTIFIER_HPP
|