summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2024-03-11 13:53:05 +0000
committerSadie Powell <sadie@witchery.services>2024-03-11 19:17:29 +0000
commit29e7674e56bf2b829bba22def2760d034a76e788 (patch)
treef40049ba995b03dd7c510d88f9f19db2d2e65a2e /include
parente2df7d4d01f8fdb41c49ce8efc462cab005e7d5c (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.h69
-rw-r--r--include/config.h9
-rw-r--r--include/convert.h133
-rw-r--r--include/modules/sql.h13
-rw-r--r--include/protocol.h6
-rw-r--r--include/uplink.h8
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)... });
}
}