aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvic-simulation')
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp2
-rw-r--r--src/openvic-simulation/diplomacy/CountryRelation.cpp53
-rw-r--r--src/openvic-simulation/diplomacy/CountryRelation.hpp95
-rw-r--r--src/openvic-simulation/diplomacy/DiplomacyManager.hpp2
-rw-r--r--src/openvic-simulation/diplomacy/DiplomaticAction.cpp117
-rw-r--r--src/openvic-simulation/diplomacy/DiplomaticAction.hpp24
-rw-r--r--src/openvic-simulation/utility/ErrorMacros.hpp68
-rw-r--r--src/openvic-simulation/utility/StringUtils.hpp1
-rw-r--r--src/openvic-simulation/utility/Utility.hpp62
9 files changed, 374 insertions, 50 deletions
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index b5a32d3..6a2a3bb 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -946,7 +946,7 @@ bool Dataloader::load_defines(GameManager& game_manager) {
Logger::error("Failed to load on actions!");
ret = false;
}
- if (!game_manager.get_diplomacy_manager().get_diplomatic_action_manager().setup_diplomatic_actions()) {
+ if (!game_manager.get_diplomacy_manager().get_diplomatic_action_manager().setup_diplomatic_actions(game_manager)) {
Logger::error("Failed to load diplomatic actions!");
ret = false;
}
diff --git a/src/openvic-simulation/diplomacy/CountryRelation.cpp b/src/openvic-simulation/diplomacy/CountryRelation.cpp
new file mode 100644
index 0000000..d07739c
--- /dev/null
+++ b/src/openvic-simulation/diplomacy/CountryRelation.cpp
@@ -0,0 +1,53 @@
+#include "CountryRelation.hpp"
+
+#include <cassert>
+
+#include "openvic-simulation/country/CountryInstance.hpp"
+#include "openvic-simulation/utility/ErrorMacros.hpp"
+
+using namespace OpenVic;
+
+CountryRelationInstanceProxy::CountryRelationInstanceProxy(std::string_view id) : country_id { id } {}
+
+CountryRelationInstanceProxy::CountryRelationInstanceProxy(CountryInstance const* country)
+ : country_id { country->get_base_country()->get_identifier() } {}
+
+CountryRelationInstanceProxy::operator std::string_view() const {
+ return country_id;
+}
+
+CountryRelationManager::CountryRelationManager(/* TODO: Country Instance Manager Reference */) {}
+
+bool CountryRelationManager::add_country(CountryRelationInstanceProxy country) {
+ // TODO: iterate through Country Instances adding country to every previously existing country_relations
+ return true;
+}
+
+bool CountryRelationManager::remove_country(CountryRelationInstanceProxy country) {
+ // TODO: iterate through country_relations removing every pair that references the country's id
+ return true;
+}
+
+country_relation_value_t CountryRelationManager::get_country_relation(
+ CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient
+) const {
+ auto it = country_relations.find({ country.country_id, recepient.country_id });
+ OV_ERR_FAIL_COND_V(it == country_relations.end(), 0);
+ return it->second;
+}
+
+country_relation_value_t*
+CountryRelationManager::get_country_relation_ptr(CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient) {
+ auto it = country_relations.find({ country.country_id, recepient.country_id });
+ OV_ERR_FAIL_COND_V(it == country_relations.end(), nullptr);
+ return &it.value();
+}
+
+bool CountryRelationManager::set_country_relation(
+ CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient, country_relation_value_t value
+) {
+ auto it = country_relations.find({ country.country_id, recepient.country_id });
+ OV_ERR_FAIL_COND_V(it == country_relations.end(), false);
+ it.value() = value;
+ return true;
+}
diff --git a/src/openvic-simulation/diplomacy/CountryRelation.hpp b/src/openvic-simulation/diplomacy/CountryRelation.hpp
new file mode 100644
index 0000000..76b3897
--- /dev/null
+++ b/src/openvic-simulation/diplomacy/CountryRelation.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include <compare>
+#include <tuple>
+
+#include "openvic-simulation/types/OrderedContainers.hpp"
+#include "openvic-simulation/utility/Utility.hpp"
+
+namespace OpenVic {
+ struct CountryInstance;
+
+ struct CountryRelationInstanceProxy {
+ std::string_view country_id;
+
+ CountryRelationInstanceProxy(std::string_view id);
+ CountryRelationInstanceProxy(CountryInstance const* country);
+
+ operator std::string_view() const;
+ };
+
+ struct CountryRelationPair : std::pair<std::string_view, std::string_view> {
+ using base_type = std::pair<std::string_view, std::string_view>;
+ using base_type::base_type;
+
+ inline constexpr auto operator<=>(CountryRelationPair const& rhs) const {
+ using ordering = decltype(OpenVic::utility::three_way(std::tie(first, second), std::tie(rhs.first, rhs.second)));
+
+ auto tied = std::tie(first, second);
+ if (tied == std::tie(rhs.first, rhs.second)) {
+ return ordering::equivalent;
+ } else if (tied == std::tie(rhs.second, rhs.first)) {
+ return ordering::equivalent;
+ }
+
+ auto& [lhs_left, lhs_right] = *this;
+ auto& [rhs_left, rhs_right] = rhs;
+
+ auto& lhs_comp_first = lhs_left > lhs_right ? lhs_left : lhs_right;
+ auto& lhs_comp_second = lhs_left < lhs_right ? lhs_left : lhs_right;
+
+ auto& rhs_comp_first = rhs_left > rhs_right ? rhs_left : rhs_right;
+ auto& rhs_comp_second = rhs_left < rhs_right ? rhs_left : rhs_right;
+
+ return OpenVic::utility::three_way(
+ std::tie(lhs_comp_first, lhs_comp_second), std::tie(rhs_comp_first, rhs_comp_second)
+ );
+ }
+
+ inline constexpr bool operator==(CountryRelationPair const& rhs) const {
+ auto tied = std::tie(first, second);
+ return tied == std::tie(rhs.first, rhs.second) || tied == std::tie(rhs.second, rhs.first);
+ }
+ };
+}
+
+namespace std {
+ template<>
+ struct hash<OpenVic::CountryRelationPair> {
+ size_t operator()(OpenVic::CountryRelationPair const& pair) const {
+ std::size_t seed1(0);
+ OpenVic::utility::hash_combine(seed1, pair.first);
+ OpenVic::utility::hash_combine(seed1, pair.second);
+
+ std::size_t seed2(0);
+ OpenVic::utility::hash_combine(seed2, pair.second);
+ OpenVic::utility::hash_combine(seed2, pair.first);
+
+ return std::min(seed1, seed2);
+ }
+ };
+}
+
+namespace OpenVic {
+ using country_relation_value_t = int16_t;
+
+ struct CountryRelationManager {
+ private:
+ // TODO: reference of manager responsible for storing CountryInstances
+ ordered_map<CountryRelationPair, country_relation_value_t> country_relations;
+
+ public:
+ CountryRelationManager(/* TODO: Country Instance Manager Reference */);
+
+ bool add_country(CountryRelationInstanceProxy country);
+ bool remove_country(CountryRelationInstanceProxy country);
+
+ country_relation_value_t
+ get_country_relation(CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient) const;
+ country_relation_value_t*
+ get_country_relation_ptr(CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient);
+ bool set_country_relation(
+ CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient, country_relation_value_t value
+ );
+ };
+}
diff --git a/src/openvic-simulation/diplomacy/DiplomacyManager.hpp b/src/openvic-simulation/diplomacy/DiplomacyManager.hpp
index c71bdbf..7aca409 100644
--- a/src/openvic-simulation/diplomacy/DiplomacyManager.hpp
+++ b/src/openvic-simulation/diplomacy/DiplomacyManager.hpp
@@ -1,9 +1,11 @@
#pragma once
+#include "openvic-simulation/diplomacy/CountryRelation.hpp"
#include "openvic-simulation/diplomacy/DiplomaticAction.hpp"
namespace OpenVic {
class DiplomacyManager {
DiplomaticActionManager PROPERTY_REF(diplomatic_action_manager);
+ CountryRelationManager PROPERTY_REF(country_relation_manager);
};
}
diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp
index d10eda3..a386d9f 100644
--- a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp
+++ b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp
@@ -1,19 +1,19 @@
#include "DiplomaticAction.hpp"
#include <string_view>
-#include <variant>
+#include "openvic-simulation/GameManager.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"
#include "openvic-simulation/utility/Logger.hpp"
using namespace OpenVic;
DiplomaticActionType::DiplomaticActionType(DiplomaticActionType::Initializer&& initializer)
- : commit_action_caller { std::move(initializer.commit) },
- allowed_to_commit { std::move(initializer.allowed) }, get_acceptance { std::move(initializer.get_acceptance) } {}
+ : commit_action_caller { std::move(initializer.commit) }, allowed_to_commit { std::move(initializer.allowed) },
+ get_acceptance { std::move(initializer.get_acceptance) } {}
CancelableDiplomaticActionType::CancelableDiplomaticActionType(CancelableDiplomaticActionType::Initializer&& initializer)
- : allowed_to_cancel{std::move(initializer.allowed_cancel)}, DiplomaticActionType(std::move(initializer)) {}
+ : allowed_to_cancel { std::move(initializer.allowed_cancel) }, DiplomaticActionType(std::move(initializer)) {}
DiplomaticActionManager::DiplomaticActionManager() {}
@@ -38,7 +38,7 @@ bool DiplomaticActionManager::add_cancelable_diplomatic_action(
}
DiplomaticActionTickCache DiplomaticActionManager::create_diplomatic_action_tick(
- std::string_view identifier, Country& sender, Country& reciever, std::any context_data
+ std::string_view identifier, CountryInstance* sender, CountryInstance* reciever, std::any context_data
) {
auto type = diplomatic_action_types.get_item_by_identifier(identifier);
@@ -52,92 +52,151 @@ DiplomaticActionTickCache DiplomaticActionManager::create_diplomatic_action_tick
return result;
}
-bool DiplomaticActionManager::setup_diplomatic_actions() {
+bool DiplomaticActionManager::setup_diplomatic_actions(GameManager& manager) {
using Argument = DiplomaticActionType::Argument;
bool result = true;
result &= add_diplomatic_action(
"form_alliance",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"call_ally",
{
.commit = [](Argument& arg) {},
- .allowed = [](const Argument& arg) { return false; },
- .get_acceptance = [](const Argument& arg) { return 1; }
+ .allowed =
+ [](const Argument& arg) {
+ return false;
+ },
+ .get_acceptance =
+ [](const Argument& arg) {
+ return 1;
+ },
}
);
result &= add_cancelable_diplomatic_action(
"request_military_access",
{
.commit = [](Argument& arg) {},
- .allowed_cancel = [](const Argument& arg) { return true; }
+ .allowed_cancel =
+ [](const Argument& arg) {
+ return true;
+ },
}
);
- result &= add_diplomatic_action(
- "give_military_access",
- { [](Argument& arg) {} }
- );
+ result &= add_diplomatic_action("give_military_access", { [](Argument& arg) {} });
result &= add_diplomatic_action(
"increase_relations",
{
- .commit = [](Argument& arg) {},
- .allowed = [](const Argument& arg) { return false; },
+ .commit =
+ [&manager](Argument& arg) {
+ auto relation = manager.get_diplomacy_manager().get_country_relation_manager().get_country_relation_ptr(
+ arg.sender, arg.reciever
+ );
+ if (!relation) {
+ return;
+ }
+ *relation += 25;
+ },
+ .allowed =
+ [](const Argument& arg) {
+ return false;
+ },
}
);
result &= add_diplomatic_action(
"decrease_relations",
- { [](Argument& arg) {} }
+ {
+ .commit =
+ [&manager](Argument& arg) {
+ auto relation = manager.get_diplomacy_manager().get_country_relation_manager().get_country_relation_ptr(
+ arg.sender, arg.reciever
+ );
+ if (!relation) {
+ return;
+ }
+ *relation -= 25;
+ },
+ .allowed =
+ [](const Argument& arg) {
+ return false;
+ },
+ }
);
result &= add_diplomatic_action(
"war_subsidies",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"declare_war",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"offer_peace",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"command_units",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"discredit",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"expel_advisors",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"increase_opinion",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"decrease_opinion",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"add_to_sphere",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"remove_from_sphere",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"justify_war",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
result &= add_diplomatic_action(
"give_vision",
- { [](Argument& arg) {} }
+ {
+ [](Argument& arg) {},
+ }
);
diplomatic_action_types.lock();
diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp
index 8f659c6..352b16e 100644
--- a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp
+++ b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp
@@ -8,17 +8,20 @@
#include <variant>
#include "openvic-simulation/country/Country.hpp"
+#include "openvic-simulation/country/CountryInstance.hpp"
#include "openvic-simulation/types/FunctionRef.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"
namespace OpenVic {
+ struct GameManager;
+
struct DiplomaticActionType {
friend struct DiplomaticActionManager;
friend struct CancelableDiplomaticActionType;
struct Argument {
- Country& sender;
- Country& reciever;
+ CountryInstance* sender;
+ CountryInstance* reciever;
std::any context_data;
};
@@ -68,7 +71,7 @@ namespace OpenVic {
allowed_to_cancel_func allowed_cancel = allowed_to_cancel_default;
operator DiplomaticActionType::Initializer() {
- return {commit, allowed, get_acceptance};
+ return { commit, allowed, get_acceptance };
}
};
@@ -87,7 +90,7 @@ namespace OpenVic {
constexpr DiplomaticActionTypeStorage(std::string_view identifier, T&& t) : HasIdentifier(identifier), base_type(t) {}
template<class Visitor>
- constexpr decltype(auto) visit(Visitor&& vis){
+ constexpr decltype(auto) visit(Visitor&& vis) {
return std::visit(std::forward<Visitor>(vis), *this);
}
@@ -99,7 +102,7 @@ namespace OpenVic {
constexpr bool is_cancelable() const {
return visit([](auto&& arg) -> bool {
using T = std::decay_t<decltype(arg)>;
- if constexpr(std::same_as<T, CancelableDiplomaticActionType>) {
+ if constexpr (std::same_as<T, CancelableDiplomaticActionType>) {
return true;
} else {
return false;
@@ -123,11 +126,14 @@ namespace OpenVic {
DiplomaticActionManager();
bool add_diplomatic_action(std::string_view identifier, DiplomaticActionType::Initializer&& initializer);
- bool add_cancelable_diplomatic_action(std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer);
+ bool add_cancelable_diplomatic_action(
+ std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer
+ );
- DiplomaticActionTickCache
- create_diplomatic_action_tick(std::string_view identifier, Country& sender, Country& reciever, std::any context_data);
+ DiplomaticActionTickCache create_diplomatic_action_tick(
+ std::string_view identifier, CountryInstance* sender, CountryInstance* reciever, std::any context_data
+ );
- bool setup_diplomatic_actions();
+ bool setup_diplomatic_actions(GameManager& manager);
};
}
diff --git a/src/openvic-simulation/utility/ErrorMacros.hpp b/src/openvic-simulation/utility/ErrorMacros.hpp
new file mode 100644
index 0000000..47b73d5
--- /dev/null
+++ b/src/openvic-simulation/utility/ErrorMacros.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "openvic-simulation/utility/Logger.hpp" // IWYU pragma: keep
+#include "openvic-simulation/utility/Utility.hpp"
+
+/**
+ * Try using `ERR_FAIL_COND_MSG`.
+ * Only use this macro if there is no sensible error message.
+ * If checking for null use ERR_FAIL_NULL_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_MSG instead.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, the current function returns.
+ */
+#define OV_ERR_FAIL_COND(m_cond) \
+ if (OV_unlikely(m_cond)) { \
+ ::OpenVic::Logger::error("Condition \"" _OV_STR(m_cond) "\" is true."); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg` and the current function returns.
+ *
+ * If checking for null use ERR_FAIL_NULL_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_MSG instead.
+ */
+#define OV_ERR_FAIL_COND_MSG(m_cond, m_msg) \
+ if (OV_unlikely(m_cond)) { \
+ ::OpenVic::Logger::error("Condition \"" _OV_STR(m_cond) "\" is true.", m_msg); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_COND_V_MSG`.
+ * Only use this macro if there is no sensible error message.
+ * If checking for null use ERR_FAIL_NULL_V_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, the current function returns `m_retval`.
+ */
+#define OV_ERR_FAIL_COND_V(m_cond, m_retval) \
+ if (OV_unlikely(m_cond)) { \
+ ::OpenVic::Logger::error( \
+ "Condition \"" _OV_STR(m_cond) "\" is true. Returning: " _OV_STR(m_retval) \
+ ); \
+ return m_retval; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg` and the current function returns `m_retval`.
+ *
+ * If checking for null use ERR_FAIL_NULL_V_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.
+ */
+#define OV_ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
+ if (OV_unlikely(m_cond)) { \
+ ::OpenVic::Logger::error( \
+ "Condition \"" _OV_STR(m_cond) "\" is true. Returning: " _OV_STR(m_retval), m_msg \
+ ); \
+ return m_retval; \
+ } else \
+ ((void)0)
diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp
index 570f296..e83fbda 100644
--- a/src/openvic-simulation/utility/StringUtils.hpp
+++ b/src/openvic-simulation/utility/StringUtils.hpp
@@ -4,6 +4,7 @@
#include <cctype>
#include <cstring>
#include <limits>
+#include <string>
#include <string_view>
#include <type_traits>
diff --git a/src/openvic-simulation/utility/Utility.hpp b/src/openvic-simulation/utility/Utility.hpp
index 0387e7f..dbbcf8f 100644
--- a/src/openvic-simulation/utility/Utility.hpp
+++ b/src/openvic-simulation/utility/Utility.hpp
@@ -4,6 +4,21 @@
#include <functional>
#include <type_traits>
+#if defined(__GNUC__)
+#define OV_likely(x) __builtin_expect(!!(x), 1)
+#define OV_unlikely(x) __builtin_expect(!!(x), 0)
+#else
+#define OV_likely(x) x
+#define OV_unlikely(x) x
+#endif
+
+// Turn argument to string constant:
+// https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
+#ifndef _OV_STR
+#define _OV_STR(m_x) #m_x
+#define _OV_MKSTR(m_x) _OV_STR(m_x)
+#endif
+
namespace OpenVic::utility {
[[noreturn]] inline void unreachable() {
// Uses compiler specific extensions if possible.
@@ -25,29 +40,35 @@ namespace OpenVic::utility {
template<size_t Shift, class T>
constexpr inline void hash_combine_index(std::size_t& s, const T& v) {
std::hash<T> h;
- if constexpr(Shift == 0) {
+ if constexpr (Shift == 0) {
s = h(v);
} else {
s ^= h(v) << Shift;
}
}
- template<class T, typename ...Args>
+ template<class T, typename... Args>
constexpr void perfect_hash(std::size_t& s, T&& v, Args&&... args) {
- static_assert(sizeof(T) + (sizeof(Args) + ...) <= sizeof(std::size_t), "Perfect hashes must be able to fit into size_t");
+ static_assert(
+ sizeof(T) + (sizeof(Args) + ...) <= sizeof(std::size_t), "Perfect hashes must be able to fit into size_t"
+ );
std::hash<T> h;
- if constexpr(sizeof...(args) == 0) {
+ if constexpr (sizeof...(args) == 0) {
s = h(v);
} else {
const std::tuple arg_tuple { args... };
s = h(v) << (sizeof(T) * CHAR_BIT);
- ([&]{
- // If args is not last pointer of args
- if (static_cast<const void*>(&(std::get<sizeof...(args) - 1>(arg_tuple))) != static_cast<const void*>(&args)) {
- s <<= sizeof(Args) * CHAR_BIT;
- }
- s |= std::hash<Args>{}(args);
- }(), ...);
+ (
+ [&] {
+ // If args is not last pointer of args
+ if (static_cast<const void*>(&(std::get<sizeof...(args) - 1>(arg_tuple))) !=
+ static_cast<const void*>(&args)) {
+ s <<= sizeof(Args) * CHAR_BIT;
+ }
+ s |= std::hash<Args> {}(args);
+ }(),
+ ...
+ );
}
}
@@ -59,4 +80,23 @@ namespace OpenVic::utility {
template<typename T, template<typename...> class Z>
inline constexpr bool is_specialization_of_v = is_specialization_of<T, Z>::value;
+
+ inline constexpr auto three_way(auto&& left, auto&& right) {
+ // This is Apple's fault again
+ #if __cpp_lib_three_way_comparison >= 201907L
+ if constexpr (std::three_way_comparable_with<std::decay_t<decltype(left)>, std::decay_t<decltype(right)>>) {
+ return left <=> right;
+ } else
+ #endif
+ {
+ if (left < right) {
+ return std::weak_ordering::less;
+ } else if (left > right) {
+ return std::weak_ordering::greater;
+ } else {
+ return std::weak_ordering::equivalent;
+ }
+ }
+ };
+
}