Skip to content
Open
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.5 recommended) with `QtWebSockets` and `QtMultimedia`
Expand Down
5 changes: 3 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 @@ -461,7 +461,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 +472,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
2 changes: 1 addition & 1 deletion iwyu-mapping-stl.imp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{include: ["<bits/stdint-uintn.h>", private, "<cstdint>", public]},
{include: ["<bits/unique_ptr.h>", private, "<memory>", public]},

# NOTE: this <cmath> is the correct header for std::abs() in c++17
# NOTE: this <cmath> is the correct header for std::abs() in C++20
{include: ["<bits/std_abs.h>", private, "<cstdlib>", public]},

{include: ["<assert.h>", public, "<cassert>", public]},
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
4 changes: 4 additions & 0 deletions src/global/AnsiOstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ 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)
Expand Down
4 changes: 4 additions & 0 deletions src/global/AnsiTextUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,11 @@ void foreachAnsi(const QStringView line, Callback &&callback)
const auto len = line.size();
qsizetype pos = 0;
while (pos < len) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QRegularExpressionMatch m = weakAnsiRegex.matchView(line, pos);
#else
QRegularExpressionMatch m = weakAnsiRegex.match(line, pos);
#endif
if (!m.hasMatch()) {
break;
}
Expand Down
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
30 changes: 19 additions & 11 deletions src/global/emojis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,20 +172,28 @@ struct NODISCARD Emojis 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
struct NODISCARD Hash final
{
auto operator()(const std::u32string &s) const { return HashBase::operator()(s); }
using is_transparent = void;
size_t operator()(const std::u32string &s) const
{
return std::hash<std::u32string>{}(s);
}
size_t operator()(std::u32string_view sv) const
Comment on lines +175 to +182

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): Align hash implementations for u32string and u32string_view to guarantee consistency

For transparent lookup, Pred(a, b) being true must imply Hash(a) == Hash(b). Relying on std::hash<std::u32string> for one overload and std::hash<std::u32string_view> for the other assumes the library uses compatible implementations, which is not guaranteed. Please route both overloads through a single hasher (e.g. always hash a std::u32string_view) to ensure consistency.

{
return std::hash<std::u32string_view>{}(sv);
}
};
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 is_transparent = void;
bool operator()(const std::u32string &a, const std::u32string &b) const
{
return a == b;
}
bool operator()(const std::u32string &a, std::u32string_view b) const { return a == b; }
bool operator()(std::u32string_view a, const std::u32string &b) const { return a == b; }
bool operator()(std::u32string_view a, std::u32string_view b) const { return a == b; }
};
using Map = std::unordered_map<std::u32string, std::optional<std::u32string>, Hash, Pred>;
#else
Expand Down Expand Up @@ -604,7 +612,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
31 changes: 10 additions & 21 deletions src/global/mm_source_location.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,17 @@

#include "macros.h"

#include <cstdint>

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

namespace mm {
/*
* NOTE: std::source_location (C++20) support:
* - GCC 11+
* - Clang 16+
* - MSVC 19.29+ (Visual Studio 2019 16.11+)
* - Apple Clang 15+ (macOS 14+)
*/
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
3 changes: 3 additions & 0 deletions src/map/ExitFields.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
struct NODISCARD ExitFields final : public ExitFieldsGetters<ExitFields>,
public ExitFieldsSetters<ExitFields>
{
public:
ExitFields() {}
Comment on lines +11 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Use a defaulted constructor instead of an empty user-provided one to preserve triviality/constexpr properties

An empty ExitFields() body makes this a user-provided constructor, which can break triviality/is_trivially_constructible and constexpr usage. If you just need a default constructor, use ExitFields() = default; to preserve the type’s existing properties.


public:
#define X_DECL_FIELD(_Type, _Prop, _OptInit) _Type _Prop _OptInit;
XFOREACH_EXIT_PROPERTY(X_DECL_FIELD)
Expand Down
3 changes: 3 additions & 0 deletions src/map/RawExit.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ template<typename Tag_>
struct NODISCARD TaggedRawExit final : public ExitFieldsGetters<TaggedRawExit<Tag_>>,
public ExitFieldsSetters<TaggedRawExit<Tag_>>
{
public:
TaggedRawExit() {}
Comment on lines +16 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Prefer = default for the new default constructor to avoid changing type traits

An empty-bodied default constructor makes the type’s ctor user-provided, which can change triviality and related type traits. If you only need a default ctor with no custom logic, prefer ExitFields() = default; to preserve those traits.

Suggested implementation:

 struct NODISCARD ExitFields final : public ExitFieldsGetters<ExitFields>,
                                     public ExitFieldsSetters<ExitFields>
 {
+public:
+    ExitFields() = default;
+

There is a similar pattern in the same file for TaggedRawExit:

public:
    TaggedRawExit() {}

If the same rationale applies (preserving triviality and related traits), consider changing this to TaggedRawExit() = default; as well.


public:
using IdType = std::conditional_t<
std::is_same_v<Tag_, ::tags::RoomIdTag>,
Expand Down
3 changes: 3 additions & 0 deletions src/map/RawRoom.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ struct NODISCARD TaggedRawRoom final : public RoomFieldsGetters<TaggedRawRoom<Ta
public RoomFieldsSetters<TaggedRawRoom<Tag_>>,
public RoomExitFieldsSetters<TaggedRawRoom<Tag_>>
{
public:
TaggedRawRoom() {}

public:
using ExitType = TaggedRawExit<Tag_>;
using IdType = typename ExitType::IdType;
Expand Down
3 changes: 3 additions & 0 deletions src/map/RoomFields.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
struct NODISCARD RoomFields final : public RoomFieldsGetters<RoomFields>,
public RoomFieldsSetters<RoomFields>
{
public:
RoomFields() {}

public:
#define X_DECL_FIELD(_Type, _Prop, _OptInit) _Type _Prop{_OptInit};
XFOREACH_ROOM_PROPERTY(X_DECL_FIELD)
Expand Down
2 changes: 1 addition & 1 deletion src/syntax/TreeParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ HelpFrame HelpFrame::makeChild()
{
flush();
HelpFrame child = *this; // copy ctor
return child; // c++17 spec says NRVO elides the move constructor here, but g++ 7.4 uses move ctor.
return child; // C++ standard says NRVO elides the move constructor here.
}

class NODISCARD TreeParser::HelpCommon final
Expand Down
17 changes: 17 additions & 0 deletions src/syntax/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,23 @@ Vector::Base::const_iterator Vector::end() const
return m_vector->end();
}

bool Vector::empty() const
{
return m_vector->empty();
}
size_t Vector::size() const
{
return m_vector->size();
}
const Value &Vector::at(size_t pos) const
{
return m_vector->at(pos);
}
const Value &Vector::operator[](size_t pos) const
{
return at(pos);
}

Vector getAnyVectorReversed(const Pair *const matched)
{
size_t len = 0;
Expand Down
8 changes: 4 additions & 4 deletions src/syntax/Value.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ struct NODISCARD Vector final
NODISCARD Base::const_iterator end() const;

public:
NODISCARD bool empty() const { return m_vector->empty(); }
NODISCARD size_t size() const { return m_vector->size(); }
NODISCARD bool empty() const;
NODISCARD size_t size() const;
// NOTE: at() throws if out of range.
const Value &at(size_t pos) const { return m_vector->at(pos); }
const Value &operator[](size_t pos) const { return at(pos); }
const Value &at(size_t pos) const;
const Value &operator[](size_t pos) const;

public:
friend std::ostream &operator<<(std::ostream &os, const Vector &v);
Expand Down
Loading
Loading