Error.h#
Fully qualified name: carb/Error.h
File members: carb/Error.h
// SPDX-FileCopyrightText: Copyright (c) 2023-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.
#pragma once
#include "Deprecation.h"
#include "Interface.h"
#include "Types.h"
#include "Version.h"
#include "cpp/ZStringView.h"
#include "detail/DeferredLoad.h"
#include "extras/Errors.h"
#include "thread/Util.h"
#include "../omni/core/Result.h"
#include "../omni/Expected.h"
#include "../omni/String.h"
#include <cinttypes>
#define carb_ErrorApi_latest CARB_HEXVERSION(2, 0)
#ifndef carb_ErrorApi
# define carb_ErrorApi CARB_HEXVERSION(1, 1)
#endif
#ifndef CARB_ERRORAPI_DEPRECATIONS
# define CARB_ERRORAPI_DEPRECATIONS 1
#endif
#if CARB_ERRORAPI_DEPRECATIONS && CARB_VERSION_ATLEAST(carb_ErrorApi, 1, 1)
# define CARB_ERRORAPI_DEPRECATED(msg) CARB_DEPRECATED(msg)
#else
# define CARB_ERRORAPI_DEPRECATED(msg)
#endif
namespace carb
{
#define CARBLOCAL_GEN(symbol, ...) \
\
k##symbol = omni::core::kResult##symbol,
enum class ErrorCode : std::int32_t
{
OMNI_RESULT_ERROR_LIST(CARBLOCAL_GEN)
};
#undef CARBLOCAL_GEN
using omni::core::Result;
// bring all the kResultXXX values into carb namespace
#define CARB_RESULT_USE_OMNI_RESULT_GEN(symbol_, ...) \
\
using omni::core::kResult##symbol_;
OMNI_RESULT_CODE_LIST(CARB_RESULT_USE_OMNI_RESULT_GEN)
#undef CARB_RESULT_USE_OMNI_RESULT_GEN
} // namespace carb
#if CARB_REQUIRE_LINKED
CARB_DYNAMICLINK const void* carbGetErrorApi(carb::Version* version);
#else
CARB_DYNAMICLINK const void* carbGetErrorApi(carb::Version* version) CARB_ATTRIBUTE(weak);
#endif
namespace carb::detail
{
CARB_DETAIL_DEFINE_DEFERRED_LOAD(getCarbErrorApiFunc, carbGetErrorApi, (const void* (*)(carb::Version*)));
} // namespace carb::detail
// Include the appropriate version of the interface
#if CARB_VERSION_ATLEAST(carb_ErrorApi, 2, 0)
# include "detail/Error2.h"
#else
# include "detail/Error1.h"
#endif
namespace carb
{
struct ErrorApi : public IError
{
static ErrorApi const& instance() noexcept;
//
// Static inline helper functions
//
#if CARB_VERSION_ATLEAST(carb_ErrorApi, 2, 0)
static void clearError() noexcept
{
instance().setError(ErrorPtr{});
}
static ErrorCode getError(cpp::zstring_view* outMessage = nullptr) noexcept
{
return static_cast<const IError&>(instance()).currentError(outMessage);
}
#else
static void clearError() noexcept
{
[[maybe_unused]] auto r = instance().setErrorTo(nullptr);
CARB_ASSERT(r == omni::core::kResultSuccess);
}
static Result getError() noexcept
{
Result r;
(void)instance().viewCurrentError(&r);
return r;
}
#endif
static ErrorCode convertFromErrno(extras::ErrnoType err = errno, std::string* message = nullptr);
static void setFromErrno();
#if CARB_PLATFORM_WINDOWS || defined(DOXYGEN_BUILD)
static ErrorCode convertFromWinApiErrorCode(DWORD err = ::GetLastError(), std::string* message = nullptr);
static void setFromWinApiErrorCode();
#endif
};
static_assert(sizeof(ErrorApi) == sizeof(IError) && alignof(ErrorApi) == alignof(IError), "Broken assumption");
class ScopedError
{
#if CARB_VERSION_ATLEAST(carb_ErrorApi, 2, 0)
ErrorPtr m_error{};
public:
ScopedError() : m_error(ErrorApi::instance().viewCurrentError())
{
}
~ScopedError()
{
ErrorApi::instance().setError(std::move(m_error));
}
explicit operator bool() const noexcept
{
return bool(m_error);
}
ErrorCode get() const
{
return m_error ? m_error->getCode() : ErrorCode{};
}
explicit operator ErrorCode() const
{
return get();
}
cpp::zstring_view getMessage() const
{
return m_error ? m_error->getMessage() : cpp::zstring_view{};
}
operator cpp::zstring_view() const
{
return getMessage();
}
#else
Error* m_error{ nullptr };
Result m_result{ kResultSuccess };
public:
ScopedError()
{
if (auto err = ErrorApi::instance().viewCurrentError(&m_result))
{
m_error = ErrorApi::instance().errorClone(err);
}
}
~ScopedError()
{
ErrorApi::instance().setErrorTo(m_error);
}
explicit operator bool() const noexcept
{
return m_error != nullptr;
}
Result get() const
{
return m_result;
}
explicit operator Result() const
{
return get();
}
# if CARB_VERSION_ATLEAST(carb_ErrorApi, 1, 1)
cpp::string_view getMessage() const
{
cpp::string_view out;
if (m_error)
ErrorApi::instance().getErrorInfoS(m_error, nullptr, &out);
return out;
}
operator cpp::string_view() const
{
return getMessage();
}
# endif
#endif
};
inline ErrorApi const& ErrorApi::instance() noexcept
{
static ErrorApi const* const papi = []() -> ErrorApi const* {
const Version expected_version = ErrorApi::getInterfaceDesc().version;
Version found_version = expected_version;
auto p = detail::getCarbErrorApiFunc()(&found_version);
CARB_FATAL_UNLESS(p != nullptr,
"Failed to load Error API for version this module was compiled against. This module was "
"compiled with Error API %" PRIu32 ".%" PRIu32
", but the maximum-supported version of the "
"API in the linked %s is %" PRIu32 ".%" PRIu32,
expected_version.major, expected_version.minor,
CARB_PLATFORM_WINDOWS ? "carb.dll" : "libcarb.so", found_version.major, found_version.minor);
return static_cast<const ErrorApi*>(p);
}();
return *papi;
}
// Common implementations for both v1 and v2
inline ErrorCode ErrorApi::convertFromErrno(extras::ErrnoType err, std::string* message)
{
if (err == 0)
{
if (message)
message->clear();
return {};
}
if (message)
*message = extras::convertErrnoToMessage(err);
switch (err)
{
case ENOSYS:
return ErrorCode::kNotImplemented;
case EACCES:
return ErrorCode::kAccessDenied;
case ENOMEM:
return ErrorCode::kOutOfMemory;
case EINVAL:
return ErrorCode::kInvalidArgument;
case EAGAIN:
#if !CARB_POSIX
// This is different on Windows but the same for POSIX
case EWOULDBLOCK:
#endif
return ErrorCode::kTryAgain;
case EINTR:
return ErrorCode::kInterrupted;
case EEXIST:
return ErrorCode::kAlreadyExists;
case EPERM:
return ErrorCode::kInvalidOperation;
case ENOENT:
return ErrorCode::kNotFound;
case ENOTDIR:
return ErrorCode::kNotDirectory;
default:
return ErrorCode::kFail;
}
}
inline void ErrorApi::setFromErrno()
{
extras::detail::ScopedErrno e; // just maintain errno over this call
std::string message;
auto result = convertFromErrno(e.get(), &message);
#if CARB_VERSION_ATLEAST(carb_ErrorApi, 2, 0)
[[maybe_unused]] auto res = instance().setError(result, message);
CARB_ASSERT(res);
#else
[[maybe_unused]] auto res = instance().setError(cpp::to_underlying(result), message);
CARB_ASSERT(res == kResultSuccess);
#endif
}
#if CARB_PLATFORM_WINDOWS
inline ErrorCode ErrorApi::convertFromWinApiErrorCode(DWORD err, std::string* output)
{
if (err == CARBWIN_ERROR_SUCCESS)
{
if (output)
output->clear();
return {};
}
if (output)
*output = std::system_category().message(err);
switch (err)
{
case CARBWIN_ERROR_FILE_NOT_FOUND:
case CARBWIN_ERROR_PATH_NOT_FOUND:
return ErrorCode::kNotFound;
case CARBWIN_ERROR_ACCESS_DENIED:
return ErrorCode::kAccessDenied;
case CARBWIN_ERROR_ALREADY_EXISTS:
case CARBWIN_ERROR_FILE_EXISTS:
return ErrorCode::kAlreadyExists;
case CARBWIN_ERROR_OUTOFMEMORY:
return ErrorCode::kOutOfMemory;
case CARBWIN_ERROR_NO_MORE_FILES:
case CARBWIN_ERROR_NO_MORE_ITEMS:
return ErrorCode::kNoMoreItems;
case CARBWIN_ERROR_CALL_NOT_IMPLEMENTED:
return ErrorCode::kNotImplemented;
case CARBWIN_WAIT_TIMEOUT:
case CARBWIN_ERROR_TIMEOUT:
return ErrorCode::kTryAgain;
case CARBWIN_ERROR_DIRECTORY:
return ErrorCode::kNotDirectory;
default:
return ErrorCode::kFail;
}
}
inline void ErrorApi::setFromWinApiErrorCode()
{
extras::detail::ScopedGetLastError gle; // just maintain GetLastError over this call
std::string message;
auto result = convertFromWinApiErrorCode(gle.get(), &message);
# if CARB_VERSION_ATLEAST(carb_ErrorApi, 2, 0)
[[maybe_unused]] auto res = instance().setError(result, message);
CARB_ASSERT(res);
# else
[[maybe_unused]] auto res = instance().setError(cpp::to_underlying(result), message);
CARB_ASSERT(res == kResultSuccess);
# endif
}
#endif
// shims
namespace detail
{
#if CARB_VERSION_ATLEAST(carb_ErrorApi, 2, 0)
inline omni::expected<void, ErrorCode> setError(ErrorCode code, cpp::string_view message = {})
{
return ErrorApi::instance().setError(code, message);
}
inline ErrorCode getError(cpp::zstring_view* outMessage = nullptr)
{
return ErrorApi::instance().getError(outMessage);
}
#else
inline omni::expected<void, ErrorCode> setError(ErrorCode code, cpp::string_view message = {})
{
if (auto res = ErrorApi::instance().setError(cpp::to_underlying(code), message); res != kResultSuccess)
return omni::unexpected((ErrorCode)res);
return {};
}
inline ErrorCode getError(cpp::zstring_view* outMessage = nullptr)
{
Result code;
auto err = ErrorApi::instance().viewCurrentError(&code);
if (outMessage)
{
if (err)
{
const char* p;
size_t s;
CARB_IGNORE_DEPRECATION_BEGIN(void) ErrorApi::instance().getErrorInfo(err, nullptr, &p, &s);
CARB_IGNORE_DEPRECATION_END
*outMessage = cpp::zstring_view{ p, s };
}
else
*outMessage = {};
}
return (ErrorCode)code;
}
#endif
} // namespace detail
} // namespace carb