1
0
mirror of https://github.com/anope/anope.git synced 2026-07-07 19:43:14 +02:00

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.
This commit is contained in:
Sadie Powell
2024-03-11 13:53:05 +00:00
parent e2df7d4d01
commit 29e7674e56
76 changed files with 572 additions and 810 deletions
+3 -66
View File
@@ -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"
+1 -8
View File
@@ -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);
+133
View File
@@ -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());
}
}
+6 -7
View File
@@ -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;
}
};
+3 -3
View File
@@ -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;
+4 -4
View File
@@ -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)... });
}
}