Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
9 changes: 7 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 @@ -436,6 +436,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
endif()

add_compile_options(-Wno-extra-semi-stmt) # enabled only for src/ directory
add_compile_options(-Wno-c++20-compat)

# require explicit template parameters when deduction guides are missing
add_compile_options(-Werror=ctad-maybe-unsupported)
Expand All @@ -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,10 @@ if(MSVC)
add_definitions(-DNOMINMAX)
# modernize the __cplusplus value
add_compile_options(/Zc:__cplusplus)
add_compile_options(/Zc:preprocessor)
add_compile_options(/Zc:lambda)
add_compile_options(/Zc:enumTypes)
add_compile_options(/Zc:internals)
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
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 list.remove_if(std::forward<Predicate>(should_remove)) > 0;
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Outdated
}

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() = default;

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() = default;

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() = default;

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() = default;

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