diff --git a/include/dpp/managed.h b/include/dpp/managed.h index 612fcd0a64..84fdfc9c83 100644 --- a/include/dpp/managed.h +++ b/include/dpp/managed.h @@ -36,13 +36,18 @@ namespace dpp { * This value contains a timestamp, worker ID, internal server ID, and an incrementing value. * Only the timestamp is relevant to us as useful metadata. */ - snowflake id; + snowflake id = 0; + + /** + * @brief Constructor, initialises id to 0. + */ + constexpr managed() noexcept = default; /** * @brief Constructor, initialises ID * @param nid ID to set */ - constexpr managed(const snowflake nid = 0) noexcept : id{nid} {} + constexpr managed(const snowflake nid) noexcept : id{nid} {} /** * @brief Copy constructor diff --git a/include/dpp/snowflake.h b/include/dpp/snowflake.h index dd6cb8a4d9..b0336defe4 100644 --- a/include/dpp/snowflake.h +++ b/include/dpp/snowflake.h @@ -22,8 +22,8 @@ #pragma once #include #include -#include -#include +#include +#include /** * @brief The main namespace for D++ functions. classes and types @@ -61,23 +61,75 @@ class DPP_EXPORT snowflake final { constexpr snowflake() noexcept = default; /** - * @brief Construct a snowflake object - * @param value A snowflake value + * @brief Copy a snowflake object + */ + constexpr snowflake(const snowflake &rhs) noexcept = default; + + /** + * @brief Move a snowflake object + */ + constexpr snowflake(snowflake &&rhs) noexcept = default; + + /** + * @brief Construct a snowflake from an integer value + * + * @throw dpp::logic_exception on assigning a negative value. the function is noexcept if the type given is unsigned + * @param snowflake_val snowflake value as an integer type */ - constexpr snowflake(const uint64_t value_) noexcept : value{value_} {} + template >> + constexpr snowflake(T snowflake_val) noexcept(std::is_unsigned_v) : value(static_cast>(snowflake_val)) { + /** + * we cast to the unsigned version of the type given - this maintains "possible loss of data" warnings for sizeof(T) > sizeof(value) + * while suppressing them for signed to unsigned conversion + */ + if constexpr (!std::is_unsigned_v) { + /* if the type is signed, at compile-time, add a check at runtime that the value is unsigned */ + if (snowflake_val < 0) { + value = 0; + throw dpp::logic_exception{"cannot assign a negative value to dpp::snowflake"}; + } + } + } /** - * @brief Construct a snowflake object + * @brief Construct a snowflake object from an unsigned integer in a string + * + * On invalid string the value will be 0 * @param string_value A snowflake value */ snowflake(std::string_view string_value) noexcept; /** - * @brief For acting like an integer - * @return The snowflake value + * @brief Copy value from another snowflake + * + * @param rhs The snowflake to copy from */ - constexpr operator uint64_t() const noexcept { - return value; + constexpr dpp::snowflake &operator=(const dpp::snowflake& rhs) noexcept = default; + + /** + * @brief Move value from another snowflake + * + * @param rhs The snowflake to move from + */ + constexpr dpp::snowflake &operator=(dpp::snowflake&& rhs) noexcept = default; + + /** + * @brief Assign value converted from a string to the snowflake + * + * On invalid string the value will be 0 + * @param snowflake_val snowflake value as a string + */ + dpp::snowflake &operator=(std::string_view snowflake_val) noexcept; + + /** + * @brief Assign integer value to the snowflake + * + * @throw dpp::logic_exception on assigning a negative value. the function is noexcept if the type given is unsigned + * @param snowflake_val snowflake value as an integer type + */ + template >> + constexpr dpp::snowflake &operator=(T snowflake_val) noexcept(std::is_unsigned_v) { + return *this = dpp::snowflake{snowflake_val}; } /** @@ -99,11 +151,13 @@ class DPP_EXPORT snowflake final { } /** - * @brief Assign from std::string + * @brief Comparison operator with another snowflake * - * @param snowflake_val string to assign from. + * @param snowflake_val snowflake */ - snowflake& operator=(std::string_view snowflake_val) noexcept; + constexpr bool operator==(dpp::snowflake snowflake_val) const noexcept { + return value == snowflake_val.value; + } /** * @brief Comparison operator with a string @@ -111,9 +165,32 @@ class DPP_EXPORT snowflake final { * @param snowflake_val snowflake value as a string */ inline bool operator==(std::string_view snowflake_val) const noexcept { + uint64_t v; + auto [end, err] = std::from_chars(snowflake_val.data(), snowflake_val.data() + snowflake_val.size(), v); + if (end != snowflake_val.data() + snowflake_val.size()) // parse error + return false; + return *this == v; + } + + /** + * @brief Comparison operator with an integer + * + * @param snowflake_val snowflake value as an integer type + */ + template >> + constexpr bool operator==(T snowflake_val) const noexcept { + /* We use the std::enable_if_t trick to disable implicit conversions so there is a perfect candidate for overload resolution for integers, and it isn't ambiguous */ return *this == dpp::snowflake{snowflake_val}; } + /** + * @brief For acting like an integer + * @return The snowflake value + */ + constexpr operator uint64_t() const noexcept { + return value; + } + /** * @brief For acting like an integer * @return A reference to the snowflake value diff --git a/src/dpp/snowflake.cpp b/src/dpp/snowflake.cpp index 97d2f9194b..46df8583b4 100644 --- a/src/dpp/snowflake.cpp +++ b/src/dpp/snowflake.cpp @@ -20,6 +20,7 @@ ************************************************************************************/ #include #include +#include namespace dpp { @@ -29,7 +30,7 @@ snowflake::snowflake(std::string_view string_value) noexcept { value = 0; } -snowflake& snowflake::operator=(std::string_view string_value) { +snowflake& snowflake::operator=(std::string_view string_value) noexcept { auto [end, err] = std::from_chars(string_value.data(), string_value.data() + string_value.size(), value); if (end != string_value.data() + string_value.size()) value = 0; diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 8e55c3cf70..2764dd54ff 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -194,10 +194,11 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } { // test dpp::snowflake + start_test(SNOWFLAKE); bool success = true; dpp::snowflake s = 69420; json j; - j["value"] = 69420; + j["value"] = s; success = dpp::snowflake_not_null(&j, "value") == 69420 && success; DPP_CHECK_CONSTRUCT_ASSIGN(SNOWFLAKE, dpp::snowflake, success); s = 42069; @@ -207,6 +208,7 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b success = success && s == 69420; s = dpp::snowflake{"1337"}; success = success && s == 1337; + success = success && dpp::snowflake{0} == 0; set_test(SNOWFLAKE, success); }