diff options
76 files changed, 573 insertions, 811 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)... }); } } diff --git a/modules/botserv/botserv.cpp b/modules/botserv/botserv.cpp index d2b8df4bb..706865522 100644 --- a/modules/botserv/botserv.cpp +++ b/modules/botserv/botserv.cpp @@ -67,12 +67,8 @@ public: Anope::string Limit; unsigned limit = 0; - try - { - if (c->GetParam("LIMIT", Limit)) - limit = convertTo<unsigned>(Limit); - } - catch (const ConvertException &) { } + if (c->GetParam("LIMIT", Limit)) + limit = Anope::Convert<unsigned>(Limit, limit); /* Should we be invited? */ if (c->HasMode("INVITE") || (limit && c->users.size() >= limit)) diff --git a/modules/botserv/bs_badwords.cpp b/modules/botserv/bs_badwords.cpp index 5dee8ed2c..09e96564b 100644 --- a/modules/botserv/bs_badwords.cpp +++ b/modules/botserv/bs_badwords.cpp @@ -222,7 +222,7 @@ private: const BadWord *b = bw->GetBadWord(Number - 1); ListFormatter::ListEntry entry; - entry["Number"] = stringify(Number); + entry["Number"] = Anope::ToString(Number); entry["Word"] = b->word; entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : "")); this->list.AddEntry(entry); @@ -241,7 +241,7 @@ private: continue; ListFormatter::ListEntry entry; - entry["Number"] = stringify(i + 1); + entry["Number"] = Anope::ToString(i + 1); entry["Word"] = b->word; entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : "")); list.AddEntry(entry); diff --git a/modules/botserv/bs_info.cpp b/modules/botserv/bs_info.cpp index c8e05577b..b4a5592b1 100644 --- a/modules/botserv/bs_info.cpp +++ b/modules/botserv/bs_info.cpp @@ -55,7 +55,7 @@ public: info[_("Real name")] = bi->realname; info[_("Created")] = Anope::strftime(bi->created, source.GetAccount()); info[_("Options")] = bi->oper_only ? _("Private") : _("None"); - info[_("Used on")] = stringify(bi->GetChannelCount()) + " channel(s)"; + info[_("Used on")] = Anope::ToString(bi->GetChannelCount()) + " channel(s)"; FOREACH_MOD(OnBotInfo, (source, bi, ci, info)); diff --git a/modules/botserv/bs_kick.cpp b/modules/botserv/bs_kick.cpp index 29c8cb598..9183eae22 100644 --- a/modules/botserv/bs_kick.cpp +++ b/modules/botserv/bs_kick.cpp @@ -106,11 +106,10 @@ struct KickerDataImpl final data["ttb"] >> ttb; spacesepstream sep(ttb); for (int i = 0; sep.GetToken(tok) && i < TTB_SIZE; ++i) - try - { - kd->ttb[i] = convertTo<int16_t>(tok); - } - catch (const ConvertException &) { } + { + if (auto n = Anope::TryConvert<int16_t>(tok)) + kd->ttb[i] = n.value(); + } kd->Check(ci); } @@ -206,21 +205,13 @@ protected: { if (!ttb.empty()) { - int16_t i; - - try - { - i = convertTo<int16_t>(ttb); - if (i < 0) - throw ConvertException(); - } - catch (const ConvertException &) + kd->ttb[ttb_idx] = Anope::Convert<int16_t>(ttb, -1); + if (kd->ttb[ttb_idx] < 0) { + kd->ttb[ttb_idx] = 0; source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); return; } - - kd->ttb[ttb_idx] = i; } else kd->ttb[ttb_idx] = 0; @@ -386,13 +377,8 @@ public: if (!ttb.empty()) { - try - { - kd->ttb[TTB_CAPS] = convertTo<int16_t>(ttb); - if (kd->ttb[TTB_CAPS] < 0) - throw ConvertException(); - } - catch (const ConvertException &) + kd->ttb[TTB_CAPS] = Anope::Convert<int16_t>(ttb, -1); + if (kd->ttb[TTB_CAPS] < 0) { kd->ttb[TTB_CAPS] = 0; source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); @@ -402,21 +388,11 @@ public: else kd->ttb[TTB_CAPS] = 0; - kd->capsmin = 10; - try - { - kd->capsmin = convertTo<int16_t>(min); - } - catch (const ConvertException &) { } + kd->capsmin = Anope::Convert(min, 0); if (kd->capsmin < 1) kd->capsmin = 10; - kd->capspercent = 25; - try - { - kd->capspercent = convertTo<int16_t>(percent); - } - catch (const ConvertException &) { } + kd->capspercent = Anope::Convert(percent, 0); if (kd->capspercent < 1 || kd->capspercent > 100) kd->capspercent = 25; @@ -518,42 +494,25 @@ public: if (!ttb.empty()) { - int16_t i; - - try - { - i = convertTo<int16_t>(ttb); - if (i < 0) - throw ConvertException(); - } - catch (const ConvertException &) + kd->ttb[TTB_FLOOD] = Anope::Convert<int16_t>(ttb, -1); + if (kd->ttb[TTB_FLOOD] < 0) { + kd->ttb[TTB_FLOOD] = 0; source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); return; } - - kd->ttb[TTB_FLOOD] = i; } else kd->ttb[TTB_FLOOD] = 0; - kd->floodlines = 6; - try - { - kd->floodlines = convertTo<int16_t>(lines); - } - catch (const ConvertException &) { } + kd->floodlines = Anope::Convert(lines, -1); if (kd->floodlines < 2) kd->floodlines = 6; - kd->floodsecs = 10; - try - { - kd->floodsecs = convertTo<int16_t>(secs); - } - catch (const ConvertException &) { } + kd->floodsecs = Anope::Convert(secs, -1); if (kd->floodsecs < 1) kd->floodsecs = 10; + if (kd->floodsecs > Config->GetModule(me)->Get<time_t>("keepdata")) kd->floodsecs = Config->GetModule(me)->Get<time_t>("keepdata"); @@ -651,31 |
