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
2 changes: 1 addition & 1 deletion BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This guide describes how to build **MMapper** from source on Linux, macOS, and W

Ensure the following tools are installed:

- A C++17-compatible compiler (e.g., GCC, Clang, MSVC)
- A C++20-compatible compiler (e.g., GCC 11+, Clang 16+, MSVC 19.29+)
- [CMake 3.20+](https://cmake.org/)
- [Ninja](https://ninja-build.org/)
- [Qt 6.4+](https://www.qt.io/) (6.8.3 recommended) with `QtWebSockets` and `QtMultimedia`
Expand Down
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ project(mmapper CXX)

list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down Expand Up @@ -419,6 +419,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
endif()

add_compile_options(-Weverything)
add_compile_options(-Wno-c++20-compat)
add_compile_options(-Wno-c++98-c++11-compat-binary-literal) # never useful.
add_compile_options(-Wno-c++98-compat) # never useful.
add_compile_options(-Wno-c++98-compat-pedantic) # sometimes useful.
Expand Down Expand Up @@ -461,7 +462,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Werror=shorten-64-to-32)
add_compile_options(-Werror=sign-conversion) # can warn about hard-to-spot bugs.
add_compile_options(-Werror=switch)
add_compile_options(-Werror=unused-result) # required for c++17 [[nodiscard]]
add_compile_options(-Werror=unused-result) # required for [[nodiscard]]
add_compile_options(-Werror=weak-vtables) # can result in crashes for ODR violations.

# remove noise (most of these cannot be fixed)
Expand All @@ -472,6 +473,7 @@ if(MSVC)
add_definitions(-DNOMINMAX)
# modernize the __cplusplus value
add_compile_options(/Zc:__cplusplus)
add_compile_options(/Zc:preprocessor)
add_compile_options(/utf-8)
add_compile_options(/permissive-)
if(CMAKE_BUILD_TYPE_UPPER MATCHES "^RELWITHDEBINFO$")
Expand Down
6 changes: 3 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ endif()

set_target_properties(
mmapper PROPERTIES
CXX_STANDARD 17
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
COMPILE_FLAGS "${WARNING_FLAGS}"
Expand All @@ -781,15 +781,15 @@ set_target_properties(

set_target_properties(
mm_global PROPERTIES
CXX_STANDARD 17
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
COMPILE_FLAGS "${WARNING_FLAGS}"
)

set_target_properties(
mm_map PROPERTIES
CXX_STANDARD 17
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
COMPILE_FLAGS "${WARNING_FLAGS}"
Expand Down
5 changes: 2 additions & 3 deletions src/display/Textures.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,8 @@ namespace mctp {
namespace detail {
// converts from EnumIndexedArray<SharedMMTexture, ...> to EnumIndexedArray<MMTexArrayPosition, ...>
template<typename T>
auto typeHack(const T &)
-> std::enable_if_t<std::is_same_v<typename T::value_type, SharedMMTexture>,
EnumIndexedArray<MMTexArrayPosition, typename T::index_type, T::SIZE>>;
requires(std::is_same_v<typename T::value_type, SharedMMTexture>)
auto typeHack(const T &) -> EnumIndexedArray<MMTexArrayPosition, typename T::index_type, T::SIZE>;

template<typename T>
struct NODISCARD Proxy
Expand Down
14 changes: 9 additions & 5 deletions src/global/AnsiOstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,17 @@ class NODISCARD AnsiOstream final
void write(char16_t codepoint);
void write(char32_t codepoint);
void write(std::string_view sv);
void write(std::u8string_view sv)
{
write(std::string_view{reinterpret_cast<const char *>(sv.data()), sv.size()});
}

template<typename T>
auto write(const T n)
-> std::enable_if_t<(std::is_integral_v<T> && !std::is_same_v<char32_t, std::decay_t<T>>
&& sizeof(char) < sizeof(T))
|| std::is_floating_point_v<T>,
void>
requires((std::is_integral_v<T> and sizeof(T) > sizeof(char)
and not(std::is_same_v<char16_t, std::decay_t<T>>
or std::is_same_v<char32_t, std::decay_t<T>>))
or std::is_floating_point_v<T>)
void write(const T n)
{
auto s = std::to_string(n);
write(std::string_view{s});
Expand Down
7 changes: 3 additions & 4 deletions src/global/Array.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,15 @@ class NODISCARD Array : public std::array<T, N>

public:
template<typename First, typename... Types>
requires(std::is_same_v<First, Types> && ...)
explicit constexpr Array(First &&first, Types &&...args)
: std::array<T, N>{std::forward<First>(first), std::forward<Types>(args)...}
{
static_assert(N == 1 + sizeof...(Types), "missing initializers");
}
};

#if __cpp_deduction_guides >= 201606
// deducation guide copied from std::array
template<typename T, typename... U>
Array(T, U...) -> Array<std::enable_if_t<(std::is_same_v<T, U> && ...), T>, 1 + sizeof...(U)>;
#endif
requires(std::is_same_v<T, U> && ...)
Array(T, U...) -> Array<T, 1 + sizeof...(U)>;
} // namespace MMapper
8 changes: 1 addition & 7 deletions src/global/Charset-Utf8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,13 +552,7 @@ NODISCARD static constexpr bool is7bit(const char c) noexcept
}
NODISCARD static constexpr bool is7bit(const std::string_view sv) noexcept
{
// NOLINTNEXTLINE (can't use std::all_of() in constexpr in c++17)
for (const char c : sv) {
if (!is7bit(c)) {
return false;
}
}
return true;
return std::all_of(sv.begin(), sv.end(), [](char c) { return is7bit(c); });
}

NODISCARD static constexpr Utf8ValidationEnum validateUtf8(std::string_view sv) noexcept
Expand Down
1 change: 0 additions & 1 deletion src/global/Charset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,6 @@ ALLOW_DISCARD QString &toAsciiInPlace(QString &str)

// NOTE: 128 (0x80) was not converted to 'z' before.
for (QChar &qc : str) {
// c++17 if statement with initializer
if (const char ch = mmqt::toLatin1(qc); !isAscii(ch)) {
qc = QLatin1Char{charset::conversion::latin1ToAscii(ch)};
}
Expand Down
16 changes: 9 additions & 7 deletions src/global/ConfigConsts-Computed.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "ConfigEnums.h"

#include <functional>
#include <stdexcept>
#include <string_view>

Expand Down Expand Up @@ -44,7 +45,7 @@ static inline constexpr PackageEnum CURRENT_PACKAGE = [] {
throw std::runtime_error("unsupported package type");
}();

static inline constexpr const PlatformEnum CURRENT_PLATFORM = [] {
static inline constexpr PlatformEnum CURRENT_PLATFORM = std::invoke([]() constexpr -> PlatformEnum {
#if defined(Q_OS_WIN)
return PlatformEnum::Windows;
#elif defined(Q_OS_MAC)
Expand All @@ -56,14 +57,15 @@ static inline constexpr const PlatformEnum CURRENT_PLATFORM = [] {
#else
throw std::runtime_error("unsupported platform");
#endif
}();
});

static inline constexpr const EnvironmentEnum CURRENT_ENVIRONMENT = [] {
static inline constexpr EnvironmentEnum CURRENT_ENVIRONMENT = std::invoke(
[]() constexpr -> EnvironmentEnum {
#if Q_PROCESSOR_WORDSIZE == 4
return EnvironmentEnum::Env32Bit;
return EnvironmentEnum::Env32Bit;
#elif Q_PROCESSOR_WORDSIZE == 8
return EnvironmentEnum::Env64Bit;
return EnvironmentEnum::Env64Bit;
#else
throw std::runtime_error("unsupported environment");
throw std::runtime_error("unsupported environment");
#endif
}();
});
4 changes: 2 additions & 2 deletions src/global/MakeQPointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

namespace mmqt {
template<typename T, typename... Args>
NODISCARD auto makeQPointer(Args &&...args)
-> std::enable_if_t<std::is_base_of_v<QObject, T>, QPointer<T>>
requires(std::is_base_of_v<QObject, T>)
NODISCARD QPointer<T> makeQPointer(Args &&...args)
Comment on lines +15 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): makeQPointer destroys the newly created QObject before returning, leaving QPointer dangling

This constructs a std::unique_ptr<T> and returns a QPointer<T> to ptr.get(). When makeQPointer returns, ptr is destroyed and T is deleted, so the QPointer immediately dangles. You need to transfer ownership out of the unique_ptr (e.g. ptr.release()) or construct with new T(...) so Qt owns the object via the parent hierarchy and it survives the function scope.

{
auto ptr = std::make_unique<T>(std::forward<Args>(args)...);
if (ptr->QObject::parent() == nullptr) {
Expand Down
6 changes: 4 additions & 2 deletions src/global/TaggedInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,17 @@ template<typename...>
struct NODISCARD underlying_helper;

template<typename T>
requires(not std::is_enum_v<T>)
struct NODISCARD underlying_helper<T>
{
using type = T;
};

template<typename T>
struct NODISCARD underlying_helper<T, std::enable_if_t<std::is_enum_v<T>>>
requires(std::is_enum_v<T>)
struct NODISCARD underlying_helper<T>
{
using type = typename std::underlying_type_t<T>;
using type = std::underlying_type_t<T>;
};

template<typename Crtp_, typename Tag_, typename Type_>
Expand Down
28 changes: 5 additions & 23 deletions src/global/emojis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,36 +171,18 @@ struct NODISCARD Emojis final
class NODISCARD HexPrefixTree final
{
private:
#if defined(__cpp_lib_generic_unordered_lookup) && __cpp_lib_generic_unordered_lookup
static_assert(false, "Not tested; this may need to be fixed.");
using HashBase = std::hash<std::u32string_view>;
struct NODISCARD Hash final : public HashBase
{
auto operator()(const std::u32string &s) const { return HashBase::operator()(s); }
};
struct NODISCARD Pred final
{
using T1 = const std::u32string &;
using T2 = const std::u32string_view;
bool operator()(T1 a, T1 b) const { return a == b; }
bool operator()(T1 a, T2 b) const { return a == b; }
bool operator()(T2 a, T1 b) const { return a == b; }
// bool operator()(T2 a, T2 b) const { return a == b; }
};
using Map = std::unordered_map<std::u32string, std::optional<std::u32string>, Hash, Pred>;
#else
// REVISIT: consider using std::unordered_map
struct NODISCARD Comp final
{
using is_transparent = void;
using T1 = const std::u32string &;
using T2 = const std::u32string_view;
bool operator()(T1 a, T1 b) const { return a < b; }
bool operator()(T1 a, T2 b) const { return a < b; }
bool operator()(T2 a, T1 b) const { return a < b; }
NODISCARD bool operator()(T1 a, T1 b) const { return a < b; }
NODISCARD bool operator()(T1 a, T2 b) const { return a < b; }
NODISCARD bool operator()(T2 a, T1 b) const { return a < b; }
// bool operator()(T2 a, T2 b) const { return a < b; }
};
using Map = std::map<std::u32string, std::optional<std::u32string>, Comp>;
#endif

Map m_map;

Expand Down Expand Up @@ -604,7 +586,7 @@ NODISCARD QString mmqt::encodeEmojiShortCodes(const QString &s)

// REVISIT: should this include "U+" or not?
char hex[32];
snprintf(hex, sizeof(hex), "[:U+%X:]", c);
snprintf(hex, sizeof(hex), "[:U+%X:]", static_cast<unsigned int>(c));
for (const char ascii : std::string_view{hex}) {
m_output += static_cast<char32_t>(static_cast<uint8_t>(ascii));
}
Expand Down
24 changes: 10 additions & 14 deletions src/global/float_cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,29 @@ NODISCARD constexpr bool isSameIntValue(const A a, const B b) noexcept
return a == b;
}

// This only exists because c++17 std::isnan() is not constexpr;
// using "f != f" feels like a hack.
template<typename FloatType>
NODISCARD constexpr bool isNan(const FloatType f) noexcept
{
#if __cplusplus >= 202000L
#if defined(__cpp_lib_constexpr_cmath) && __cpp_lib_constexpr_cmath >= 202202L
return std::isnan(f);
#elif defined(__clang__) || defined(__GNUC__)
return __builtin_isnan(f);
#else
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
#endif
return f != f; // NOLINT (this is only true for NaNs)
#ifdef __clang__
#pragma clang diagnostic pop
#endif
// std::isnan() is not constexpr in all C++20 implementations (e.g. MSVC 2022)
return f != f;
#endif
}

// this only exists because c++17 std::isfinite() is not constexpr
template<typename FloatType>
NODISCARD constexpr bool isFinite(const FloatType f) noexcept
{
#if __cplusplus >= 202000L
#if defined(__cpp_lib_constexpr_cmath) && __cpp_lib_constexpr_cmath >= 202202L
return std::isfinite(f);
#elif defined(__clang__) || defined(__GNUC__)
return __builtin_isfinite(f);
#else
constexpr auto inf = std::numeric_limits<FloatType>::infinity();
// std::isfinite() is not constexpr in all C++20 implementations (e.g. MSVC 2022)
const auto inf = std::numeric_limits<FloatType>::infinity();
return !isNan(f) && -inf < f && f < inf;
#endif
}
Expand Down
7 changes: 2 additions & 5 deletions src/global/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@
#include <cstdint>
#include <cstring>
#include <string_view>
#include <type_traits>

template<typename T>
MAYBE_UNUSED NODISCARD static auto numeric_hash(const T val) noexcept
-> std::enable_if_t<std::is_arithmetic_v<T>, size_t>
MAYBE_UNUSED NODISCARD static size_t numeric_hash(const std::integral auto val) noexcept

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): numeric_hash now only accepts integral types, which is a behavior change from the previous arithmetic version

Previously, std::is_arithmetic_v<T> allowed both integral and floating-point arguments; const std::integral auto val now rejects floats, so existing callers hashing float/double will break. If this narrowing is intentional, please document or rename accordingly; otherwise, consider a requires std::is_arithmetic_v<T> constraint or adding a std::floating_point overload to preserve prior behavior.

{
static constexpr const size_t size = sizeof(val);
static constexpr size_t size = sizeof(val);
char buf[size];
std::memcpy(buf, &val, size);
return std::hash<std::string_view>()({buf, size});
Expand Down
24 changes: 3 additions & 21 deletions src/global/mm_source_location.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,10 @@

#include "macros.h"

#include <cstdint>

#if __cplusplus >= 202000L \
&& __has_builtin(__builtin_source_location) // && __cpp_lib_source_location >= 201907L
#include <source_location>

namespace mm {
using source_location = std::source_location;
}
#define MM_SOURCE_LOCATION() (std::source_location::current())
#else
namespace mm {
// TODO: replace this with C++20's std::source_location
struct NODISCARD source_location final
{
const char *m_file_name = "";
const char *m_function_name = "";
std::uint_least32_t m_line = 0;

NODISCARD const char *file_name() const { return this->m_file_name; }
NODISCARD const char *function_name() const { return this->m_function_name; }
NODISCARD std::uint_least32_t line() const { return this->m_line; }
};
} // namespace mm
#define MM_SOURCE_LOCATION() (mm::source_location{__FILE__, __FUNCTION__, __LINE__})
#endif

#define MM_SOURCE_LOCATION() (std::source_location::current())
14 changes: 2 additions & 12 deletions src/global/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,16 +411,7 @@ ALLOW_DISCARD static inline size_t erase_if(Container &container, Callback &&cal
template<typename T, typename Predicate>
NODISCARD bool listRemoveIf(std::list<T> &list, Predicate &&should_remove)
{
// c++20 version of remove_if() returns # of elements removed, but c++17 doesn't.
bool removed = false;
list.remove_if([&removed, &should_remove](const T &element) -> bool {
if (should_remove(element)) {
removed = true;
return true;
}
return false;
});
return removed;
return std::erase_if(list, std::forward<Predicate>(should_remove)) > 0;
}

template<typename Container, typename Callback>
Expand Down Expand Up @@ -449,9 +440,8 @@ NODISCARD static inline auto find_min_computed(const Container &container, Callb
}
}

// This can be removed and replaced with std::remove_cvref_t<T> in c++20.
template<typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
using remove_cvref_t = std::remove_cvref_t<T>;

template<typename T, typename... Ts>
struct are_distinct : std::conjunction<std::negation<std::is_same<T, Ts>>..., are_distinct<Ts...>>
Expand Down
Loading
Loading