diff options
author | Sadie Powell <sadie@witchery.services> | 2024-03-11 13:53:05 +0000 |
---|---|---|
committer | Sadie Powell <sadie@witchery.services> | 2024-03-11 19:17:29 +0000 |
commit | 29e7674e56bf2b829bba22def2760d034a76e788 (patch) | |
tree | f40049ba995b03dd7c510d88f9f19db2d2e65a2e /include | |
parent | e2df7d4d01f8fdb41c49ce8efc462cab005e7d5c (diff) |
Replace convertTo/stringify with non-throwing alternatives.
Having these throw is terrible for ergonomics and there are loads
of places where the exception was either silently ignored or not
handled at all. Having a function which returns an optional and
another that returns a default works a lot better imo.
Diffstat (limited to 'include')
-rw-r--r-- | include/anope.h | 69 | ||||
-rw-r--r-- | include/config.h | 9 | ||||
-rw-r--r-- | include/convert.h | 133 | ||||
-rw-r--r-- | include/modules/sql.h | 13 | ||||
-rw-r--r-- | include/protocol.h | 6 | ||||
-rw-r--r-- | include/uplink.h | 8 |
6 files changed, 150 insertions, 88 deletions
diff --git a/include/anope.h b/include/anope.h index fd3728d84..c4a2079aa 100644 --- a/include/anope.h +++ b/include/anope.h @@ -39,6 +39,7 @@ namespace Anope typedef std::string::reverse_iterator reverse_iterator; typedef std::string::const_reverse_iterator const_reverse_iterator; typedef std::string::size_type size_type; + typedef std::string::value_type value_type; static const size_type npos = static_cast<size_type>(-1); /** @@ -729,72 +730,6 @@ public: virtual ~ModuleException() noexcept = default; }; -class CoreExport ConvertException final - : public CoreException -{ -public: - ConvertException(const Anope::string &reason = "") : CoreException(reason) { } - - virtual ~ConvertException() noexcept = default; -}; - -/** Convert something to a string - */ -inline Anope::string stringify(const Anope::string &x) -{ - return x; -} - -template<typename T> inline Anope::string stringify(const T &x) -{ - std::ostringstream stream; - - if (!(stream << x)) - throw ConvertException("Stringify fail"); - - return stream.str(); -} - -template<typename T> inline void convert(const Anope::string &s, T &x, Anope::string &leftover, bool failIfLeftoverChars = true) -{ - leftover.clear(); - std::istringstream i(s.str()); - char c; - if (!(i >> x)) - throw ConvertException("Convert fail"); - if (failIfLeftoverChars) - { - if (i.get(c)) - throw ConvertException("Convert fail"); - } - else - { - std::string left; - getline(i, left); - leftover = left; - } -} - -template<typename T> inline void convert(const Anope::string &s, T &x, bool failIfLeftoverChars = true) -{ - Anope::string Unused; - convert(s, x, Unused, failIfLeftoverChars); -} - -template<typename T> inline T convertTo(const Anope::string &s, Anope::string &leftover, bool failIfLeftoverChars = true) -{ - T x; - convert(s, x, leftover, failIfLeftoverChars); - return x; -} - -template<typename T> inline T convertTo(const Anope::string &s, bool failIfLeftoverChars = true) -{ - T x; - convert(s, x, failIfLeftoverChars); - return x; -} - /** Casts to be used instead of dynamic_cast, this uses dynamic_cast * for debug builds and static_cast on release builds * to speed up the program because dynamic_cast relies on RTTI. @@ -814,3 +749,5 @@ template<typename T, typename O> inline T anope_dynamic_static_cast(O ptr) return static_cast<T>(ptr); } #endif + +#include "convert.h" diff --git a/include/config.h b/include/config.h index 022ee503d..652d42a7a 100644 --- a/include/config.h +++ b/include/config.h @@ -46,14 +46,7 @@ namespace Configuration template<typename T> T Get(const Anope::string &tag, const Anope::string &def = "") const { - const Anope::string &value = this->Get<const Anope::string>(tag, def); - if (!value.empty()) - try - { - return convertTo<T>(value); - } - catch (const ConvertException &) { } - return T(); + return Anope::TryConvert<T>(this->Get<const Anope::string>(tag, def)).value_or(T()); } bool Set(const Anope::string &tag, const Anope::string &value); diff --git a/include/convert.h b/include/convert.h new file mode 100644 index 000000000..3cdbcd787 --- /dev/null +++ b/include/convert.h @@ -0,0 +1,133 @@ +/* + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +#pragma once + +#include <optional> + +namespace Anope +{ + /** Attempts to convert a string to any type. + * @param in The value to convert. + * @param leftover If non-nullptr then the location to store leftover data. + */ + template<typename T> + inline std::optional<T> TryConvert(const Anope::string &in, Anope::string *leftover = nullptr) + { + std::istringstream tmp(in.str()); + T out; + if (!(tmp >> out)) + return std::nullopt; + + if (leftover) + { + std::string extra; + std::getline(tmp, extra); + *leftover = extra; + } + else + { + char extra; + if (tmp >> extra) + return std::nullopt; + } + return out; + } + + /** Converts a string to any type. + * @param in The value to convert. + * @param def The default to use if the conversion failed. + * @param leftover If non-nullptr then the location to store leftover data. + */ + template<typename T> + inline T Convert(const Anope::string &in, T def, Anope::string *leftover = nullptr) + { + return TryConvert<T>(in, leftover).value_or(def); + } + + /** Attempts to convert any type to a string. + * @param in The value to convert. + */ + template <class T> + inline std::optional<Anope::string> TryString(const T &in) + { + std::ostringstream tmp; + if (!(tmp << in)) + return std::nullopt; + return tmp.str(); + } + + /** No-op function that returns the string that was passed to it. + * @param in The string to return. + */ + inline const string &ToString(const string &in) + { + return in; + } + + /** Converts a std::string to a string. + * @param in The value to convert. + */ + inline string ToString(const std::string &in) + { + return in; + } + + /** Converts a char array to a string. + * @param in The value to convert. + */ + inline string ToString(const char *in) + { + return string(in); + } + + /** Converts a char to a string. + * @param in The value to convert. + */ + inline string ToString(char in) + { + return string(1, static_cast<string::value_type>(in)); + } + + /** Converts an unsigned char to a string. + * @param in The value to convert. + */ + inline string ToString(unsigned char in) + { + return string(1, static_cast<string::value_type>(in)); + } + + /** Converts a bool to a string. + * @param in The value to convert. + */ + inline string ToString(bool in) + { + return (in ? "1" : "0"); + } + + /** Converts a type that std::to_string is implemented for to a string. + * @param in The value to convert. + */ + template<typename Stringable> + inline std::enable_if_t<std::is_arithmetic_v<Stringable>, string> ToString(const Stringable &in) + { + return std::to_string(in); + } + + /** Converts any type to a string. + * @param in The value to convert. + */ + template <class T> + inline std::enable_if_t<!std::is_arithmetic_v<T>, string> ToString(const T &in) + { + return TryString(in).value_or(Anope::string()); + } +} diff --git a/include/modules/sql.h b/include/modules/sql.h index 8ac03803f..4c1b7d455 100644 --- a/include/modules/sql.h +++ b/include/modules/sql.h @@ -128,13 +128,12 @@ namespace SQL template<typename T> void SetValue(const Anope::string &key, const T &value, bool escape = true) { - try - { - Anope::string string_value = stringify(value); - this->parameters[key].data = string_value; - this->parameters[key].escape = escape; - } - catch (const ConvertException &ex) { } + auto str = Anope::TryString(value); + if (!str.has_value()) + return; + + this->parameters[key].data = str.value(); + this->parameters[key].escape = escape; } }; diff --git a/include/protocol.h b/include/protocol.h index 88bb94bfc..612593c89 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -164,14 +164,14 @@ public: template <typename... Args> void SendMode(const MessageSource &source, Channel *chan, const Anope::string &modes, Args &&...args) { - SendModeInternal(source, chan, modes, { stringify(args)... }); + SendModeInternal(source, chan, modes, { Anope::ToString(args)... }); } virtual void SendModeInternal(const MessageSource &source, User *u, const Anope::string &modes, const std::vector<Anope::string> &values); template <typename... Args> void SendMode(const MessageSource &source, User *u, const Anope::string &modes, Args &&...args) { - SendModeInternal(source, u, modes, { stringify(args)... }); + SendModeInternal(source, u, modes, { Anope::ToString(args)... }); } /** Introduces a client to the rest of the network @@ -258,7 +258,7 @@ public: template <typename... Args> void SendNumeric(int numeric, const Anope::string &dest, Args &&...args) { - SendNumericInternal(numeric, dest, { stringify(args)... }); + SendNumericInternal(numeric, dest, { Anope::ToString(args)... }); } virtual void SendLogin(User *u, NickAlias *na) = 0; diff --git a/include/uplink.h b/include/uplink.h index 12365dd65..924c05cfc 100644 --- a/include/uplink.h +++ b/include/uplink.h @@ -23,25 +23,25 @@ namespace Uplink template<typename... Args> void Send(const Anope::map<Anope::string> &tags, const MessageSource &source, const Anope::string &command, Args &&...args) { - SendInternal(tags, source, command, { stringify(args)... }); + SendInternal(tags, source, command, { Anope::ToString(args)... }); } template<typename... Args> void Send(const Anope::map<Anope::string> &tags, const Anope::string &command, Args &&...args) { - SendInternal(tags, Me, command, { stringify(args)... }); + SendInternal(tags, Me, command, { Anope::ToString(args)... }); } template<typename... Args> void Send(const MessageSource &source, const Anope::string &command, Args &&...args) { - SendInternal({}, source, command, { stringify(args)... }); + SendInternal({}, source, command, { Anope::ToString(args)... }); } template<typename... Args> void Send(const Anope::string &command, Args &&...args) { - SendInternal({}, Me, command, { stringify(args)... }); + SendInternal({}, Me, command, { Anope::ToString(args)... }); } } |