Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion docs/web/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ A function that may fail shall generally return either:
- an `std::unique_ptr` or an `std::shared_ptr` object holding if those
results are stored on the heap. In this case, the failure of the
function is indicated by the fact that the underlying pointer is null.
- a class to which an `InvalidValue` object is implicitly convertible.
- a class to which an `InvalidResult` object is implicitly convertible.

> ** Note **
>
Expand Down Expand Up @@ -129,6 +129,16 @@ For convenience, the `registerErrorMessage` always returns an invalid
value, i.e. a value that is convertible to any of the returned type
described in the previous section.


> **Note**
>
> In some rare case, it may happen that the implicit conversion is
> leading to a compile-time error ambiguous. This is for
> instance the case for functions returning an optional
> of a type that can be invalid. In this case, one may
> remove the ambiguity by specifying the expected return type
> a template parameter of the `registerErrorMessage` method.

By default, the error messages are packed up to the moment when error(s)
must be reported to the end-user. The error messages can then be
retrieved by the `getErrorMessage` method (or `getRawErrorMessage`, see
Expand Down
55 changes: 49 additions & 6 deletions include/MGIS/ErrorBacktrace.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ namespace mgis {
* \param[in] e: error message
* \param[in] l: description of the call site
*/
void assertOrTerminate(const bool,
const char *const,
const std::source_location & = std::source_location::current());
void assertOrTerminate(
const bool,
const char *const,
const std::source_location & = std::source_location::current());
/*!
* \brief assert that the given boolean is true. If `false`, `terminate` is
* called.
Expand All @@ -82,9 +83,10 @@ namespace mgis {
* \param[in] e: error code
* \param[in] l: description of the call site
*/
void assertOrTerminate(const bool,
const ErrorReport,
const std::source_location & = std::source_location::current());
void assertOrTerminate(
const bool,
const ErrorReport,
const std::source_location & = std::source_location::current());
/*!
* \brief terminate the excution after displaying the given error message
* \param[in] e: error message
Expand Down Expand Up @@ -120,6 +122,30 @@ namespace mgis {
const ErrorReport,
const std::source_location & =
std::source_location::current()) noexcept;
/*!
* \brief register a new error message
* \param[in] e: error message
* \param[in] l: description of the call site
* \note for convenience, this method always return an invalid object
*/
template <typename ReturnType>
[[nodiscard]] ReturnType registerErrorMessage(
const char *const,
const std::source_location & = std::source_location::current()) //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized);
/*!
* \brief register a new error message
* \param[in] e: error code
* \param[in] l: description of the call site
* \note for convenience, this method always return an invalid object
*/
template <typename ReturnType>
[[nodiscard]] InvalidResult registerErrorMessage(
const ErrorReport,
const std::source_location & =
std::source_location::current()) noexcept //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized);

#else
/*!
* \brief assert that the given boolean is true. If `false`, `terminate` is
Expand Down Expand Up @@ -160,6 +186,23 @@ namespace mgis {
*/
[[nodiscard]] InvalidResult registerErrorMessage(
const ErrorReport) noexcept;
/*!
* \brief register a new error message
* \param[in] e: error message
* \note for convenience, this method always return an invalid object
*/
template <typename ReturnType>
[[nodiscard]] ReturnType registerErrorMessage(const char *const) //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized);
/*!
* \brief register a new error message
* \param[in] e: error code
* \note for convenience, this method always return an invalid results
*/
template <typename ReturnType>
[[nodiscard]] ReturnType registerErrorMessage(
const ErrorReport) noexcept //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized);
#endif
/*!
* \brief register a new error message
Expand Down
33 changes: 33 additions & 0 deletions include/MGIS/ErrorBacktrace.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ namespace mgis {
}
} // end of assertOrTerminate

template <typename ReturnType>
ReturnType ErrorBacktrace::registerErrorMessage(
const char *const e,
const std::source_location &l) //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized) {
std::ignore = this->registerErrorMessage(e, l);
return internal::InvalidValueTraits<ReturnType>::getValue();
} // end of registerErrorMessage

template <typename ReturnType>
ReturnType ErrorBacktrace::registerErrorMessage(
const ErrorReport e,
const std::source_location &l) noexcept //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized) {
std::ignore = this->registerErrorMessage(e, l);
return internal::InvalidValueTraits<ReturnType>::getValue();
} // end of registerErrorMessage

#else

inline void ErrorBacktrace::assertOrTerminate(const bool b, const char *msg) {
Expand All @@ -47,6 +65,21 @@ namespace mgis {
}
} // end of assertOrTerminate

template <typename ReturnType>
ReturnType ErrorBacktrace::registerErrorMessage(const char *const e) //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized) {
std::ignore = this->registerErrorMessage(e);
return internal::InvalidValueTraits<ReturnType>::getValue();
} // end of registerErrorMessage

template <typename ReturnType>
ReturnType ErrorBacktrace::registerErrorMessage(
const ErrorReport e) noexcept //
requires(internal::InvalidValueTraits<ReturnType>::isSpecialized) {
std::ignore = this->registerErrorMessage(e);
return internal::InvalidValueTraits<ReturnType>::getValue();
} // end of registerErrorMessage

#endif

} // end of namespace mgis
Expand Down
9 changes: 0 additions & 9 deletions include/MGIS/InvalidResult.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@ namespace mgis::internal {
static constexpr auto getValue() noexcept { return std::optional<T>{}; }
};

//! \brief partial specialisation for std::optional
template <typename T>
struct InvalidValueTraits<std::optional<const T>> {
static constexpr bool isSpecialized = true;
static constexpr auto getValue() noexcept {
return std::optional<const T>{};
}
};

//! \brief partial specialisation for std::optional<bool>
template <>
struct InvalidValueTraits<std::optional<bool>> {
Expand Down
13 changes: 13 additions & 0 deletions tests/ErrorBacktraceTest.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ namespace mgis::internal {
return true;
}

static std::optional<std::unique_ptr<int>> f4(ErrorBacktrace &e) {
return e.registerErrorMessage<std::optional<std::unique_ptr<int>>>(
"using explicit return type, because of std::optional permissive "
"constructor would not allow to use InvalidResult");
}

} // end of namespace mgis::internal

#endif /* MGIS_USE_EXCEPTIONS_FOR_ERROR_REPORTING */
Expand Down Expand Up @@ -61,6 +67,13 @@ int main() {
expect_false(::mgis::internal::f1(e));
expect_eq(e.getRawErrorMessage(), "invalid call to f2\n* invalid call");
} // end of ConstructTest
{
ErrorBacktrace e;
expect_false(isValid(::mgis::internal::f4(e)));
expect_eq(e.getRawErrorMessage(),
"using explicit return type, because of std::optional permissive "
"constructor would not allow to use InvalidResult");
} // end of ConstructTest
{
ErrorBacktrace e;
std::ignore = e.registerErrorMessage(
Expand Down
Loading