aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2020-08-26 23:31:45 +0200
committerShauren <shauren.trinity@gmail.com>2022-02-04 00:27:13 +0100
commitbe9dadc18949c6877f486eab9ee95237224a237e (patch)
treef34fcd28a0acf43d54c4bf853a24800090a2761b
parent7b88fd607e974843481d3055eb2eebc53c2a4b49 (diff)
Common/Utilities: Centralize string -> T conversion in StringConvert.h (PR #25335)
(cherry picked from commit cd30e0b86ce6ee88386a91cebdf353fc55805c57)
-rw-r--r--cmake/compiler/clang/settings.cmake25
-rw-r--r--src/common/Configuration/Config.cpp11
-rw-r--r--src/common/Utilities/StringConvert.h180
-rw-r--r--src/common/Utilities/Types.h6
-rw-r--r--src/common/Utilities/Util.cpp5
-rw-r--r--src/common/Utilities/Util.h3
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.h15
-rw-r--r--src/server/game/Chat/Hyperlinks.h34
-rw-r--r--src/server/scripts/Commands/cs_battlenet_account.cpp28
9 files changed, 251 insertions, 56 deletions
diff --git a/cmake/compiler/clang/settings.cmake b/cmake/compiler/clang/settings.cmake
index 716750d8715..1fe89542a84 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 <charconv>
+#include <cstdint>
+
+int main()
+{
+ uint64_t n;
+ char const c[] = \"0\";
+ std::from_chars(c, c+1, n);
+ return static_cast<int>(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
+ -DTRINITY_NEED_CHARCONV_WORKAROUND)
+endif()
+
if(WITH_WARNINGS)
target_compile_options(trinity-warning-interface
INTERFACE
diff --git a/src/common/Configuration/Config.cpp b/src/common/Configuration/Config.cpp
index 4ac24715646..408e64ffdd7 100644
--- a/src/common/Configuration/Config.cpp
+++ b/src/common/Configuration/Config.cpp
@@ -17,6 +17,7 @@
#include "Config.h"
#include "Log.h"
+#include "StringConvert.h"
#include "Util.h"
#include <boost/property_tree/ini_parser.hpp>
#include <algorithm>
@@ -137,7 +138,15 @@ bool ConfigMgr::GetBoolDefault(std::string const& name, bool def, bool quiet) co
{
std::string val = GetValueDefault(name, std::string(def ? "1" : "0"), quiet);
val.erase(std::remove(val.begin(), val.end(), '"'), val.end());
- return StringToBool(val);
+ Optional<bool> boolVal = Trinity::StringTo<bool>(val);
+ if (boolVal)
+ return *boolVal;
+ else
+ {
+ TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use '%s' instead",
+ name.c_str(), _filename.c_str(), def ? "true" : "false");
+ return def;
+ }
}
int32 ConfigMgr::GetIntDefault(std::string const& name, int32 def, bool quiet) const
diff --git a/src/common/Utilities/StringConvert.h b/src/common/Utilities/StringConvert.h
new file mode 100644
index 00000000000..f90e47cab10
--- /dev/null
+++ b/src/common/Utilities/StringConvert.h
@@ -0,0 +1,180 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITY_STRINGCONVERT_H
+#define TRINITY_STRINGCONVERT_H
+
+#include "Define.h"
+#include "Errors.h"
+#include "Optional.h"
+#include "Types.h"
+#include "Util.h"
+#include <charconv>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+namespace Trinity::Impl::StringConvertImpl
+{
+ template <typename T, typename = void> struct For
+ {
+ static_assert(Trinity::dependant_false_v<T>, "Unsupported type used for ToString or StringTo");
+ /*
+ static Optional<T> FromString(std::string_view str, ...);
+ static std::string ToString(T&& val, ...);
+ */
+ };
+
+ // @todo relax this once proper std::from_chars support exists
+ template <typename T>
+ struct For<T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>>>
+ {
+ static Optional<T> 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)
+ return val;
+ else
+ return std::nullopt;
+ }
+
+ static std::string ToString(T val)
+ {
+ std::string buf(20,'\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<uint64, void>
+ {
+ static Optional<uint64> 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<int64, void>
+ {
+ static Optional<int64> 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<bool, void>
+ {
+ static Optional<bool> FromString(std::string_view str)
+ {
+ 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");
+ }
+ };
+}
+
+namespace Trinity
+{
+ template <typename Result, typename... Params>
+ Optional<Result> StringTo(std::string_view str, Params&&... params)
+ {
+ return Trinity::Impl::StringConvertImpl::For<Result>::FromString(str, std::forward<Params>(params)...);
+ }
+
+ template <typename Type, typename... Params>
+ std::string ToString(Type&& val, Params&&... params)
+ {
+ return Trinity::Impl::StringConvertImpl::For<std::decay_t<Type>>::ToString(std::forward<Type>(val), std::forward<Params>(params)...);
+ }
+}
+
+#endif
diff --git a/src/common/Utilities/Types.h b/src/common/Utilities/Types.h
index ab78fb871a9..223a09c4ee3 100644
--- a/src/common/Utilities/Types.h
+++ b/src/common/Utilities/Types.h
@@ -63,6 +63,12 @@ namespace Trinity
*/
template<template<typename...> typename Check, typename... Ts>
using find_type_if_t = typename find_type_if<Check, Ts...>::type;
+
+ template <typename T>
+ struct dependant_false { static constexpr bool value = false; };
+
+ template <typename T>
+ constexpr bool dependant_false_v = dependant_false<T>::value;
}
#endif // Types_h__
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
index 914aed5816c..f3f76569d11 100644
--- a/src/common/Utilities/Util.cpp
+++ b/src/common/Utilities/Util.cpp
@@ -836,11 +836,6 @@ void Trinity::Impl::HexStrToByteArray(std::string_view str, uint8* out, size_t o
}
}
-bool StringToBool(std::string_view str)
-{
- return ((str == "1") || StringEqualI(str, "true") || StringEqualI(str, "yes"));
-}
-
bool StringEqualI(std::string_view str1, std::string_view str2)
{
return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(),
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
index 95c1fd0ee00..28d0285d74d 100644
--- a/src/common/Utilities/Util.h
+++ b/src/common/Utilities/Util.h
@@ -20,6 +20,8 @@
#include "Define.h"
#include "Errors.h"
+#include "Optional.h"
+
#include <array>
#include <string>
#include <string_view>
@@ -380,7 +382,6 @@ inline std::vector<uint8> HexStrToByteVector(std::string_view str, bool reverse
return buf;
}
-TC_COMMON_API bool StringToBool(std::string_view str);
TC_COMMON_API float DegToRad(float degrees);
TC_COMMON_API bool StringEqualI(std::string_view str1, std::string_view str2);
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
index 271d4850f2a..e2270c0259f 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
@@ -21,6 +21,7 @@
#include "ChatCommandHelpers.h"
#include "ChatCommandTags.h"
#include "SmartEnum.h"
+#include "StringConvert.h"
#include "Util.h"
#include <charconv>
#include <map>
@@ -55,20 +56,10 @@ struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T>>>
char const* next = args;
std::string_view token(args, Trinity::Impl::ChatCommands::tokenize(next));
- if (!token.length())
- return nullptr;
-
- std::from_chars_result result;
- if (StringStartsWith(token, "0x"))
- result = std::from_chars(token.data() + 2, token.data() + token.length(), val, 16);
- else if (StringStartsWith(token, "0b"))
- result = std::from_chars(token.data() + 2, token.data() + token.length(), val, 2);
+ if (Optional<T> v = StringTo<T>(token, 0))
+ val = *v;
else
- result = std::from_chars(token.data(), token.data() + token.length(), val, 10);
-
- if ((token.data() + token.length()) != result.ptr)
return nullptr;
-
return next;
}
};
diff --git a/src/server/game/Chat/Hyperlinks.h b/src/server/game/Chat/Hyperlinks.h
index 960c6a10338..2085a27c4e5 100644
--- a/src/server/game/Chat/Hyperlinks.h
+++ b/src/server/game/Chat/Hyperlinks.h
@@ -19,7 +19,7 @@
#define TRINITY_HYPERLINKS_H
#include "ObjectGuid.h"
-#include "Optional.h"
+#include "StringConvert.h"
#include <string>
#include <type_traits>
#include <utility>
@@ -220,26 +220,28 @@ namespace Trinity::Hyperlinks
return true;
}
- static bool StoreTo(ObjectGuid& val, char const* pos, size_t len)
- {
- val = ObjectGuid::FromString(std::string(pos, len));
- return true;
- }
-
template <typename T>
- static std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
+ static std::enable_if_t<std::is_integral_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
{
- try { val = std::stoull(std::string(pos, len)); }
- catch (...) { return false; }
- return true;
+ if (Optional<T> res = Trinity::StringTo<T>(std::string_view(pos, len)))
+ {
+ val = *res;
+ return true;
+ }
+ else
+ return false;
}
- template <typename T>
- static std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
+ static bool StoreTo(ObjectGuid& val, char const* pos, size_t len)
{
- try { val = std::stoll(std::string(pos, len)); }
- catch (...) { return false; }
- return true;
+ ObjectGuid parsed = ObjectGuid::FromString(std::string(pos, len));
+ if (parsed != ObjectGuid::FromStringFailed)
+ {
+ val = parsed;
+ return true;
+ }
+ else
+ return false;
}
};
diff --git a/src/server/scripts/Commands/cs_battlenet_account.cpp b/src/server/scripts/Commands/cs_battlenet_account.cpp
index 748cfbe1aee..c4f0dff6c01 100644
--- a/src/server/scripts/Commands/cs_battlenet_account.cpp
+++ b/src/server/scripts/Commands/cs_battlenet_account.cpp
@@ -68,45 +68,31 @@ public:
}
/// Create an account
- static bool HandleAccountCreateCommand(ChatHandler* handler, char const* args)
+ static bool HandleAccountCreateCommand(ChatHandler* handler, std::string const& accountName, std::string const& password, Optional<bool> createGameAccount)
{
- if (!*args)
- return false;
-
- ///- %Parse the command line arguments
- char* accountName = strtok((char*)args, " ");
- char* password = strtok(nullptr, " ");
- if (!accountName || !password)
- return false;
-
- if (!strchr(accountName, '@'))
+ if (accountName.find('@') == std::string::npos)
{
handler->SendSysMessage(LANG_ACCOUNT_INVALID_BNET_NAME);
handler->SetSentErrorMessage(true);
return false;
}
- char* createGameAccountParam = strtok(nullptr, " ");
- bool createGameAccount = true;
- if (createGameAccountParam)
- createGameAccount = StringToBool(createGameAccountParam);
-
std::string gameAccountName;
- switch (Battlenet::AccountMgr::CreateBattlenetAccount(std::string(accountName), std::string(password), createGameAccount, &gameAccountName))
+ switch (Battlenet::AccountMgr::CreateBattlenetAccount(accountName, password, createGameAccount.value_or(true), &gameAccountName))
{
case AccountOpResult::AOR_OK:
{
- if (createGameAccount)
- handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET_WITH_GAME, accountName, gameAccountName.c_str());
+ if (createGameAccount == true)
+ handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET_WITH_GAME, accountName.c_str(), gameAccountName.c_str());
else
- handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET, accountName);
+ handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET, accountName.c_str());
if (handler->GetSession())
{
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (%s) created Battle.net account %s%s%s",
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUID().ToString().c_str(),
- accountName, createGameAccount ? " with game account " : "", createGameAccount ? gameAccountName.c_str() : "");
+ accountName.c_str(), createGameAccount == true ? " with game account " : "", createGameAccount == true ? gameAccountName.c_str() : "");
}
break;
}