diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 6344bd2ea8..b063cbba76 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -4,6 +4,7 @@ add_files(
alloc_type.hpp
backup_type.hpp
bitmath_func.hpp
+ convertible_through_base.hpp
endian_func.hpp
enum_type.hpp
format.hpp
diff --git a/src/core/convertible_through_base.hpp b/src/core/convertible_through_base.hpp
new file mode 100644
index 0000000000..0b22f35cd5
--- /dev/null
+++ b/src/core/convertible_through_base.hpp
@@ -0,0 +1,24 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file convertible_through_base.hpp Concept for unifying the convert through 'base()' behaviour of several 'strong' types. */
+
+#ifndef CONVERTIBLE_THROUGH_BASE_HPP
+#define CONVERTIBLE_THROUGH_BASE_HPP
+
+/**
+ * A type is considered 'convertible through base()' when it has a 'base()'
+ * function that returns something that can be converted to int64_t.
+ * @tparam T The type under consideration.
+ */
+template
+concept ConvertibleThroughBase = requires(T const a) {
+ typename T::BaseType;
+ { a.base() } noexcept -> std::convertible_to;
+};
+
+#endif /* CONVERTIBLE_THROUGH_BASE_HPP */
diff --git a/src/core/format.hpp b/src/core/format.hpp
index d0267e4394..a00dab44ea 100644
--- a/src/core/format.hpp
+++ b/src/core/format.hpp
@@ -11,11 +11,11 @@
#define FORMAT_HPP
#include "../3rdparty/fmt/format.h"
-#include "strong_typedef_type.hpp"
+#include "convertible_through_base.hpp"
-template
-struct fmt::formatter::value>> : fmt::formatter::type> {
- using underlying_type = typename std::underlying_type::type;
+template requires std::is_enum_v
+struct fmt::formatter : fmt::formatter> {
+ using underlying_type = typename std::underlying_type_t;
using parent = typename fmt::formatter;
constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context &ctx)
@@ -23,14 +23,14 @@ struct fmt::formatter::value>> : fmt::
return parent::parse(ctx);
}
- fmt::format_context::iterator format(const E &e, format_context &ctx) const
+ fmt::format_context::iterator format(const E &e, fmt::format_context &ctx) const
{
return parent::format(underlying_type(e), ctx);
}
};
-template
-struct fmt::formatter::value>> : fmt::formatter {
+template
+struct fmt::formatter : fmt::formatter {
using underlying_type = typename T::BaseType;
using parent = typename fmt::formatter;
@@ -39,7 +39,7 @@ struct fmt::formatter::value, int> = 0>
-constexpr To ClampTo(From value)
+template
+constexpr To ClampTo(ConvertibleThroughBase auto value)
{
return ClampTo(value.base());
}
@@ -264,16 +264,13 @@ constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
* @param max The maximum of the interval
* @see IsInsideBS()
*/
-template , std::is_base_of>, int> = 0>
-constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
+constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
{
- if constexpr (std::is_base_of_v) {
- return static_cast(x.base() - min) < (max - min);
- } else {
- return static_cast(x - min) < (max - min);
- }
+ return static_cast(x - min) < (max - min);
}
+constexpr bool IsInsideMM(const ConvertibleThroughBase auto x, const size_t min, const size_t max) noexcept { return IsInsideMM(x.base(), min, max); }
+
/**
* Type safe swap operation
* @param a variable to swap with b
diff --git a/src/core/strong_typedef_type.hpp b/src/core/strong_typedef_type.hpp
index 2b929430b6..fe5eda2127 100644
--- a/src/core/strong_typedef_type.hpp
+++ b/src/core/strong_typedef_type.hpp
@@ -160,7 +160,7 @@ namespace StrongType {
constexpr Typedef &operator =(Typedef &&rhs) { this->value = std::move(rhs.value); return *this; }
/* Only allow conversion to BaseType via method. */
- constexpr TBaseType base() const { return this->value; }
+ constexpr TBaseType base() const noexcept { return this->value; }
/* Only allow TProperties classes access to the internal value. Everyone else needs to call .base(). */
friend struct Compare;
diff --git a/src/misc/endian_buffer.hpp b/src/misc/endian_buffer.hpp
index ba59d4fd14..f8ee5ade64 100644
--- a/src/misc/endian_buffer.hpp
+++ b/src/misc/endian_buffer.hpp
@@ -15,8 +15,6 @@
#include "../core/enum_type.hpp"
#include "../core/overflowsafe_type.hpp"
-struct StrongTypedefBase;
-
/**
* Endian-aware buffer adapter that always writes values in little endian order.
* @note This class uses operator overloading (<<, just like streams) for writing
@@ -50,13 +48,17 @@ public:
return *this;
}
- template >, std::is_base_of>, int> = 0>
+ EndianBufferWriter &operator <<(const ConvertibleThroughBase auto data)
+ {
+ this->Write(data.base());
+ return *this;
+ }
+
+ template requires (!std::is_class_v)
EndianBufferWriter &operator <<(const T data)
{
if constexpr (std::is_enum_v) {
this->Write(to_underlying(data));
- } else if constexpr (std::is_base_of_v) {
- this->Write(data.base());
} else {
this->Write(data);
}
@@ -147,13 +149,18 @@ public:
return *this;
}
- template >, std::is_base_of>, int> = 0>
+ template
+ EndianBufferReader &operator >>(T &data)
+ {
+ data = T{this->Read()};
+ return *this;
+ }
+
+ template requires (!std::is_class_v)
EndianBufferReader &operator >>(T &data)
{
if constexpr (std::is_enum_v) {
data = static_cast(this->Read>());
- } else if constexpr (std::is_base_of_v) {
- data = T{this->Read()};
} else {
data = this->Read();
}
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index 9f5befb6ea..b9996cbac4 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -1949,8 +1949,7 @@ static bool OrderConditionCompare(OrderConditionComparator occ, int variable, in
}
}
-template ::value, int> = 0>
-static bool OrderConditionCompare(OrderConditionComparator occ, T variable, int value)
+static bool OrderConditionCompare(OrderConditionComparator occ, ConvertibleThroughBase auto variable, int value)
{
return OrderConditionCompare(occ, variable.base(), value);
}
diff --git a/src/strings_func.h b/src/strings_func.h
index 6a43472db1..23a5eb3ce8 100644
--- a/src/strings_func.h
+++ b/src/strings_func.h
@@ -14,7 +14,7 @@
#include "string_type.h"
#include "gfx_type.h"
#include "core/bitmath_func.hpp"
-#include "core/strong_typedef_type.hpp"
+#include "core/convertible_through_base.hpp"
#include "vehicle_type.h"
/**
@@ -83,14 +83,12 @@ void SetDParam(size_t n, uint64_t v);
void SetDParamMaxValue(size_t n, uint64_t max_value, uint min_count = 0, FontSize size = FS_NORMAL);
void SetDParamMaxDigits(size_t n, uint count, FontSize size = FS_NORMAL);
-template ::value, int> = 0>
-void SetDParam(size_t n, T v)
+void SetDParam(size_t n, ConvertibleThroughBase auto v)
{
SetDParam(n, v.base());
}
-template ::value, int> = 0>
-void SetDParamMaxValue(size_t n, T max_value, uint min_count = 0, FontSize size = FS_NORMAL)
+void SetDParamMaxValue(size_t n, ConvertibleThroughBase auto max_value, uint min_count = 0, FontSize size = FS_NORMAL)
{
SetDParamMaxValue(n, max_value.base(), min_count, size);
}
diff --git a/src/strings_internal.h b/src/strings_internal.h
index b710be23e5..9462a1c984 100644
--- a/src/strings_internal.h
+++ b/src/strings_internal.h
@@ -167,8 +167,7 @@ public:
this->parameters[n].data = v;
}
- template ::value, int> = 0>
- void SetParam(size_t n, T v)
+ void SetParam(size_t n, ConvertibleThroughBase auto v)
{
SetParam(n, v.base());
}
diff --git a/src/window_func.h b/src/window_func.h
index 8dfbe69bae..2bd314db99 100644
--- a/src/window_func.h
+++ b/src/window_func.h
@@ -12,16 +12,15 @@
#include "window_type.h"
#include "company_type.h"
+#include "core/convertible_through_base.hpp"
#include "core/geometry_type.hpp"
-#include "core/strong_typedef_type.hpp"
Window *FindWindowById(WindowClass cls, WindowNumber number);
Window *FindWindowByClass(WindowClass cls);
Window *GetMainWindow();
void ChangeWindowOwner(Owner old_owner, Owner new_owner);
-template ::value, int> = 0>
-Window *FindWindowById(WindowClass cls, T number)
+Window *FindWindowById(WindowClass cls, ConvertibleThroughBase auto number)
{
return FindWindowById(cls, number.base());
}
@@ -44,8 +43,7 @@ void InputLoop();
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data = 0, bool gui_scope = false);
void InvalidateWindowClassesData(WindowClass cls, int data = 0, bool gui_scope = false);
-template ::value, int> = 0>
-void InvalidateWindowData(WindowClass cls, T number, int data = 0, bool gui_scope = false)
+void InvalidateWindowData(WindowClass cls, ConvertibleThroughBase auto number, int data = 0, bool gui_scope = false)
{
InvalidateWindowData(cls, number.base(), data, gui_scope);
}
@@ -67,8 +65,7 @@ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_
void SetWindowDirty(WindowClass cls, WindowNumber number);
void SetWindowClassesDirty(WindowClass cls);
-template ::value, int> = 0>
-void SetWindowDirty(WindowClass cls, T number)
+void SetWindowDirty(WindowClass cls, ConvertibleThroughBase auto number)
{
SetWindowDirty(cls, number.base());
}
@@ -76,8 +73,7 @@ void SetWindowDirty(WindowClass cls, T number)
void CloseWindowById(WindowClass cls, WindowNumber number, bool force = true, int data = 0);
void CloseWindowByClass(WindowClass cls, int data = 0);
-template ::value, int> = 0>
-void CloseWindowById(WindowClass cls, T number, bool force = true, int data = 0)
+void CloseWindowById(WindowClass cls, ConvertibleThroughBase auto number, bool force = true, int data = 0)
{
CloseWindowById(cls, number.base(), force, data);
}
diff --git a/src/window_gui.h b/src/window_gui.h
index 11722365a6..5a47a21517 100644
--- a/src/window_gui.h
+++ b/src/window_gui.h
@@ -348,8 +348,7 @@ public:
void CreateNestedTree();
void FinishInitNested(WindowNumber window_number = 0);
- template ::value, int> = 0>
- void FinishInitNested(T number)
+ void FinishInitNested(ConvertibleThroughBase auto number)
{
this->FinishInitNested(number.base());
}
@@ -996,8 +995,7 @@ public:
Window *BringWindowToFrontById(WindowClass cls, WindowNumber number);
Window *FindWindowFromPt(int x, int y);
-template ::value, int> = 0>
-Window *BringWindowToFrontById(WindowClass cls, T number)
+Window *BringWindowToFrontById(WindowClass cls, ConvertibleThroughBase auto number)
{
return BringWindowToFrontById(cls, number.base());
}