Browse Source

Add `tl::expected` and use it in `ParseInt`

`std::expected` has been standardized for C++23.
pull/6488/head
Gleb Mazovetskiy 3 years ago
parent
commit
ebe8c6e576
  1. 2444
      3rdParty/tl/expected.hpp
  2. 2
      Source/control.cpp
  3. 60
      Source/debug.cpp
  4. 8
      Source/diablo.cpp
  5. 12
      Source/pfile.cpp
  6. 31
      Source/utils/parse_int.hpp

2444
3rdParty/tl/expected.hpp vendored

File diff suppressed because it is too large Load Diff

2
Source/control.cpp

@ -390,7 +390,7 @@ std::string TextCmdArena(const std::string_view parameter)
} }
const ParseIntResult<int> parsedParam = ParseInt<int>(parameter, /*min=*/0); const ParseIntResult<int> parsedParam = ParseInt<int>(parameter, /*min=*/0);
const _setlevels arenaLevel = parsedParam.ok() ? static_cast<_setlevels>(parsedParam.value - 1 + SL_FIRST_ARENA) : _setlevels::SL_NONE; const _setlevels arenaLevel = parsedParam.has_value() ? static_cast<_setlevels>(parsedParam.value() - 1 + SL_FIRST_ARENA) : _setlevels::SL_NONE;
if (!IsArenaLevel(arenaLevel)) { if (!IsArenaLevel(arenaLevel)) {
StrAppend(ret, _("Invalid arena-number. Valid numbers are:")); StrAppend(ret, _("Invalid arena-number. Valid numbers are:"));
AppendArenaOverview(ret); AppendArenaOverview(ret);

60
Source/debug.cpp

@ -195,9 +195,9 @@ std::string DebugCmdWarpToLevel(const std::string_view parameter)
{ {
Player &myPlayer = *MyPlayer; Player &myPlayer = *MyPlayer;
const ParseIntResult<int> parsedParam = ParseInt<int>(parameter, /*min=*/0); const ParseIntResult<int> parsedParam = ParseInt<int>(parameter, /*min=*/0);
if (!parsedParam.ok()) if (!parsedParam.has_value())
return "Specify the level number"; return "Specify the level number";
const int level = parsedParam.value; const int level = parsedParam.value();
if (level > (gbIsHellfire ? 24 : 16)) if (level > (gbIsHellfire ? 24 : 16))
return StrCat("Level ", level, " is not known. Do you want to write a mod?"); return StrCat("Level ", level, " is not known. Do you want to write a mod?");
if (!setlevel && myPlayer.isOnLevel(level)) if (!setlevel && myPlayer.isOnLevel(level))
@ -220,9 +220,9 @@ std::string DebugCmdLoadQuestMap(const std::string_view parameter)
} }
const ParseIntResult<int> parsedParam = ParseInt<int>(parameter, /*min=*/0); const ParseIntResult<int> parsedParam = ParseInt<int>(parameter, /*min=*/0);
if (!parsedParam.ok()) if (!parsedParam.has_value())
return "Specify the level number"; return "Specify the level number";
const int level = parsedParam.value; const int level = parsedParam.value();
if (level < 1) if (level < 1)
return "Map id must be 1 or higher"; return "Map id must be 1 or higher";
if (setlevel && setlvlnum == level) if (setlevel && setlvlnum == level)
@ -255,21 +255,21 @@ std::string DebugCmdLoadMap(const std::string_view parameter)
break; break;
case 1: { case 1: {
const ParseIntResult<int> parsedArg = ParseInt<int>(arg, /*min=*/0); const ParseIntResult<int> parsedArg = ParseInt<int>(arg, /*min=*/0);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument 1 as integer"; return "Failed to parse argument 1 as integer";
mapType = parsedArg.value; mapType = parsedArg.value();
} break; } break;
case 2: { case 2: {
const ParseIntResult<int> parsedArg = ParseInt<int>(arg); const ParseIntResult<int> parsedArg = ParseInt<int>(arg);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument 2 as integer"; return "Failed to parse argument 2 as integer";
spawn.x = parsedArg.value; spawn.x = parsedArg.value();
} break; } break;
case 3: { case 3: {
const ParseIntResult<int> parsedArg = ParseInt<int>(arg); const ParseIntResult<int> parsedArg = ParseInt<int>(arg);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument 3 as integer"; return "Failed to parse argument 3 as integer";
spawn.y = parsedArg.value; spawn.y = parsedArg.value();
} break; } break;
} }
count++; count++;
@ -418,9 +418,9 @@ std::string DebugCmdResetLevel(const std::string_view parameter)
int level; int level;
{ {
const ParseIntResult<int> parsedArg = ParseInt<int>(*it, /*min=*/0); const ParseIntResult<int> parsedArg = ParseInt<int>(*it, /*min=*/0);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument 1 as integer"; return "Failed to parse argument 1 as integer";
level = parsedArg.value; level = parsedArg.value();
} }
if (level > (gbIsHellfire ? 24 : 16)) if (level > (gbIsHellfire ? 24 : 16))
return StrCat("Level ", level, " is not known. Do you want to write an extension mod?"); return StrCat("Level ", level, " is not known. Do you want to write an extension mod?");
@ -429,9 +429,9 @@ std::string DebugCmdResetLevel(const std::string_view parameter)
if (++it != args.end()) { if (++it != args.end()) {
const ParseIntResult<uint32_t> parsedArg = ParseInt<uint32_t>(*it); const ParseIntResult<uint32_t> parsedArg = ParseInt<uint32_t>(*it);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument 2 as uint32_t"; return "Failed to parse argument 2 as uint32_t";
glSeedTbl[level] = parsedArg.value; glSeedTbl[level] = parsedArg.value();
} }
if (myPlayer.isOnLevel(level)) if (myPlayer.isOnLevel(level))
@ -515,9 +515,9 @@ std::string DebugCmdQuest(const std::string_view parameter)
} }
const ParseIntResult<int> parsedArg = ParseInt<int>(parameter, /*min=*/0); const ParseIntResult<int> parsedArg = ParseInt<int>(parameter, /*min=*/0);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument as integer"; return "Failed to parse argument as integer";
const int questId = parsedArg.value; const int questId = parsedArg.value();
if (questId >= MAXQUESTS) if (questId >= MAXQUESTS)
return StrCat("Quest ", questId, " is not known. Do you want to write a mod?"); return StrCat("Quest ", questId, " is not known. Do you want to write a mod?");
@ -563,9 +563,9 @@ std::string DebugCmdMinStats(const std::string_view parameter)
std::string DebugCmdSetSpellsLevel(const std::string_view parameter) std::string DebugCmdSetSpellsLevel(const std::string_view parameter)
{ {
const ParseIntResult<uint8_t> parsedArg = ParseInt<uint8_t>(parameter); const ParseIntResult<uint8_t> parsedArg = ParseInt<uint8_t>(parameter);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument as uint8_t"; return "Failed to parse argument as uint8_t";
const uint8_t level = parsedArg.value; const uint8_t level = parsedArg.value();
for (uint8_t i = static_cast<uint8_t>(SpellID::Firebolt); i < MAX_SPELLS; i++) { for (uint8_t i = static_cast<uint8_t>(SpellID::Firebolt); i < MAX_SPELLS; i++) {
if (GetSpellBookLevel(static_cast<SpellID>(i)) != -1) { if (GetSpellBookLevel(static_cast<SpellID>(i)) != -1) {
NetSendCmdParam2(true, CMD_CHANGE_SPELL_LEVEL, i, level); NetSendCmdParam2(true, CMD_CHANGE_SPELL_LEVEL, i, level);
@ -595,9 +595,9 @@ std::string DebugCmdChangeHealth(const std::string_view parameter)
if (!parameter.empty()) { if (!parameter.empty()) {
const ParseIntResult<int> parsedArg = ParseInt<int>(parameter); const ParseIntResult<int> parsedArg = ParseInt<int>(parameter);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument as integer"; return "Failed to parse argument as integer";
change = parsedArg.value; change = parsedArg.value();
} }
if (change == 0) if (change == 0)
@ -618,9 +618,9 @@ std::string DebugCmdChangeMana(const std::string_view parameter)
if (!parameter.empty()) { if (!parameter.empty()) {
const ParseIntResult<int> parsedArg = ParseInt<int>(parameter); const ParseIntResult<int> parsedArg = ParseInt<int>(parameter);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument as integer"; return "Failed to parse argument as integer";
change = parsedArg.value; change = parsedArg.value();
} }
if (change == 0) if (change == 0)
@ -699,9 +699,9 @@ std::string DebugCmdSpawnUniqueMonster(const std::string_view parameter)
int count = 1; int count = 1;
for (std::string_view arg : SplitByChar(parameter, ' ')) { for (std::string_view arg : SplitByChar(parameter, ' ')) {
const ParseIntResult<int> parsedArg = ParseInt<int>(arg); const ParseIntResult<int> parsedArg = ParseInt<int>(arg);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument as integer"; return "Failed to parse argument as integer";
const int num = parsedArg.value; const int num = parsedArg.value();
if (num > 0) { if (num > 0) {
count = num; count = num;
break; break;
@ -789,9 +789,9 @@ std::string DebugCmdSpawnMonster(const std::string_view parameter)
int count = 1; int count = 1;
for (std::string_view arg : SplitByChar(parameter, ' ')) { for (std::string_view arg : SplitByChar(parameter, ' ')) {
const ParseIntResult<int> parsedArg = ParseInt<int>(arg); const ParseIntResult<int> parsedArg = ParseInt<int>(arg);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument as integer"; return "Failed to parse argument as integer";
const int num = parsedArg.value; const int num = parsedArg.value();
if (num > 0) { if (num > 0) {
count = num; count = num;
break; break;
@ -966,9 +966,9 @@ std::string DebugCmdQuestInfo(const std::string_view parameter)
} }
const ParseIntResult<int> parsedArg = ParseInt<int>(parameter, /*min=*/0); const ParseIntResult<int> parsedArg = ParseInt<int>(parameter, /*min=*/0);
if (!parsedArg.ok()) if (!parsedArg.has_value())
return "Failed to parse argument as integer"; return "Failed to parse argument as integer";
const int questId = parsedArg.value; const int questId = parsedArg.value();
if (questId >= MAXQUESTS) if (questId >= MAXQUESTS)
return StrCat("Quest ", questId, " is not known. Do you want to write a mod?"); return StrCat("Quest ", questId, " is not known. Do you want to write a mod?");
@ -982,10 +982,10 @@ std::string DebugCmdPlayerInfo(const std::string_view parameter)
return StrCat("Provide a player ID between 0 and ", Players.size() - 1); return StrCat("Provide a player ID between 0 and ", Players.size() - 1);
} }
const ParseIntResult<size_t> parsedArg = ParseInt<size_t>(parameter); const ParseIntResult<size_t> parsedArg = ParseInt<size_t>(parameter);
if (!parsedArg.ok()) { if (!parsedArg.has_value()) {
return "Failed to parse argument as size_t in range"; return "Failed to parse argument as size_t in range";
} }
const size_t playerId = parsedArg.value; const size_t playerId = parsedArg.value();
if (static_cast<size_t>(playerId) >= Players.size()) if (static_cast<size_t>(playerId) >= Players.size())
return "My friend, we need a valid playerId."; return "My friend, we need a valid playerId.";
Player &player = Players[playerId]; Player &player = Players[playerId];

8
Source/diablo.cpp

@ -1015,11 +1015,11 @@ void DiabloParseFlags(int argc, char **argv)
diablo_quit(64); diablo_quit(64);
} }
ParseIntResult<int> parsedParam = ParseInt<int>(argv[++i]); ParseIntResult<int> parsedParam = ParseInt<int>(argv[++i]);
if (!parsedParam.ok()) { if (!parsedParam.has_value()) {
PrintFlagMessage("--demo", " must be a number"); PrintFlagMessage("--demo", " must be a number");
diablo_quit(64); diablo_quit(64);
} }
demoNumber = parsedParam.value; demoNumber = parsedParam.value();
gbShowIntro = false; gbShowIntro = false;
} else if (arg == "--timedemo") { } else if (arg == "--timedemo") {
timedemo = true; timedemo = true;
@ -1029,11 +1029,11 @@ void DiabloParseFlags(int argc, char **argv)
diablo_quit(64); diablo_quit(64);
} }
ParseIntResult<int> parsedParam = ParseInt<int>(argv[++i]); ParseIntResult<int> parsedParam = ParseInt<int>(argv[++i]);
if (!parsedParam.ok()) { if (!parsedParam.has_value()) {
PrintFlagMessage("--record", " must be a number"); PrintFlagMessage("--record", " must be a number");
diablo_quit(64); diablo_quit(64);
} }
recordNumber = parsedParam.value; recordNumber = parsedParam.value();
} else if (arg == "--create-reference") { } else if (arg == "--create-reference") {
createDemoReference = true; createDemoReference = true;
#else #else

12
Source/pfile.cpp

@ -285,9 +285,9 @@ void CreateDetailDiffs(std::string_view prefix, std::string_view memoryMapFile,
if (it != counter.end()) if (it != counter.end())
return it->second; return it->second;
const ParseIntResult<int> countFromMapFile = ParseInt<int>(counterAsString); const ParseIntResult<int> countFromMapFile = ParseInt<int>(counterAsString);
if (!countFromMapFile.ok()) if (!countFromMapFile.has_value())
app_fatal(StrCat("Failed to parse ", counterAsString, " as int")); app_fatal(StrCat("Failed to parse ", counterAsString, " as int"));
return CompareCounter { countFromMapFile.value, countFromMapFile.value }; return CompareCounter { countFromMapFile.value(), countFromMapFile.value() };
}; };
auto addDiff = [&](const std::string &diffKey) { auto addDiff = [&](const std::string &diffKey) {
auto it = foundDiffs.find(diffKey); auto it = foundDiffs.find(diffKey);
@ -366,9 +366,9 @@ void CreateDetailDiffs(std::string_view prefix, std::string_view memoryMapFile,
const auto bitsAsString = std::string(*++it); const auto bitsAsString = std::string(*++it);
const auto comment = std::string(*++it); const auto comment = std::string(*++it);
const ParseIntResult<size_t> parsedBytes = ParseInt<size_t>(bitsAsString); const ParseIntResult<size_t> parsedBytes = ParseInt<size_t>(bitsAsString);
if (!parsedBytes.ok()) if (!parsedBytes.has_value())
app_fatal(StrCat("Failed to parse ", bitsAsString, " as size_t")); app_fatal(StrCat("Failed to parse ", bitsAsString, " as size_t"));
const size_t bytes = static_cast<size_t>(parsedBytes.value / 8); const size_t bytes = static_cast<size_t>(parsedBytes.value() / 8);
if (command == "LT") { if (command == "LT") {
int32_t valueReference = read32BitInt(compareInfoReference, false); int32_t valueReference = read32BitInt(compareInfoReference, false);
@ -395,9 +395,9 @@ void CreateDetailDiffs(std::string_view prefix, std::string_view memoryMapFile,
CompareCounter count = getCounter(countAsString); CompareCounter count = getCounter(countAsString);
const ParseIntResult<size_t> parsedBytes = ParseInt<size_t>(bitsAsString); const ParseIntResult<size_t> parsedBytes = ParseInt<size_t>(bitsAsString);
if (!parsedBytes.ok()) if (!parsedBytes.has_value())
app_fatal(StrCat("Failed to parse ", bitsAsString, " as size_t")); app_fatal(StrCat("Failed to parse ", bitsAsString, " as size_t"));
const size_t bytes = static_cast<size_t>(parsedBytes.value / 8); const size_t bytes = static_cast<size_t>(parsedBytes.value() / 8);
for (int i = 0; i < count.max(); i++) { for (int i = 0; i < count.max(); i++) {
count.checkIfDataExists(i, compareInfoReference, compareInfoActual); count.checkIfDataExists(i, compareInfoReference, compareInfoActual);
if (!compareBytes(bytes)) { if (!compareBytes(bytes)) {

31
Source/utils/parse_int.hpp

@ -4,30 +4,17 @@
#include <string_view> #include <string_view>
#include <system_error> #include <system_error>
#include <expected.hpp>
namespace devilution { namespace devilution {
enum class ParseIntStatus { enum class ParseIntError {
Ok, ParseError = 1,
ParseError,
OutOfRange OutOfRange
}; };
template <typename IntT> template <typename IntT>
struct ParseIntResult { using ParseIntResult = tl::expected<IntT, ParseIntError>;
ParseIntStatus status;
IntT value = 0;
[[nodiscard]] bool ok() const
{
return status == ParseIntStatus::Ok;
}
template <typename T>
[[nodiscard]] IntT value_or(T defaultValue) const // NOLINT(readability-identifier-naming)
{
return ok() ? value : static_cast<IntT>(defaultValue);
}
};
template <typename IntT> template <typename IntT>
ParseIntResult<IntT> ParseInt( ParseIntResult<IntT> ParseInt(
@ -37,12 +24,12 @@ ParseIntResult<IntT> ParseInt(
IntT value; IntT value;
const std::from_chars_result result = std::from_chars(str.data(), str.data() + str.size(), value); const std::from_chars_result result = std::from_chars(str.data(), str.data() + str.size(), value);
if (result.ec == std::errc::invalid_argument) if (result.ec == std::errc::invalid_argument)
return ParseIntResult<IntT> { ParseIntStatus::ParseError }; return tl::unexpected(ParseIntError::ParseError);
if (result.ec == std::errc::result_out_of_range || value < min || value > max) if (result.ec == std::errc::result_out_of_range || value < min || value > max)
return ParseIntResult<IntT> { ParseIntStatus::OutOfRange }; return tl::unexpected(ParseIntError::OutOfRange);
if (result.ec != std::errc()) if (result.ec != std::errc())
return ParseIntResult<IntT> { ParseIntStatus::ParseError }; return tl::unexpected(ParseIntError::ParseError);
return ParseIntResult<IntT> { ParseIntStatus::Ok, value }; return value;
} }
} // namespace devilution } // namespace devilution

Loading…
Cancel
Save