Skip to content

Commit

Permalink
lowlevel: Require error types in Results to be stringifiable
Browse files Browse the repository at this point in the history
  • Loading branch information
caseif committed Jun 19, 2024
1 parent 4813074 commit 8b3a7cd
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 4 deletions.
2 changes: 2 additions & 0 deletions engine/libs/lowlevel/include/argus/lowlevel/filesystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ namespace argus {
struct FileOpenError {
FileOpenErrorReason reason;
int error_code;

std::string to_string(void) const;
};

/**
Expand Down
73 changes: 69 additions & 4 deletions engine/libs/lowlevel/include/argus/lowlevel/result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,50 @@ namespace argus {
template<typename T, typename E>
class Result;

template<typename, typename T>
struct has_to_string_fn : std::false_type {
};

template<typename T>
struct has_to_string_fn<T, std::enable_if_t<
std::is_same_v<std::string, typename function_traits<decltype(&T::to_string)>::return_type>
&& std::tuple_size_v<typename function_traits<decltype(&T::to_string)>::argument_types> == 0
&& function_traits<decltype(&T::to_string)>::is_const::value,
void>> : std::true_type {
};

template<typename T>
constexpr bool has_to_string_fn_v = has_to_string_fn<T, void>::value;

template<typename, typename T>
struct is_stringifiable : std::false_type {
};

template<>
struct is_stringifiable<void, void> : std::true_type {
};

template<>
struct is_stringifiable<std::string, void> : std::true_type {
};

template<>
struct is_stringifiable<std::string &, void> : std::true_type {
};

template<>
struct is_stringifiable<const std::string &, void> : std::true_type {
};

template<typename T>
struct is_stringifiable<T,
std::void_t<decltype(std::to_string(*static_cast<std::remove_reference_t<T> *>(nullptr)))>>
: std::true_type {
};

template<typename T>
constexpr bool is_stringifiable_v = is_stringifiable<T, void>::value;

template<typename T, typename E>
std::enable_if_t<!std::is_void_v<T>, Result<T, E>> ok(T value);

Expand Down Expand Up @@ -80,9 +124,30 @@ namespace argus {

template<typename T, typename E>
class Result {
static_assert(is_stringifiable_v<E> || has_to_string_fn_v<E>,
"Error type of Result must be a string or otherwise provide a valid to_string member function");

private:
ResultStorage<T, E> m_storage;

std::string stringify_error(void) const {
if (!is_err()) {
crash_ll("Cannot stringify non-error Result");
}

if constexpr (std::is_void_v<E>) {
return "(void)";
} else if constexpr (std::is_same_v<std::string, std::remove_cv_t<std::remove_reference_t<E>>>) {
return unwrap_err();
} else if constexpr (std::is_arithmetic_v<std::remove_reference_t<E>>) {
return std::to_string(unwrap_err());
} else if constexpr (has_to_string_fn_v<E>) {
return unwrap_err().to_string();
} else {
static_assert(false, "Cannot apply stringify_error to type");
}
}

public:
// workaround for VS <=17.2, see comment in ResultStorage
// definition
Expand Down Expand Up @@ -119,7 +184,7 @@ namespace argus {
template<typename U = T>
[[nodiscard]] std::enable_if_t<!std::is_void_v<U>, const U &> unwrap(void) const {
if (!is_ok()) {
crash_ll("Attempted to call unwrap() on error-typed Result");
crash_ll("Attempted to call unwrap() on error-typed Result (" + stringify_error() + ")");
}
if constexpr (!std::is_void_v<E>) {
#if defined(_MSC_VER) && (_MSVC_STL_UPDATE < 202203L)
Expand All @@ -142,7 +207,7 @@ namespace argus {
template<typename U = E>
[[nodiscard]] std::enable_if_t<!std::is_void_v<U>, const U &> unwrap_err(void) const {
if (!is_err()) {
crash_ll("Attempted to call unwrap() on value-typed Result");
crash_ll("Attempted to call unwrap_err() on value-typed Result");
}
if constexpr (!std::is_void_v<T>) {
#if defined(_MSC_VER) && (_MSVC_STL_UPDATE < 202203L)
Expand Down Expand Up @@ -239,7 +304,7 @@ namespace argus {
template<typename U = T>
std::enable_if_t<!std::is_void_v<U>, const U &> expect(const std::string &msg) const {
if (!is_ok()) {
crash_ll(msg);
crash_ll(msg + " (" + stringify_error() + ")");
}
return unwrap();
}
Expand All @@ -252,7 +317,7 @@ namespace argus {
template<typename U = T>
std::enable_if_t<std::is_void_v<U>, void> expect(const std::string &msg) const {
if (!is_ok()) {
crash_ll(msg);
crash_ll(msg + " (" + stringify_error() + ")");
}
}

Expand Down
9 changes: 9 additions & 0 deletions engine/libs/lowlevel/src/filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@
#define CHUNK_SIZE 4096LU

namespace argus {
std::string FileOpenError::to_string(void) const {
return "FileOpenError { "
"reason = "
+ std::to_string(std::underlying_type_t<FileOpenErrorReason>(reason))
+ ", error_code = "
+ std::to_string(error_code)
+ " }";
}

static FileOpenErrorReason _map_file_error(int code) {
switch (code) {
case EPERM:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ namespace argus {
ResourceErrorReason reason;
std::string uid;
std::string info;

std::string to_string(void) const;
};

/**
Expand Down
11 changes: 11 additions & 0 deletions engine/static/resman/src/resource_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@
#define RESOURCES_DIR "resources"

namespace argus {
std::string ResourceError::to_string(void) const {
return "ResourceError { "
"reason = "
+ std::to_string(std::underlying_type_t<ResourceErrorReason>(reason))
+ ", uid = \""
+ uid
+ "\", info = \""
+ info
+ "\" }";
}

static void _load_initial_ext_mappings(std::map<std::string, std::string> &target) {
size_t count = 0;
extension_mapping_t *mappings = arp_get_extension_mappings(&count);
Expand Down
2 changes: 2 additions & 0 deletions engine/static/scripting/include/argus/scripting/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ namespace argus {
struct BindingError {
std::string bound_name;
std::string msg;

std::string to_string(void) const;
};
}
30 changes: 30 additions & 0 deletions engine/static/scripting/src/error.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* This file is a part of Argus.
* Copyright (c) 2019-2024, Max Roncace <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "argus/scripting/error.hpp"

namespace argus {
std::string BindingError::to_string(void) const {
return "{ "
"bound_name = \""
+ bound_name
+ "\", message = \""
+ msg
+ "\" }";
}
}

0 comments on commit 8b3a7cd

Please sign in to comment.