1
0
Fork 0

Codechange: add concept of ConvertibleThroughBase for strong types

This makes it possible to write templated alternatives for ConvertibleThroughBase
which are then available for any (strong) type that implements a base() function
In the end making adding new ConvertibleThroughBase types less awkward.
pull/13433/head
Rubidium 2025-02-01 12:57:01 +01:00 committed by rubidium42
parent 3e747397f0
commit 55588b052e
11 changed files with 68 additions and 49 deletions

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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 <typename T>
concept ConvertibleThroughBase = requires(T const a) {
typename T::BaseType;
{ a.base() } noexcept -> std::convertible_to<int64_t>;
};
#endif /* CONVERTIBLE_THROUGH_BASE_HPP */

View File

@ -11,11 +11,11 @@
#define FORMAT_HPP
#include "../3rdparty/fmt/format.h"
#include "strong_typedef_type.hpp"
#include "convertible_through_base.hpp"
template <typename E, typename Char>
struct fmt::formatter<E, Char, std::enable_if_t<std::is_enum<E>::value>> : fmt::formatter<typename std::underlying_type<E>::type> {
using underlying_type = typename std::underlying_type<E>::type;
template <typename E, typename Char> requires std::is_enum_v<E>
struct fmt::formatter<E, Char> : fmt::formatter<typename std::underlying_type_t<E>> {
using underlying_type = typename std::underlying_type_t<E>;
using parent = typename fmt::formatter<underlying_type>;
constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context &ctx)
@ -23,14 +23,14 @@ struct fmt::formatter<E, Char, std::enable_if_t<std::is_enum<E>::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 <typename T, typename Char>
struct fmt::formatter<T, Char, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::value>> : fmt::formatter<typename T::BaseType> {
template <ConvertibleThroughBase T, typename Char>
struct fmt::formatter<T, Char> : fmt::formatter<typename T::BaseType> {
using underlying_type = typename T::BaseType;
using parent = typename fmt::formatter<underlying_type>;
@ -39,7 +39,7 @@ struct fmt::formatter<T, Char, std::enable_if_t<std::is_base_of<StrongTypedefBas
return parent::parse(ctx);
}
fmt::format_context::iterator format(const T &t, format_context &ctx) const
fmt::format_context::iterator format(const T &t, fmt::format_context &ctx) const
{
return parent::format(t.base(), ctx);
}

View File

@ -10,7 +10,7 @@
#ifndef MATH_FUNC_HPP
#define MATH_FUNC_HPP
#include "strong_typedef_type.hpp"
#include "convertible_through_base.hpp"
/**
* Returns the absolute value of (scalar) variable.
@ -217,8 +217,8 @@ constexpr To ClampTo(From value)
/**
* Specialization of ClampTo for #StrongType::Typedef.
*/
template <typename To, typename From, std::enable_if_t<std::is_base_of<StrongTypedefBase, From>::value, int> = 0>
constexpr To ClampTo(From value)
template <typename To>
constexpr To ClampTo(ConvertibleThroughBase auto value)
{
return ClampTo<To>(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 <typename T, std::enable_if_t<std::disjunction_v<std::is_convertible<T, size_t>, std::is_base_of<StrongTypedefBase, T>>, 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<StrongTypedefBase, T>) {
return static_cast<size_t>(x.base() - min) < (max - min);
} else {
return static_cast<size_t>(x - min) < (max - min);
}
return static_cast<size_t>(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

View File

@ -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;

View File

@ -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 <class T, std::enable_if_t<std::disjunction_v<std::negation<std::is_class<T>>, std::is_base_of<StrongTypedefBase, T>>, int> = 0>
EndianBufferWriter &operator <<(const ConvertibleThroughBase auto data)
{
this->Write(data.base());
return *this;
}
template <class T> requires (!std::is_class_v<T>)
EndianBufferWriter &operator <<(const T data)
{
if constexpr (std::is_enum_v<T>) {
this->Write(to_underlying(data));
} else if constexpr (std::is_base_of_v<StrongTypedefBase, T>) {
this->Write(data.base());
} else {
this->Write(data);
}
@ -147,13 +149,18 @@ public:
return *this;
}
template <class T, std::enable_if_t<std::disjunction_v<std::negation<std::is_class<T>>, std::is_base_of<StrongTypedefBase, T>>, int> = 0>
template <ConvertibleThroughBase T>
EndianBufferReader &operator >>(T &data)
{
data = T{this->Read<typename T::BaseType>()};
return *this;
}
template <class T> requires (!std::is_class_v<T>)
EndianBufferReader &operator >>(T &data)
{
if constexpr (std::is_enum_v<T>) {
data = static_cast<T>(this->Read<std::underlying_type_t<T>>());
} else if constexpr (std::is_base_of_v<StrongTypedefBase, T>) {
data = T{this->Read<typename T::BaseType>()};
} else {
data = this->Read<T>();
}

View File

@ -1949,8 +1949,7 @@ static bool OrderConditionCompare(OrderConditionComparator occ, int variable, in
}
}
template <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::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);
}

View File

@ -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 <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::value, int> = 0>
void SetDParam(size_t n, T v)
void SetDParam(size_t n, ConvertibleThroughBase auto v)
{
SetDParam(n, v.base());
}
template <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::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);
}

View File

@ -167,8 +167,7 @@ public:
this->parameters[n].data = v;
}
template <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::value, int> = 0>
void SetParam(size_t n, T v)
void SetParam(size_t n, ConvertibleThroughBase auto v)
{
SetParam(n, v.base());
}

View File

@ -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 <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::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 <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::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 <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::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 <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::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);
}

View File

@ -348,8 +348,7 @@ public:
void CreateNestedTree();
void FinishInitNested(WindowNumber window_number = 0);
template <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::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 <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::value, int> = 0>
Window *BringWindowToFrontById(WindowClass cls, T number)
Window *BringWindowToFrontById(WindowClass cls, ConvertibleThroughBase auto number)
{
return BringWindowToFrontById(cls, number.base());
}