refactored identifier, added tests and added primary_key_accessor and tests

This commit is contained in:
2026-04-03 19:14:14 +02:00
parent f751541bdd
commit 920fdc68ed
9 changed files with 696 additions and 275 deletions
+146 -116
View File
@@ -1,14 +1,19 @@
#ifndef MATADOR_IDENTIFIER_HPP
#define MATADOR_IDENTIFIER_HPP
#include "matador/utils/default_type_traits.hpp"
#include "matador/utils/field_attributes.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 {
@@ -31,7 +36,13 @@ 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> > > {
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); }
};
@@ -48,122 +59,70 @@ struct identifier_type_traits<null_type_t, void> {
static std::string to_string() { return "null"; }
};
template<>
struct identifier_type_traits<const char *, void> {
static bool is_valid(const char *value);
static std::string to_string(const char *value);
};
namespace detail {
template<typename Type>
size_t hash(const Type &value) {
return std::hash<Type>()(value);
}
size_t hash(const char *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 {
private:
struct base {
base(const std::type_index &ti, basic_type type);
base(const base &x) = delete;
base &operator=(const base &x) = delete;
base(base &&x) = delete;
base &operator=(base &&x) = delete;
virtual ~base() = default;
[[nodiscard]] virtual base *copy() const = 0;
[[nodiscard]] virtual bool equal_to(const base &x) const = 0;
[[nodiscard]] virtual bool less(const base &x) const = 0;
[[nodiscard]] virtual bool is_valid() const = 0;
virtual void serialize(identifier_serializer &s) = 0;
[[nodiscard]] virtual std::string str() const = 0;
[[nodiscard]] virtual size_t hash() const = 0;
std::type_index type_index_;
basic_type type_{basic_type::Null};
};
template<class IdType>
struct pk final : base {
using self = pk;
explicit pk(const IdType &id)
: base(std::type_index(typeid(IdType)), data_type_traits<IdType>::type(1))
, id_(id)
, size_(sizeof(IdType)) {
}
[[nodiscard]] base *copy() const override {
return new self(id_);
}
[[nodiscard]] bool equal_to(const base &x) const override {
return static_cast<const pk &>(x).id_ == id_;
}
[[nodiscard]] bool less(const base &x) const override {
return id_ < static_cast<const pk &>(x).id_;
}
[[nodiscard]] bool is_valid() const override {
return identifier_type_traits<IdType>::is_valid(id_);
}
[[nodiscard]] std::string str() const override {
return identifier_type_traits<IdType>::to_string(id_);
}
void serialize(identifier_serializer &s) override {
s.serialize(id_, size_);
}
[[nodiscard]] size_t hash() const override {
return detail::hash(id_);
}
IdType id_;
size_t size_{};
};
struct null_pk final : base {
null_pk();
[[nodiscard]] base *copy() const override;
[[nodiscard]] bool equal_to(const base &x) const override;
[[nodiscard]] bool less(const base &x) const override;
[[nodiscard]] bool is_valid() const override;
void serialize(identifier_serializer &s) override;
[[nodiscard]] std::string str() const override;
[[nodiscard]] size_t hash() const override;
null_type_t null_;
};
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>
explicit identifier(const Type &id)
: id_(std::make_shared<pk<Type> >(id)) {
template<typename Type, std::enable_if_t<is_identifier_supported_v<Type>, int> = 0>
explicit identifier(Type &&id)
: value_(std::forward<Type>(id)) {
}
explicit identifier(const char *id)
: id_(std::make_shared<pk<std::string> >(id)) {
}
identifier(const identifier &x);
identifier(const identifier &x) = default;
identifier &operator=(const identifier &x);
identifier(identifier &&x) noexcept;
identifier &operator=(identifier &&x) noexcept;
identifier(identifier &&x) noexcept = default;
identifier &operator=(identifier &&x) noexcept = default;
template<typename Type>
identifier &operator=(const Type &value) {
id_ = std::make_shared<pk<Type> >(value);
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 char *value) {
id_ = std::make_shared<pk<std::string> >(value);
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;
}
@@ -180,17 +139,8 @@ public:
[[nodiscard]] const std::type_index &type_index() const;
[[nodiscard]] basic_type type() const;
[[nodiscard]] identifier share() const;
[[nodiscard]] size_t use_count() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_date() const;
[[nodiscard]] bool is_time() const;
[[nodiscard]] bool is_timestamp() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_valid() const;
@@ -200,15 +150,97 @@ public:
[[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:
explicit identifier(const std::shared_ptr<base> &id);
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:
std::shared_ptr<base> id_;
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
@@ -219,13 +251,11 @@ struct id_pk_hash {
/// @endcond
}
namespace std {
template<>
struct hash<matador::utils::identifier> {
struct std::hash<matador::utils::identifier> {
size_t operator()(const matador::utils::identifier &id) const noexcept {
return id.hash();
}
};
} // namespace std
}; // namespace std
#endif //MATADOR_IDENTIFIER_HPP
#endif //MATADOR_IDENTIFIER_HPP