Codechange: Make TileIndex a "strong" typedef to give it a distinct type.

This is accomplished by changing it to a single member struct with the
appropriate operator overloads to make it all work with not too much
source modifications.
pull/353/head
Michael Lutz 3 years ago
parent 4fc055d6e9
commit b0990fcff7

@ -1081,7 +1081,7 @@ struct BuildVehicleWindow : Window {
{
this->vehicle_type = type;
this->listview_mode = tile == INVALID_TILE;
this->window_number = this->listview_mode ? (int)type : tile;
this->window_number = this->listview_mode ? (int)type : (int)tile;
this->sel_engine = INVALID_ENGINE;
@ -1116,7 +1116,7 @@ struct BuildVehicleWindow : Window {
this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
this->FinishInitNested(tile == INVALID_TILE ? (int)type : tile);
this->FinishInitNested(tile == INVALID_TILE ? (int)type : (int)tile);
this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
@ -1683,7 +1683,7 @@ void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
* so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
* As it always is a low value, it won't collide with any real tile
* number. */
uint num = (tile == INVALID_TILE) ? (int)type : tile;
uint num = (tile == INVALID_TILE) ? (int)type : (int)tile;
assert(IsCompanyBuildableVehicleType(type));

@ -75,7 +75,7 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
source_id(source_id),
source(source),
source_xy(source_xy),
loaded_at_xy(loaded_at_xy)
loaded_at_xy(loaded_at_xy.value)
{
assert(count != 0);
this->source_type = source_type;

@ -27,7 +27,7 @@ void UpdateLandscapingLimits();
bool CheckCompanyHasMoney(CommandCost &cost);
void SubtractMoneyFromCompany(const CommandCost& cost);
void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost& cost);
CommandCost CheckOwnership(Owner owner, TileIndex tile = 0);
CommandCost CheckOwnership(Owner owner, TileIndex tile = 0U);
CommandCost CheckTileOwnership(TileIndex tile);
extern CompanyID _local_company;

@ -2576,11 +2576,11 @@ struct CompanyWindow : Window
break;
case WID_C_BUY_SHARE:
DoCommandP(CMD_BUY_SHARE_IN_COMPANY, STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS, 0, this->window_number, 0);
DoCommandP(CMD_BUY_SHARE_IN_COMPANY, STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS, (TileIndex)0, this->window_number, 0);
break;
case WID_C_SELL_SHARE:
DoCommandP(CMD_SELL_SHARE_IN_COMPANY, STR_ERROR_CAN_T_SELL_25_SHARE_IN, 0, this->window_number, 0);
DoCommandP(CMD_SELL_SHARE_IN_COMPANY, STR_ERROR_CAN_T_SELL_25_SHARE_IN, (TileIndex)0, this->window_number, 0);
break;
case WID_C_COMPANY_PASSWORD:
@ -2771,7 +2771,7 @@ struct BuyCompanyWindow : Window {
break;
case WID_BC_YES:
DoCommandP(CMD_BUY_COMPANY, STR_ERROR_CAN_T_BUY_COMPANY, 0, this->window_number, 0);
DoCommandP(CMD_BUY_COMPANY, STR_ERROR_CAN_T_BUY_COMPANY, (TileIndex)0, this->window_number, 0);
break;
}
}

@ -28,4 +28,5 @@ add_files(
smallvec_type.hpp
span_type.hpp
string_compare_type.hpp
strong_typedef_type.hpp
)

@ -0,0 +1,73 @@
/*
* 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 strong_typedef_type.hpp Type (helpers) for making a strong typedef that is a distinct type. */
#ifndef STRONG_TYPEDEF_TYPE_HPP
#define STRONG_TYPEDEF_TYPE_HPP
/** Non-templated base for #StrongTypedef for use with type trait queries. */
struct StrongTypedefBase {};
/**
* Templated helper to make a type-safe 'typedef' representing a single POD value.
* A normal 'typedef' is not distinct from its base type and will be treated as
* identical in many contexts. This class provides a distinct type that can still
* be assign from and compared to values of its base type.
*
* @note This is meant to be used as a base class, not directly.
* @tparam T Storage type
* @tparam Tthis Type of the derived class (i.e. the concrete usage of this class).
*/
template <class T, class Tthis>
struct StrongTypedef : StrongTypedefBase {
using Type = T;
T value{}; ///< Backing storage field.
constexpr StrongTypedef() = default;
constexpr StrongTypedef(const StrongTypedef &o) = default;
constexpr StrongTypedef(StrongTypedef &&o) = default;
constexpr StrongTypedef(const T &value) : value(value) {}
constexpr Tthis &operator =(const StrongTypedef &rhs) { this->value = rhs.value; return static_cast<Tthis &>(*this); }
constexpr Tthis &operator =(StrongTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast<Tthis &>(*this); }
constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast<Tthis &>(*this); }
explicit constexpr operator T() const { return this->value; }
constexpr bool operator ==(const StrongTypedef &rhs) const { return this->value == rhs.value; }
constexpr bool operator !=(const StrongTypedef &rhs) const { return this->value != rhs.value; }
constexpr bool operator ==(const T &rhs) const { return this->value == rhs; }
constexpr bool operator !=(const T &rhs) const { return this->value != rhs; }
};
/**
* Extension of #StrongTypedef with operators for addition and subtraction.
* @tparam T Storage type
* @tparam Tthis Type of the derived class (i.e. the concrete usage of this class).
*/
template <class T, class Tthis>
struct StrongIntegralTypedef : StrongTypedef<T, Tthis> {
using StrongTypedef<T, Tthis>::StrongTypedef;
constexpr Tthis &operator ++() { this->value++; return static_cast<Tthis &>(*this); }
constexpr Tthis &operator --() { this->value--; return static_cast<Tthis &>(*this); }
constexpr Tthis operator ++(int) { auto res = static_cast<Tthis &>(*this); this->value++; return res; }
constexpr Tthis operator --(int) { auto res = static_cast<Tthis &>(*this); this->value--; return res; }
constexpr Tthis &operator +=(const Tthis &rhs) { this->value += rhs.value; return *static_cast<Tthis *>(this); }
constexpr Tthis &operator -=(const Tthis &rhs) { this->value -= rhs.value; return *static_cast<Tthis *>(this); }
constexpr Tthis operator +(const Tthis &rhs) const { return Tthis{ this->value + rhs.value }; }
constexpr Tthis operator -(const Tthis &rhs) const { return Tthis{ this->value - rhs.value }; }
constexpr Tthis operator +(const T &rhs) const { return Tthis{ this->value + rhs }; }
constexpr Tthis operator -(const T &rhs) const { return Tthis{ this->value - rhs }; }
};
#endif /* STRONG_TYPEDEF_TYPE_HPP */

@ -941,7 +941,7 @@ void ReleaseDisastersTargetingIndustry(IndustryID i)
/* primary disaster vehicles that have chosen target */
if (v->subtype == ST_AIRPLANE || v->subtype == ST_HELICOPTER) {
/* if it has chosen target, and it is this industry (yes, dest_tile is IndustryID here), set order to "leaving map peacefully" */
if (v->current_order.GetDestination() > 0 && v->dest_tile == i) v->current_order.SetDestination(3);
if (v->current_order.GetDestination() > 0 && v->dest_tile == (uint32)i) v->current_order.SetDestination(3);
}
}
}

@ -641,7 +641,7 @@ public:
if (confirmed) {
VehicleGroupWindow *w = (VehicleGroupWindow*)win;
w->vli.index = ALL_GROUP;
DoCommandP(CMD_DELETE_GROUP, STR_ERROR_GROUP_CAN_T_DELETE, 0, w->group_confirm, 0);
DoCommandP(CMD_DELETE_GROUP, STR_ERROR_GROUP_CAN_T_DELETE, (TileIndex)0, w->group_confirm, 0);
}
}
@ -965,7 +965,7 @@ public:
case ADI_REMOVE_ALL: // Remove all Vehicles from the selected group
assert(Group::IsValidID(this->vli.index));
DoCommandP(CMD_REMOVE_ALL_VEHICLES_GROUP, STR_ERROR_GROUP_CAN_T_REMOVE_ALL_VEHICLES, 0, this->vli.index, 0);
DoCommandP(CMD_REMOVE_ALL_VEHICLES_GROUP, STR_ERROR_GROUP_CAN_T_REMOVE_ALL_VEHICLES, (TileIndex)0, this->vli.index, 0);
break;
default: NOT_REACHED();
}

@ -84,7 +84,7 @@ TileIndex TileAdd(TileIndex tile, TileIndexDiff add,
char buf[512];
seprintf(buf, lastof(buf), "TILE_ADD(%s) when adding 0x%.4X and 0x%.4X failed",
exp, tile, add);
exp, (uint32)tile, add);
#if !defined(_MSC_VER)
fprintf(stderr, "%s:%d %s\n", file, line, buf);
#else

@ -204,7 +204,7 @@ static inline TileIndex TileVirtXY(uint x, uint y)
*/
static inline uint TileX(TileIndex tile)
{
return tile & MapMaxX();
return tile.value & MapMaxX();
}
/**
@ -214,7 +214,7 @@ static inline uint TileX(TileIndex tile)
*/
static inline uint TileY(TileIndex tile)
{
return tile >> MapLogX();
return tile.value >> MapLogX();
}
/**

@ -18,7 +18,7 @@
/* virtual */ uint32 RailTypeScopeResolver::GetRandomBits() const
{
uint tmp = CountBits(this->tile + (TileX(this->tile) + TileY(this->tile)) * TILE_SIZE);
uint tmp = CountBits(static_cast<uint32>(this->tile + (TileX(this->tile) + TileY(this->tile)) * TILE_SIZE));
return GB(tmp, 0, 2);
}

@ -18,7 +18,7 @@
/* virtual */ uint32 RoadTypeScopeResolver::GetRandomBits() const
{
uint tmp = CountBits(this->tile + (TileX(this->tile) + TileY(this->tile)) * TILE_SIZE);
uint tmp = CountBits(static_cast<uint32>(this->tile + (TileX(this->tile) + TileY(this->tile)) * TILE_SIZE));
return GB(tmp, 0, 2);
}

@ -311,7 +311,7 @@ struct NewsWindow : Window {
/* Initialize viewport if it exists. */
NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_N_VIEWPORT);
if (nvp != nullptr) {
nvp->InitializeViewport(this, ni->reftype1 == NR_VEHICLE ? 0x80000000 | ni->ref1 : GetReferenceTile(ni->reftype1, ni->ref1), ZOOM_LVL_NEWS);
nvp->InitializeViewport(this, ni->reftype1 == NR_VEHICLE ? 0x80000000 | ni->ref1 : (uint32)GetReferenceTile(ni->reftype1, ni->ref1), ZOOM_LVL_NEWS);
if (this->ni->flags & NF_NO_TRANSPARENT) nvp->disp_flags |= ND_NO_TRANSPARENCY;
if ((this->ni->flags & NF_INCOLOUR) == 0) {
nvp->disp_flags |= ND_SHADE_GREY;

@ -1343,7 +1343,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
if (_cargo_count != 0 && CargoPacket::CanAllocateItem()) {
StationID source = (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
TileIndex source_xy = (source != INVALID_STATION) ? Station::Get(source)->xy : 0;
TileIndex source_xy = (source != INVALID_STATION) ? Station::Get(source)->xy : (TileIndex)0;
v->cargo.Append(new CargoPacket(_cargo_count, _cargo_days, source, source_xy, source_xy));
}
}

@ -238,7 +238,7 @@
EnforcePrecondition(false, company != COMPANY_INVALID);
/* Network commands only allow 0 to indicate invalid tiles, not INVALID_TILE */
return ScriptObject::DoCommand(tile == INVALID_TILE ? 0 : tile , (uint32)(delta), company | expenses_type << 8 , CMD_CHANGE_BANK_BALANCE);
return ScriptObject::DoCommand(tile == INVALID_TILE ? (TileIndex)0U : tile , (uint32)(delta), company | expenses_type << 8 , CMD_CHANGE_BANK_BALANCE);
}
/* static */ bool ScriptCompany::BuildCompanyHQ(TileIndex tile)

@ -82,6 +82,7 @@
#include "../../core/overflowsafe_type.hpp"
#include "../../company_type.h"
#include "../../tile_type.h"
#include <squirrel.h>
/* Define all types here, so we don't have to include the whole _type.h maze */
@ -100,7 +101,6 @@ typedef uint32 StringID; ///< The ID of a string.
typedef uint16 SubsidyID; ///< The ID of a subsidy.
typedef uint16 StoryPageID; ///< The ID of a story page.
typedef uint16 StoryPageElementID; ///< The ID of a story page element.
typedef uint32 TileIndex; ///< The ID of a tile (just named differently).
typedef uint16 TownID; ///< The ID of a town.
typedef uint32 VehicleID; ///< The ID of a vehicle.

@ -14,6 +14,7 @@
#include "../core/smallvec_type.hpp"
#include "../economy_type.h"
#include "../string_func.h"
#include "../tile_type.h"
#include "squirrel_helper_type.hpp"
template <class CL, ScriptType ST> const char *GetClassName();
@ -85,6 +86,7 @@ namespace SQConvert {
template <> inline int Return<int32> (HSQUIRRELVM vm, int32 res) { sq_pushinteger(vm, res); return 1; }
template <> inline int Return<int64> (HSQUIRRELVM vm, int64 res) { sq_pushinteger(vm, res); return 1; }
template <> inline int Return<Money> (HSQUIRRELVM vm, Money res) { sq_pushinteger(vm, res); return 1; }
template <> inline int Return<TileIndex> (HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32)res.value); return 1; }
template <> inline int Return<bool> (HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; }
template <> inline int Return<char *> (HSQUIRRELVM vm, char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); free(res); } return 1; }
template <> inline int Return<const char *>(HSQUIRRELVM vm, const char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); } return 1; }
@ -103,6 +105,7 @@ namespace SQConvert {
template <> inline int16 GetParam(ForceType<int16> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
template <> inline int32 GetParam(ForceType<int32> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
template <> inline int64 GetParam(ForceType<int64> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
template <> inline TileIndex GetParam(ForceType<TileIndex> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return TileIndex((uint32)(int32)tmp); }
template <> inline Money GetParam(ForceType<Money> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
template <> inline bool GetParam(ForceType<bool> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; }
template <> inline void *GetParam(ForceType<void *> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; }

@ -10,6 +10,8 @@
#ifndef TILE_TYPE_H
#define TILE_TYPE_H
#include "core/strong_typedef_type.hpp"
static const uint TILE_SIZE = 16; ///< Tile size in world coordinates.
static const uint TILE_UNIT_MASK = TILE_SIZE - 1; ///< For masking in/out the inner-tile world coordinate units.
static const uint TILE_PIXELS = 32; ///< Pixel distance between tile columns/rows in #ZOOM_LVL_BASE.
@ -80,11 +82,29 @@ enum TropicZone {
/**
* The index/ID of a Tile.
*/
typedef uint32 TileIndex;
struct TileIndex : StrongIntegralTypedef<uint32, TileIndex> {
using StrongIntegralTypedef<uint32, TileIndex>::StrongIntegralTypedef;
/** Implicit conversion to the base type for e.g. array indexing. */
constexpr operator uint32() const { return this->value; }
/* Import operators from the base class into our overload set. */
using StrongIntegralTypedef::operator ==;
using StrongIntegralTypedef::operator !=;
using StrongIntegralTypedef::operator +;
using StrongIntegralTypedef::operator -;
/* Add comparison and add/sub for signed ints as e.g. 0 is signed and will
* match ambiguously when only unsigned overloads are present. */
constexpr bool operator ==(int rhs) const { return this->value == (uint32)rhs; }
constexpr bool operator !=(int rhs) const { return this->value != (uint32)rhs; }
constexpr TileIndex operator +(int rhs) const { return { (uint32)(this->value + rhs) }; }
constexpr TileIndex operator -(int rhs) const { return { (uint32)(this->value - rhs) }; }
};
/**
* The very nice invalid tile marker
*/
static const TileIndex INVALID_TILE = (TileIndex)-1;
static inline constexpr TileIndex INVALID_TILE = TileIndex{ (uint32)-1 };
#endif /* TILE_TYPE_H */

@ -147,6 +147,36 @@ public:
* Allocate a new iterator that is a copy of this one.
*/
virtual TileIterator *Clone() const = 0;
/**
* Equality comparison.
*/
bool operator ==(const TileIterator &rhs) const
{
return this->tile == rhs.tile;
}
/**
* Inequality comparison.
*/
bool operator !=(const TileIterator &rhs) const
{
return this->tile != rhs.tile;
}
/**
* Equality comparison.
*/
bool operator ==(const TileIndex &rhs) const
{
return this->tile == rhs;
}
/**
* Inequality comparison.
*/
bool operator !=(const TileIndex &rhs) const
{
return this->tile != rhs;
}
};
/** Iterator to iterate over a tile area (rectangle) of the map. */

@ -578,25 +578,25 @@ struct TimetableWindow : Window {
case WID_VT_CLEAR_TIME: { // Clear waiting time.
uint32 p1 = PackTimetableArgs(v, this->sel_index, false);
DoCommandP(CMD_CHANGE_TIMETABLE, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, 0, p1, 0);
DoCommandP(CMD_CHANGE_TIMETABLE, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, (TileIndex)0, p1, 0);
break;
}
case WID_VT_CLEAR_SPEED: { // Clear max speed button.
uint32 p1 = PackTimetableArgs(v, this->sel_index, true);
DoCommandP(CMD_CHANGE_TIMETABLE, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, 0, p1, UINT16_MAX);
DoCommandP(CMD_CHANGE_TIMETABLE, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, (TileIndex)0, p1, UINT16_MAX);
break;
}
case WID_VT_RESET_LATENESS: // Reset the vehicle's late counter.
DoCommandP(CMD_SET_VEHICLE_ON_TIME, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, 0, v->index, 0);
DoCommandP(CMD_SET_VEHICLE_ON_TIME, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, (TileIndex)0, v->index, 0);
break;
case WID_VT_AUTOFILL: { // Autofill the timetable.
uint32 p2 = 0;
if (!HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) SetBit(p2, 0);
if (_ctrl_pressed) SetBit(p2, 1);
DoCommandP(CMD_AUTOFILL_TIMETABLE, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, 0, v->index, p2);
DoCommandP(CMD_AUTOFILL_TIMETABLE, STR_ERROR_CAN_T_TIMETABLE_VEHICLE, (TileIndex)0, v->index, p2);
break;
}

@ -474,12 +474,12 @@ public:
_warn_town_no_roads = true;
}
DoCommandP(CMD_EXPAND_TOWN, STR_ERROR_CAN_T_EXPAND_TOWN, 0, this->window_number, 0);
DoCommandP(CMD_EXPAND_TOWN, STR_ERROR_CAN_T_EXPAND_TOWN, (TileIndex)0, this->window_number, 0);
break;
}
case WID_TV_DELETE: // delete town - only available on Scenario editor
DoCommandP(CMD_DELETE_TOWN, STR_ERROR_TOWN_CAN_T_DELETE, 0, this->window_number, 0);
DoCommandP(CMD_DELETE_TOWN, STR_ERROR_TOWN_CAN_T_DELETE, (TileIndex)0, this->window_number, 0);
break;
}
}

@ -399,7 +399,7 @@ CommandCost CmdPlantTree(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32
Company *c = (_game_mode != GM_EDITOR) ? Company::GetIfValid(_current_company) : nullptr;
int limit = (c == nullptr ? INT32_MAX : GB(c->tree_limit, 16, 16));
TileArea ta(tile, p2);
TileArea ta(tile, (TileIndex)p2);
for (TileIndex current_tile : ta) {
switch (GetTileType(current_tile)) {
case MP_TREES:
@ -528,7 +528,7 @@ static void DrawTile_Trees(TileInfo *ti)
/* Do not draw trees when the invisible trees setting is set */
if (IsInvisibilitySet(TO_TREES)) return;
uint tmp = CountBits(ti->tile + ti->x + ti->y);
uint tmp = CountBits(static_cast<uint32>(ti->tile + ti->x + ti->y));
uint index = GB(tmp, 0, 2) + (GetTreeType(ti->tile) << 2);
/* different tree styles above one of the grounds */

@ -454,7 +454,7 @@ CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32
/* Outside the editor you can only drag canals, and not areas */
if (_game_mode != GM_EDITOR) {
TileArea ta(tile, p1);
TileArea ta(tile, (TileIndex)p1);
if (ta.w != 1 && ta.h != 1) return CMD_ERROR;
}
@ -462,9 +462,9 @@ CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32
std::unique_ptr<TileIterator> iter;
if (HasBit(p2, 2)) {
iter = std::make_unique<DiagonalTileIterator>(tile, p1);
iter = std::make_unique<DiagonalTileIterator>(tile, (TileIndex)p1);
} else {
iter = std::make_unique<OrthogonalTileIterator>(tile, p1);
iter = std::make_unique<OrthogonalTileIterator>(tile, (TileIndex)p1);
}
for (; *iter != INVALID_TILE; ++(*iter)) {

Loading…
Cancel
Save