Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ jobs:
- name: Build
run: tools/ci/build.sh

- name: Test
run: tools/ci/test.sh

- run: tools/ci/collectCoverageData.sh && external/libutil/tools/ci/uploadCoverageData.sh
if: matrix.coverage && success()

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ add_subdirectory(libs)
add_subdirectory(extras)

find_package(Gettext REQUIRED)
file(GLOB RTTR_PO_FILES external/languages/*.po)
file(GLOB RTTR_PO_FILES CONFIGURE_DEPENDS external/languages/*.po)
set(RTTR_TRANSLATION_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen/languages) # Used by copyDepsToBuildDir
gettext_create_translations(external/languages/rttr.pot ALL
DESTINATION ${RTTR_TRANSLATION_OUTPUT}
Expand Down
8 changes: 4 additions & 4 deletions libs/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

set(RTTR_Assert_Enabled 2 CACHE STRING "Status of RTTR assertions: 0=Disabled, 1=Enabled, 2=Default(Enabled only in debug)")

file(GLOB COMMON_SRC src/*.cpp)
file(GLOB COMMON_HEADERS include/*.h include/*.hpp)
file(GLOB COMMON_HELPERS_SRC src/helpers/*.cpp)
file(GLOB COMMON_HELPERS_HEADERS include/helpers/*.h include/helpers/*.hpp)
file(GLOB COMMON_SRC CONFIGURE_DEPENDS src/*.cpp)
file(GLOB COMMON_HEADERS CONFIGURE_DEPENDS include/*.h include/*.hpp)
file(GLOB COMMON_HELPERS_SRC CONFIGURE_DEPENDS src/helpers/*.cpp)
file(GLOB COMMON_HELPERS_HEADERS CONFIGURE_DEPENDS include/helpers/*.h include/helpers/*.hpp)
set(ALL_SRC ${COMMON_SRC} ${COMMON_HEADERS} ${COMMON_HELPERS_SRC} ${COMMON_HELPERS_HEADERS})

source_group(src FILES ${COMMON_SRC} ${COMMON_HEADERS})
Expand Down
68 changes: 68 additions & 0 deletions libs/common/include/helpers/IdRange.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "RTTR_Assert.h"
#include "StrongId.h"
#include <iterator>
#include <limits>

namespace helpers {
/// Iterate over all valid strong Ids of a given container size
template<typename T>
class idRange;

template<typename T, typename U>
class idRange<StrongId<T, U>>
{
using id_t = StrongId<T, U>;
using underlying_t = typename id_t::underlying_t;
const underlying_t startValue_;
const underlying_t endValue_;

explicit idRange(underlying_t startValue, underlying_t count) : startValue_(startValue), endValue_(count + 1)
{
RTTR_Assert(count < std::numeric_limits<underlying_t>::max());
RTTR_Assert(startValue_ <= endValue_);
}

public:
/// Create range for container with given number of elements
explicit idRange(underlying_t count) : idRange(1, count) {}

class iterator
{
protected:
underlying_t value;

public:
using iterator_category = std::forward_iterator_tag;
using value_type = id_t;
using difference_type = std::make_signed_t<std::common_type_t<underlying_t, int>>;
using pointer = id_t*;
using reference = id_t&;

explicit constexpr iterator(underlying_t value) : value(value) {}
constexpr id_t operator*() const noexcept { return id_t(value); }
void operator++() noexcept { ++value; }
constexpr bool operator!=(iterator rhs) const noexcept { return value != rhs.value; }
};

iterator begin() const noexcept { return iterator(startValue_); }
iterator end() const noexcept { return iterator(endValue_); }

template<typename T_Id, typename U2>
friend constexpr idRange<T_Id> idRangeAfter(T_Id prev, U2 totalCount);
};

/// Return range starting after the given value
/// totalCount is the count of the full range not including the `prev` element
template<typename T, typename U>
constexpr idRange<T> idRangeAfter(T prev, U totalCount)
{
return idRange<T>(prev.next().value(), totalCount);
}

} // namespace helpers
1 change: 1 addition & 0 deletions libs/common/include/helpers/Range.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class range

class iterator
{
protected:
T value;

public:
Expand Down
54 changes: 54 additions & 0 deletions libs/common/include/helpers/StrongId.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <iosfwd>
#include <type_traits>

namespace helpers {

/// Template for declaring strong types used for IDs
/// Very thin wrapper mainly to distinguish different IDs.
/// The "invalid" value is just a marker and could be used as any other value.
/// StrongId can be used as indices into 1-based containers, i.e. the entry at index 0 is the invalid entry.
/// See `StrongIdVector` for avoiding that entry.
/// Usage: using MyId = StrongId<unsigned, struct MyIdTag>
template<class Underlying, class Tag>
class StrongId
{
static_assert(std::is_integral_v<Underlying> && std::is_unsigned_v<Underlying>);
static constexpr Underlying invalidValue_ = 0;

public:
using underlying_t = Underlying;

/// Factory function to get the invalid ID
static constexpr StrongId invalidValue() { return StrongId(invalidValue_); }

/// Construct an invalid ID
constexpr StrongId() : value_(invalidValue_){};
constexpr explicit StrongId(underlying_t value) : value_(value) {}

constexpr explicit operator bool() const noexcept { return isValid(); }
constexpr bool isValid() const noexcept { return value_ != invalidValue_; }

constexpr underlying_t value() const { return value_; }

/// Return the ID following this ID
constexpr StrongId next() noexcept { return StrongId(value_ + 1); }
/// Reset to invalid ID (default value)
constexpr void reset() noexcept { value_ = invalidValue_; }

explicit constexpr operator Underlying() const { return value_; }

friend constexpr bool operator==(const StrongId r, const StrongId l) { return r.value_ == l.value_; };
friend constexpr bool operator!=(const StrongId r, const StrongId l) { return r.value_ != l.value_; };

friend std::ostream& operator<<(std::ostream& os, const StrongId t) { return os << t.value_; }

private:
underlying_t value_;
};
} // namespace helpers
37 changes: 37 additions & 0 deletions libs/common/include/helpers/StrongIdVector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "RTTR_Assert.h"
#include "StrongId.h"
#include <vector>

namespace helpers {

/// Vector of T-elements (std::vector<T>) indexed by StrongId TIndex
template<typename T, typename TIndex>
struct StrongIdVector;

template<typename T, typename TIndex_U, typename TIndex_T>
struct StrongIdVector<T, StrongId<TIndex_U, TIndex_T>> : std::vector<T>
{
using Parent = std::vector<T>;
using index_t = StrongId<TIndex_U, TIndex_T>;

using Parent::Parent;

typename Parent::reference operator[](index_t id)
{
RTTR_Assert(id && id.value() <= this->size());
return Parent::operator[](id.value() - 1);
}
typename Parent::const_reference operator[](index_t id) const
{
RTTR_Assert(id && id.value() <= this->size());
return Parent::operator[](id.value() - 1);
}
};

} // namespace helpers
17 changes: 7 additions & 10 deletions libs/common/include/helpers/serializeContainers.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Copyright (C) 2005 - 2025 Settlers Freaks (sf-team at siedler25.org)
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "serializeEnums.h"
#include "underlyingIntegral.h"
#include "s25util/Serializer.h"
#include <type_traits>

Expand All @@ -28,18 +29,14 @@ namespace detail {
template<typename T>
std::enable_if_t<!std::is_enum_v<T>, T> popEnumOrIntegral(Serializer& ser)
{
return ser.Pop<T>();
using Integral = underlyingIntegral_t<T>;
return T(ser.Pop<Integral>());
}

template<typename T>
void pushContainer(Serializer& ser, const T& container, long)
{
using Type = typename T::value_type;
// Deactivated to bug in clang-tidy
// NOLINTBEGIN(modernize-type-traits)
using Integral =
typename std::conditional_t<std::is_enum_v<Type>, std::underlying_type<Type>, std::common_type<Type>>::type;
// NOLINTEND(modernize-type-traits)
using Integral = underlyingIntegral_t<typename T::value_type>;
for(const auto el : container)
{
// Cast also required for bool vector -.-
Expand Down Expand Up @@ -82,7 +79,7 @@ template<typename T>
void pushContainer(Serializer& ser, const T& container, bool ignoreSize = false)
{
using Type = typename T::value_type;
static_assert(std::is_integral_v<Type> || std::is_enum_v<Type>, "Only integral types and enums are possible");
static_assert(IsIntegralLike_v<Type>, "Only integral(-like) types are possible");

if(detail::isResizableContainer_v<T> && !ignoreSize)
ser.PushVarSize(container.size());
Expand All @@ -93,7 +90,7 @@ template<typename T>
void popContainer(Serializer& ser, T&& result, bool ignoreSize = false)
{
using Type = typename std::remove_reference_t<T>::value_type;
static_assert(std::is_integral_v<Type> || std::is_enum_v<Type>, "Only integral types and enums are possible");
static_assert(IsIntegralLike_v<Type>, "Only integral(-like) types are possible");

if(!ignoreSize)
detail::maybePopSizeAndResize(ser, result);
Expand Down
18 changes: 7 additions & 11 deletions libs/common/include/helpers/toString.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (C) 2005 - 2025 Settlers Freaks (sf-team at siedler25.org)
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "underlyingIntegral.h"
#include <string>
#include <type_traits>

Expand All @@ -16,18 +17,13 @@ std::string toString(const T value)
return std::to_string(value);
}

template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
template<typename T, std::enable_if_t<IsIntegralLike_v<T>, int> = 0>
std::string toString(const T value)
{
constexpr bool typeAtLeastInt = sizeof(T) >= sizeof(int);
using TargetType = std::conditional_t<typeAtLeastInt, T, std::conditional_t<std::is_signed_v<T>, int, unsigned>>;
return std::to_string(static_cast<TargetType>(value));
}

template<typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
std::string toString(const T value)
{
return toString(static_cast<std::underlying_type_t<T>>(value));
using U = underlyingIntegral_t<T>;
constexpr bool typeAtLeastInt = sizeof(U) >= sizeof(int);
using TargetType = std::conditional_t<typeAtLeastInt, U, std::conditional_t<std::is_signed_v<U>, int, unsigned>>;
return std::to_string(static_cast<TargetType>(static_cast<U>(value)));
}

} // namespace helpers
45 changes: 45 additions & 0 deletions libs/common/include/helpers/underlyingIntegral.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "serializeEnums.h"
#include <type_traits>

namespace helpers {

/// Get underlying type, i.e. the "storage" type of T
template<typename T, typename = void>
struct underlyingIntegral;
template<typename T>
struct underlyingIntegral<T, std::void_t<typename T::underlying_t>>
{
using type = typename T::underlying_t;
};
template<class T>
struct underlyingIntegral<T, std::enable_if_t<std::is_enum_v<T>>>
{
using type = std::underlying_type_t<T>;
};
template<class T>
struct underlyingIntegral<T, std::enable_if_t<std::is_integral_v<T>>>
{
using type = T;
};
template<typename T>
using underlyingIntegral_t = typename underlyingIntegral<T>::type;

/// Detect if the type is an integral, or has an underlying integral
template<typename T, typename = void>
struct IsIntegralLike : std::false_type
{};

template<typename T>
struct IsIntegralLike<T, std::void_t<underlyingIntegral_t<T>>> : std::true_type
{};

template<typename T>
constexpr bool IsIntegralLike_v = IsIntegralLike<T>::value;

} // namespace helpers
4 changes: 2 additions & 2 deletions libs/libGamedata/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

set(SOURCES_SUBDIRS )
macro(AddDirectory dir)
file(GLOB SUB_FILES ${dir}/*.cpp ${dir}/*.h ${dir}/*.hpp ${dir}/*.tpp)
file(GLOB SUB_FILES CONFIGURE_DEPENDS ${dir}/*.cpp ${dir}/*.h ${dir}/*.hpp ${dir}/*.tpp)
set(SOURCES_SUBDIRS ${SOURCES_SUBDIRS} ${SUB_FILES})
source_group(${dir} FILES ${SUB_FILES})
endmacro()

AddDirectory(gameData)
AddDirectory(lua)

file(GLOB SOURCES_OTHER *.cpp *.h)
file(GLOB SOURCES_OTHER CONFIGURE_DEPENDS *.cpp *.h)
source_group(other FILES ${SOURCES_OTHER})

find_package(Lua 5.1 REQUIRED)
Expand Down
4 changes: 2 additions & 2 deletions libs/s25main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ gather_dll(BZIP2)

set(SOURCES_SUBDIRS )
macro(AddDirectory dir)
file(GLOB SUB_FILES ${dir}/*.cpp ${dir}/*.h ${dir}/*.hpp ${dir}/*.tpp)
file(GLOB SUB_FILES CONFIGURE_DEPENDS ${dir}/*.cpp ${dir}/*.h ${dir}/*.hpp ${dir}/*.tpp)
set(SOURCES_SUBDIRS ${SOURCES_SUBDIRS} ${SUB_FILES})
source_group(${dir} FILES ${SUB_FILES})
endmacro()
Expand Down Expand Up @@ -37,7 +37,7 @@ AddDirectory(postSystem)
AddDirectory(random)
AddDirectory(resources)
AddDirectory(world)
file(GLOB SOURCES_OTHER *.cpp *.h)
file(GLOB SOURCES_OTHER CONFIGURE_DEPENDS *.cpp *.h)
source_group(other FILES ${SOURCES_OTHER})

set(s25Main_SRCS
Expand Down
2 changes: 1 addition & 1 deletion libs/s25main/Debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ bool DebugInfo::SendString(const char* str, size_t len)
len = strlen(str) + 1;

if(!SendUnsigned(len))
return (false);
return false;

return (Send(str, len));
}
Expand Down
Loading
Loading