169 lines
6.0 KiB
C++
169 lines
6.0 KiB
C++
#pragma once
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <concepts>
|
|
#include <initializer_list>
|
|
#include <limits>
|
|
#include <span>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
namespace structive {
|
|
enum class Synchronization_Default {
|
|
independent,
|
|
shared,
|
|
unsynchronized
|
|
};
|
|
struct Sync_Default_Rule {
|
|
Synchronization_Default value{Synchronization_Default::independent};
|
|
};
|
|
struct Sync_Property_Rule {
|
|
enum class Mode {
|
|
independent,
|
|
unsynchronized
|
|
};
|
|
Mode mode{Mode::independent};
|
|
std::string property;
|
|
};
|
|
struct Sync_Group_Rule {
|
|
std::string name;
|
|
std::vector<std::string> properties;
|
|
};
|
|
template <auto Member, Sync_Property_Rule::Mode Mode>
|
|
struct Sync_Member_Rule {
|
|
static constexpr auto member = Member;
|
|
static constexpr auto mode = Mode;
|
|
};
|
|
template <auto... Members>
|
|
struct Sync_Member_Group_Rule {
|
|
static_assert(sizeof...(Members) > 0);
|
|
std::string name;
|
|
};
|
|
inline constexpr Sync_Default_Rule sync_all_independent{Synchronization_Default::independent};
|
|
inline constexpr Sync_Default_Rule sync_all_shared{Synchronization_Default::shared};
|
|
inline constexpr Sync_Default_Rule sync_all_unsynchronized{Synchronization_Default::unsynchronized};
|
|
inline Sync_Property_Rule sync_independent(std::string_view property) {
|
|
return {Sync_Property_Rule::Mode::independent, std::string(property)};
|
|
}
|
|
inline Sync_Property_Rule sync_unsynchronized(std::string_view property) {
|
|
return {Sync_Property_Rule::Mode::unsynchronized, std::string(property)};
|
|
}
|
|
template <auto Member>
|
|
constexpr auto sync_independent() {
|
|
return Sync_Member_Rule<Member, Sync_Property_Rule::Mode::independent>{};
|
|
}
|
|
template <auto Member>
|
|
constexpr auto sync_unsynchronized() {
|
|
return Sync_Member_Rule<Member, Sync_Property_Rule::Mode::unsynchronized>{};
|
|
}
|
|
template <auto... Members>
|
|
Sync_Member_Group_Rule<Members...> sync_group(std::string_view name) {
|
|
return {std::string(name)};
|
|
}
|
|
template <class... Properties>
|
|
Sync_Group_Rule sync_group(std::string_view name, Properties&&... properties) requires (std::convertible_to<Properties, std::string_view> && ...) {
|
|
Sync_Group_Rule rule;
|
|
rule.name = name;
|
|
rule.properties.reserve(sizeof...(Properties));
|
|
(rule.properties.emplace_back(std::string_view(std::forward<Properties>(properties))), ...);
|
|
return rule;
|
|
}
|
|
class Synchronization_Plan {
|
|
Synchronization_Default default_mode_{Synchronization_Default::independent};
|
|
std::vector<Sync_Property_Rule> property_rules_;
|
|
std::vector<Sync_Group_Rule> group_rules_;
|
|
public:
|
|
using synchronization_plan_tag = void;
|
|
Synchronization_Plan() = default;
|
|
explicit Synchronization_Plan(Synchronization_Default mode) : default_mode_(mode) {}
|
|
Synchronization_Default default_mode() const noexcept {
|
|
return default_mode_;
|
|
}
|
|
const auto& property_rules() const noexcept {
|
|
return property_rules_;
|
|
}
|
|
const auto& group_rules() const noexcept {
|
|
return group_rules_;
|
|
}
|
|
Synchronization_Plan& set_default(Synchronization_Default mode) {
|
|
default_mode_ = mode;
|
|
return *this;
|
|
}
|
|
Synchronization_Plan& independent(std::string_view property) {
|
|
property_rules_.push_back(sync_independent(property));
|
|
return *this;
|
|
}
|
|
Synchronization_Plan& unsynchronized(std::string_view property) {
|
|
property_rules_.push_back(sync_unsynchronized(property));
|
|
return *this;
|
|
}
|
|
Synchronization_Plan& group(std::string_view name, std::span<const std::string_view> properties) {
|
|
Sync_Group_Rule rule;
|
|
rule.name = name;
|
|
rule.properties.reserve(properties.size());
|
|
for (auto property : properties) {
|
|
rule.properties.emplace_back(property);
|
|
}
|
|
group_rules_.push_back(std::move(rule));
|
|
return *this;
|
|
}
|
|
Synchronization_Plan& group(std::string_view name, std::initializer_list<std::string_view> properties) {
|
|
return group(name, std::span<const std::string_view>{properties.begin(), properties.size()});
|
|
}
|
|
Synchronization_Plan& apply(Sync_Default_Rule rule) {
|
|
default_mode_ = rule.value;
|
|
return *this;
|
|
}
|
|
Synchronization_Plan& apply(Sync_Property_Rule rule) {
|
|
property_rules_.push_back(std::move(rule));
|
|
return *this;
|
|
}
|
|
Synchronization_Plan& apply(Sync_Group_Rule rule) {
|
|
group_rules_.push_back(std::move(rule));
|
|
return *this;
|
|
}
|
|
};
|
|
template <class Type>
|
|
concept Synchronization_Plan_Type = requires {
|
|
typename Type::synchronization_plan_tag;
|
|
};
|
|
template <class Type>
|
|
concept Runtime_Synchronization_Rule = std::same_as<std::remove_cvref_t<Type>, Sync_Default_Rule> || std::same_as<std::remove_cvref_t<Type>, Sync_Property_Rule> || std::same_as<std::remove_cvref_t<Type>, Sync_Group_Rule>;
|
|
template <class... Rules>
|
|
struct Synchronization_Spec {
|
|
using synchronization_spec_tag = void;
|
|
std::tuple<Rules...> rules;
|
|
};
|
|
template <class Type>
|
|
concept Synchronization_Spec_Type = requires {
|
|
typename Type::synchronization_spec_tag;
|
|
};
|
|
template <class Type>
|
|
concept Synchronization_Source_Type = Synchronization_Plan_Type<Type> || Synchronization_Spec_Type<Type>;
|
|
template <class... Rules>
|
|
auto synchronization(Rules&&... rules) {
|
|
if constexpr ((Runtime_Synchronization_Rule<Rules> && ...)) {
|
|
Synchronization_Plan plan;
|
|
(plan.apply(std::forward<Rules>(rules)), ...);
|
|
return plan;
|
|
} else {
|
|
return Synchronization_Spec<std::decay_t<Rules>...>{{std::forward<Rules>(rules)...}};
|
|
}
|
|
}
|
|
template <std::size_t Property_Count>
|
|
struct Resolved_Synchronization_Plan {
|
|
static constexpr std::size_t unsynchronized_slot = std::numeric_limits<std::size_t>::max();
|
|
std::array<std::size_t, Property_Count> lock_slots{};
|
|
std::size_t lock_count{};
|
|
constexpr std::size_t slot(std::size_t property_index) const noexcept {
|
|
return lock_slots[property_index];
|
|
}
|
|
constexpr bool uses_lock(std::size_t property_index) const noexcept {
|
|
return slot(property_index) != unsynchronized_slot;
|
|
}
|
|
};
|
|
}
|