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
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: true
BinPackParameters: true
ColumnLimit: 160
ColumnLimit: 120
CommentPragmas: '^!<'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ContinuationIndentWidth: 0
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/unittests_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ on:

jobs:
WinVS2019:
name: Windows VS2019
runs-on: windows-2019
name: Windows VS2022
runs-on: windows-2022

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ if(NOT status-code_IS_DEPENDENCY AND (NOT DEFINED BUILD_TESTING OR BUILD_TESTING
add_test(NAME test-status-code-noexcept COMMAND $<TARGET_FILE:test-status-code-noexcept>)

add_executable(test-status-code-not-posix "test/main.cpp")
target_compile_definitions(test-status-code-not-posix PRIVATE SYSTEM_ERROR2_NOT_POSIX=1 "SYSTEM_ERROR2_FATAL=::abort()")
target_compile_definitions(test-status-code-not-posix PRIVATE SYSTEM_ERROR2_NOT_POSIX=1)
target_link_libraries(test-status-code-not-posix PRIVATE status-code)
set_target_properties(test-status-code-not-posix PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
Expand Down
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Reference implementation for proposed SG14 `status_code` (`<system_error2>`) in C++ 11

(C) 2018 - 2021 Niall Douglas [http://www.nedproductions.biz/](http://www.nedproductions.biz/)
(C) 2018 - 2026 Niall Douglas [http://www.nedproductions.biz/](http://www.nedproductions.biz/)
Please send feedback to the SG14 study group mailing list at [https://lists.isocpp.org/mailman/listinfo.cgi/sg14/](https://lists.isocpp.org/mailman/listinfo.cgi/sg14/).

Docs: [https://ned14.github.io/status-code/](https://ned14.github.io/status-code/)
Expand Down
54 changes: 29 additions & 25 deletions example/thrown_exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,22 @@ class _thrown_exception_domain : public status_code_domain
// Fetch a constexpr instance of this domain
static inline constexpr const _thrown_exception_domain &get();

protected:
// Return the name of this domain
virtual _base::string_ref name() const noexcept override final { return _base::string_ref("thrown exception"); }
virtual int _do_name(_vtable_name_args &args) const noexcept override final
{
args.ret = _base::string_ref("thrown exception");
return 0;
}

// Return information about the value type of this domain
virtual payload_info_t payload_info() const noexcept override
virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override final
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) :
alignof(status_code_domain *)};
}

protected:
// This internal routine maps an exception ptr onto a generic_code
// It is surely hideously slow, but that's all relative in the end
static errc _to_generic_code(value_type c) noexcept
Expand Down Expand Up @@ -169,7 +174,8 @@ class _thrown_exception_domain : public status_code_domain
return true;
}
// True if the exception ptr is equivalent to some other status code
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override final
virtual bool _do_equivalent(const status_code<void> &code1,
const status_code<void> &code2) const noexcept override final
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const thrown_exception_code &>(code1);
Expand All @@ -193,22 +199,25 @@ class _thrown_exception_domain : public status_code_domain
return false;
}
// Called as a fallback if _equivalent() fails
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override final
virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override final
{
assert(code.domain() == *this);
const auto &c1 = static_cast<const thrown_exception_code &>(code);
return generic_code(_to_generic_code(c1.value()));
assert(args.code.domain() == *this);
const auto &c1 = static_cast<const thrown_exception_code &>(args.code);
args.ret = generic_code(_to_generic_code(c1.value()));
}
// Extract the what() from the exception
virtual _base::string_ref _do_message(const status_code<void> &code) const noexcept override final
virtual int _do_message(_vtable_message_args &args) const noexcept override final
{
assert(code.domain() == *this);
const auto &c = static_cast<const thrown_exception_code &>(code);
assert(args.code.domain() == *this);
const auto &c = static_cast<const thrown_exception_code &>(args.code);
const std::exception_ptr &e = exception_ptr_storage[c.value()];
try
{
if(!e)
return _base::string_ref("expired");
{
args.ret = _base::string_ref("expired");
return 0;
}
std::rethrow_exception(e);
}
catch(const std::exception &x)
Expand All @@ -217,22 +226,17 @@ class _thrown_exception_domain : public status_code_domain
makes copies instead of using the one stored in the array. So
for it we actively must copy the message. */
#ifdef _WIN32
auto *msg = x.what();
auto len = strlen(msg);
auto *p = static_cast<char *>(malloc(len + 1));
if(p == nullptr)
{
return _base::string_ref("failed to allocate memory for what()");
}
memcpy(p, msg, len + 1);
return _base::atomic_refcounted_string_ref(p, len);
args.ret = _base::atomic_refcounted_string_ref(x.what());
return 0;
#else
return _base::string_ref(x.what());
args.ret = _base::string_ref(x.what());
return 0;
#endif
}
catch(...)
{
return _base::string_ref("unknown thrown exception");
args.ret = _base::string_ref("unknown thrown exception");
return EAGAIN;
}
}
// Throw the code as a C++ exception
Expand Down
26 changes: 13 additions & 13 deletions include/status-code/boost_error_code.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,7 @@ class _boost_error_code_domain final : public status_code_domain
#endif
{
std::string msg = c.message();
auto *p = static_cast<char *>(malloc(msg.size() + 1)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to allocate message");
}
memcpy(p, msg.c_str(), msg.size() + 1);
return _base::atomic_refcounted_string_ref(p, msg.size());
return _base::atomic_refcounted_string_ref(msg.c_str(), msg.size());
}
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
catch(...)
Expand All @@ -96,7 +90,8 @@ class _boost_error_code_domain final : public status_code_domain
}

public:
//! The value type of the `boost::system::error_code` code, which stores the `int` from the `boost::system::error_code`
//! The value type of the `boost::system::error_code` code, which stores the `int` from the
//! `boost::system::error_code`
using value_type = int;
using _base::string_ref;

Expand Down Expand Up @@ -128,7 +123,8 @@ class _boost_error_code_domain final : public status_code_domain
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) :
alignof(status_code_domain *)};
}

protected:
Expand All @@ -143,7 +139,8 @@ class _boost_error_code_domain final : public status_code_domain

namespace detail
{
extern inline _boost_error_code_domain *boost_error_code_domain_from_category(const boost::system::error_category &category)
extern inline _boost_error_code_domain *
boost_error_code_domain_from_category(const boost::system::error_category &category)
{
static constexpr size_t max_items = 64;
static struct storage_t
Expand Down Expand Up @@ -210,7 +207,8 @@ namespace mixins
{
}

template <class Base> inline const boost::system::error_category &mixin<Base, _boost_error_code_domain>::category() const noexcept
template <class Base>
inline const boost::system::error_category &mixin<Base, _boost_error_code_domain>::category() const noexcept
{
const auto &domain = static_cast<const _boost_error_code_domain &>(this->domain());
return domain.error_category();
Expand All @@ -235,7 +233,8 @@ inline bool _boost_error_code_domain::_do_failure(const status_code<void> &code)
return static_cast<const boost_error_code &>(code).value() != 0; // NOLINT
}

inline bool _boost_error_code_domain::_do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept
inline bool _boost_error_code_domain::_do_equivalent(const status_code<void> &code1,
const status_code<void> &code2) const noexcept
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const boost_error_code &>(code1); // NOLINT
Expand Down Expand Up @@ -294,7 +293,8 @@ inline generic_code _boost_error_code_domain::_generic_code(const status_code<vo
return errc::unknown;
}

inline _boost_error_code_domain::string_ref _boost_error_code_domain::_do_message(const status_code<void> &code) const noexcept
inline _boost_error_code_domain::string_ref
_boost_error_code_domain::_do_message(const status_code<void> &code) const noexcept
{
assert(code.domain() == *this);
const auto &c = static_cast<const boost_error_code &>(code); // NOLINT
Expand Down
75 changes: 47 additions & 28 deletions include/status-code/com_code.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ SYSTEM_ERROR2_NAMESPACE_BEGIN
class _com_code_domain;
/*! (Windows only) A COM error code. Note semantic equivalence testing is only implemented for `FACILITY_WIN32`
and `FACILITY_NT_BIT`. As you can see at
[https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/](https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/), there
are an awful lot of COM error codes, and keeping mapping tables for all of them would be impractical (for the Win32 and NT facilities, we actually reuse the
mapping tables in `win32_code` and `nt_code`). You can, of course, inherit your own COM code domain from this one and override the `_do_equivalent()` function
to add semantic equivalence testing for whichever extra COM codes that your application specifically needs.
[https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/](https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/),
there are an awful lot of COM error codes, and keeping mapping tables for all of them would be impractical (for the
Win32 and NT facilities, we actually reuse the mapping tables in `win32_code` and `nt_code`). You can, of course,
inherit your own COM code domain from this one and override the `_do_equivalent()` function to add semantic equivalence
testing for whichever extra COM codes that your application specifically needs.
*/
using com_code = status_code<_com_code_domain>;
//! (Windows only) A specialisation of `status_error` for the COM error code domain.
Expand All @@ -61,9 +62,9 @@ class _com_code_domain : public status_code_domain

//! Construct from a `HRESULT` error code
#ifdef _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP
static _base::string_ref _make_string_ref(HRESULT c, wchar_t *perrinfo = nullptr) noexcept
static _base::string_ref _make_string_ref(int &errcode, HRESULT c, wchar_t *perrinfo = nullptr) noexcept
#else
static _base::string_ref _make_string_ref(HRESULT c, IErrorInfo *perrinfo = nullptr) noexcept
static _base::string_ref _make_string_ref(int &errcode, HRESULT c, IErrorInfo *perrinfo = nullptr) noexcept
#endif
{
_com_error ce(c, perrinfo);
Expand All @@ -73,16 +74,19 @@ class _com_code_domain : public status_code_domain
win32::DWORD bytes;
if(wlen == 0)
{
errcode = ENOENT;
return _base::string_ref("failed to get message from system");
}
for(;;)
{
auto *p = static_cast<char *>(malloc(allocation)); // NOLINT
if(p == nullptr)
{
errcode = ENOMEM;
return _base::string_ref("failed to get message from system");
}
bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, ce.ErrorMessage(), (int) (wlen + 1), p, (int) allocation, nullptr, nullptr);
bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, ce.ErrorMessage(), (int) (wlen + 1), p, (int) allocation,
nullptr, nullptr);
if(bytes != 0)
{
char *end = strchr(p, 0);
Expand All @@ -91,21 +95,25 @@ class _com_code_domain : public status_code_domain
--end;
}
*end = 0; // NOLINT
return _base::atomic_refcounted_string_ref(p, end - p);
_base::atomic_refcounted_string_ref ret(p, end - p);
free(p);
return ret;
}
free(p); // NOLINT
if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/)
{
allocation += allocation >> 2;
continue;
}
errcode = EILSEQ;
return _base::string_ref("failed to get message from system");
}
#else
auto wlen = static_cast<win32::DWORD>(strlen(ce.ErrorMessage()));
auto *p = static_cast<char *>(malloc(wlen + 1)); // NOLINT
if(p == nullptr)
{
errcode = ENOMEM;
return _base::string_ref("failed to get message from system");
}
memcpy(p, ce.ErrorMessage(), wlen + 1);
Expand All @@ -115,7 +123,9 @@ class _com_code_domain : public status_code_domain
--end;
}
*end = 0; // NOLINT
return _base::atomic_refcounted_string_ref(p, end - p);
_base::atomic_refcounted_string_ref ret(p, end - p);
free(p);
return ret;
#endif
}

Expand All @@ -139,23 +149,27 @@ class _com_code_domain : public status_code_domain
//! Constexpr singleton getter. Returns the constexpr com_code_domain variable.
static inline constexpr const _com_code_domain &get();

virtual string_ref name() const noexcept override { return string_ref("COM domain"); } // NOLINT

virtual payload_info_t payload_info() const noexcept override
protected:
virtual int _do_name(_vtable_name_args &args) const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
args.ret = string_ref("COM domain");
return 0;
} // NOLINT
virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override
{
args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) :
alignof(status_code_domain *)};
}

protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
return static_cast<const com_code &>(code).value() < 0; // NOLINT
}
/*! Note semantic equivalence testing is only implemented for `FACILITY_WIN32` and `FACILITY_NT_BIT`.
*/
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
virtual bool _do_equivalent(const status_code<void> &code1,
const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const com_code &>(code1); // NOLINT
Expand Down Expand Up @@ -204,29 +218,34 @@ class _com_code_domain : public status_code_domain
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override
{
assert(code.domain() == *this);
const auto &c1 = static_cast<const com_code &>(code); // NOLINT
assert(args.code.domain() == *this);
const auto &c1 = static_cast<const com_code &>(args.code); // NOLINT
if(c1.value() == S_OK)
{
return generic_code(errc::success);
args.ret = generic_code(errc::success);
return;
}
if((c1.value() & FACILITY_NT_BIT) != 0)
{
return generic_code(static_cast<errc>(_nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT)));
args.ret = generic_code(static_cast<errc>(_nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT)));
return;
}
if(HRESULT_FACILITY(c1.value()) == FACILITY_WIN32)
{
return generic_code(static_cast<errc>(_win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value()))));
args.ret = generic_code(static_cast<errc>(_win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value()))));
return;
}
return generic_code(errc::unknown);
args.ret = generic_code(errc::unknown);
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
virtual int _do_message(_vtable_message_args &args) const noexcept override
{
assert(code.domain() == *this);
const auto &c = static_cast<const com_code &>(code); // NOLINT
return _make_string_ref(c.value());
assert(args.code.domain() == *this);
const auto &c = static_cast<const com_code &>(args.code); // NOLINT
int ret = 0;
args.ret = _make_string_ref(ret, c.value());
return ret;
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE)
SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
Expand Down
Loading