From faef25686163c2fbfd9c91d7c93c450aec82449b Mon Sep 17 00:00:00 2001 From: Treeston Date: Sat, 26 Jul 2025 00:40:54 +0200 Subject: [PATCH] Common/Utilities: ported StringConvert helpers from 335 branch --- cmake/compiler/clang/settings.cmake | 25 +++ src/common/Utilities/StringConvert.h | 278 +++++++++++++++++++++++++++ src/common/Utilities/Types.h | 6 + 3 files changed, 309 insertions(+) create mode 100644 src/common/Utilities/StringConvert.h diff --git a/cmake/compiler/clang/settings.cmake b/cmake/compiler/clang/settings.cmake index 31865c67d6..c1e517ab26 100644 --- a/cmake/compiler/clang/settings.cmake +++ b/cmake/compiler/clang/settings.cmake @@ -13,6 +13,31 @@ else() message(STATUS "Clang: Minimum version required is ${CLANG_EXPECTED_VERSION}, found ${CMAKE_CXX_COMPILER_VERSION} - ok!") endif() +# This tests for a bug in clang-7 that causes linkage to fail for 64-bit from_chars (in some configurations) +# If the clang requirement is bumped to >= clang-8, you can remove this check, as well as +# the associated ifdef block in src/common/Utilities/StringConvert.h +include(CheckCXXSourceCompiles) + +check_cxx_source_compiles(" +#include +#include + +int main() +{ + uint64_t n; + char const c[] = \"0\"; + std::from_chars(c, c+1, n); + return static_cast(n); +} +" CLANG_HAVE_PROPER_CHARCONV) + +if (NOT CLANG_HAVE_PROPER_CHARCONV) + message(STATUS "Clang: Detected from_chars bug for 64-bit integers, workaround enabled") + target_compile_definitions(trinity-compile-option-interface + INTERFACE + TRINITY_NEED_CHARCONV_WORKAROUND) +endif() + if(WITH_WARNINGS) target_compile_options(trinity-warning-interface INTERFACE diff --git a/src/common/Utilities/StringConvert.h b/src/common/Utilities/StringConvert.h new file mode 100644 index 0000000000..ebe33cee72 --- /dev/null +++ b/src/common/Utilities/StringConvert.h @@ -0,0 +1,278 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITY_STRINGCONVERT_H +#define TRINITY_STRINGCONVERT_H + +#include "Define.h" +#include "Errors.h" +#include "Optional.h" +#include "Types.h" +#include +#include +#include +#include + +TC_COMMON_API bool StringEqualI(std::string_view str1, std::string_view str2); + +namespace Trinity::Impl::StringConvertImpl +{ + template struct For + { + static_assert(Trinity::dependant_false_v, "Unsupported type used for ToString or StringTo"); + /* + static Optional FromString(std::string_view str, ...); + static std::string ToString(T&& val, ...); + */ + }; + + template + struct For && !std::is_same_v>> + { + static Optional FromString(std::string_view str, int base = 10) + { + if (base == 0) + { + if (StringEqualI(str.substr(0, 2), "0x")) + { + base = 16; + str.remove_prefix(2); + } + else if (StringEqualI(str.substr(0, 2), "0b")) + { + base = 2; + str.remove_prefix(2); + } + else + base = 10; + + if (str.empty()) + return std::nullopt; + } + + char const* const start = str.data(); + char const* const end = (start + str.length()); + + T val; + std::from_chars_result const res = std::from_chars(start, end, val, base); + if ((res.ptr == end) && (res.ec == std::errc())) + return val; + else + return std::nullopt; + } + + static std::string ToString(T val) + { + using buffer_size = std::integral_constant; + + std::string buf(buffer_size::value,'\0'); /* 2^64 is 20 decimal characters, -(2^63) is 20 including the sign */ + char* const start = buf.data(); + char* const end = (start + buf.length()); + std::to_chars_result const res = std::to_chars(start, end, val); + ASSERT(res.ec == std::errc()); + buf.resize(res.ptr - start); + return buf; + } + }; + +#ifdef TRINITY_NEED_CHARCONV_WORKAROUND + /* + If this is defined, std::from_chars will cause linkage errors for 64-bit types. + (This is a bug in clang-7.) + + If the clang requirement is bumped to >= clang-8, remove this ifdef block and its + associated check in cmake/compiler/clang/settings.cmake + */ + template <> + struct For + { + static Optional FromString(std::string_view str, int base = 10) + { + if (str.empty()) + return std::nullopt; + try + { + size_t n; + uint64 val = std::stoull(std::string(str), &n, base); + if (n != str.length()) + return std::nullopt; + return val; + } + catch (...) { return std::nullopt; } + } + + static std::string ToString(uint64 val) + { + return std::to_string(val); + } + }; + + template <> + struct For + { + static Optional FromString(std::string_view str, int base = 10) + { + try { + if (str.empty()) + return std::nullopt; + size_t n; + int64 val = std::stoll(std::string(str), &n, base); + if (n != str.length()) + return std::nullopt; + return val; + } + catch (...) { return std::nullopt; } + } + + static std::string ToString(int64 val) + { + return std::to_string(val); + } + }; +#endif + + template <> + struct For + { + static Optional FromString(std::string_view str, int strict = 0) /* this is int to match the signature for "proper" integral types */ + { + if (strict) + { + if (str == "1") + return true; + if (str == "0") + return false; + return std::nullopt; + } + else + { + if ((str == "1") || StringEqualI(str, "y") || StringEqualI(str, "on") || StringEqualI(str, "yes") || StringEqualI(str, "true")) + return true; + if ((str == "0") || StringEqualI(str, "n") || StringEqualI(str, "off") || StringEqualI(str, "no") || StringEqualI(str, "false")) + return false; + return std::nullopt; + } + } + + static std::string ToString(bool val) + { + return (val ? "1" : "0"); + } + }; + +#if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT + template + struct For>> + { + static Optional FromString(std::string_view str, std::chars_format fmt = std::chars_format()) + { + if (str.empty()) + return std::nullopt; + + if (fmt == std::chars_format()) + { + if (StringEqualI(str.substr(0, 2), "0x")) + { + fmt = std::chars_format::hex; + str.remove_prefix(2); + } + else + fmt = std::chars_format::general; + + if (str.empty()) + return std::nullopt; + } + + char const* const start = str.data(); + char const* const end = (start + str.length()); + + T val; + std::from_chars_result const res = std::from_chars(start, end, val, fmt); + if ((res.ptr == end) && (res.ec == std::errc())) + return val; + else + return std::nullopt; + } + + // this allows generic converters for all numeric types (easier templating!) + static Optional FromString(std::string_view str, int base) + { + if (base == 16) + return FromString(str, std::chars_format::hex); + else if (base == 10) + return FromString(str, std::chars_format::general); + else + return FromString(str, std::chars_format()); + } + + static std::string ToString(T val) + { + return std::to_string(val); + } + }; +#else + // @todo replace this once libc++ supports double args to from_chars + template + struct For>> + { + static Optional FromString(std::string_view str, int base = 0) + { + try { + if (str.empty()) + return std::nullopt; + + if ((base == 10) && StringEqualI(str.substr(0, 2), "0x")) + return std::nullopt; + + std::string tmp; + if (base == 16) + tmp.append("0x"); + tmp.append(str); + + size_t n; + T val = static_cast(std::stold(tmp, &n)); + if (n != tmp.length()) + return std::nullopt; + return val; + } + catch (...) { return std::nullopt; } + } + + static std::string ToString(T val) + { + return std::to_string(val); + } + }; +#endif +} + +namespace Trinity +{ + template + Optional StringTo(std::string_view str, Params&&... params) + { + return Trinity::Impl::StringConvertImpl::For::FromString(str, std::forward(params)...); + } + + template + std::string ToString(Type&& val, Params&&... params) + { + return Trinity::Impl::StringConvertImpl::For>::ToString(std::forward(val), std::forward(params)...); + } +} + +#endif diff --git a/src/common/Utilities/Types.h b/src/common/Utilities/Types.h index f441b7b680..922be079de 100644 --- a/src/common/Utilities/Types.h +++ b/src/common/Utilities/Types.h @@ -63,6 +63,12 @@ namespace Trinity */ template typename Check, typename... Ts> using find_type_if_t = typename find_type_if::type; + + template + struct dependant_false { static constexpr bool value = false; }; + + template + constexpr bool dependant_false_v = dependant_false::value; } #endif // Types_h__