2
0
mirror of https://github.com/boostorg/outcome.git synced 2026-01-19 04:22:13 +00:00

Port Outcome over to work with ABI refactored status_code.

This commit is contained in:
Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com)
2025-11-03 16:45:15 +00:00
parent 0b89aa7fdf
commit 168a4c3064
12 changed files with 189 additions and 83 deletions

View File

@@ -16,7 +16,7 @@ BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: true
BinPackParameters: true
ColumnLimit: 160
ColumnLimit: 120
CommentPragmas: '^!<'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ContinuationIndentWidth: 0

View File

@@ -238,7 +238,7 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test" AND NOT outcome_IS_DEPENDENCY AND (
target_compile_options(${target_name} PRIVATE /wd4530 /wd4577)
# target_compile_options(${target_name} PRIVATE /permissive-) # test bug report #142
endif()
target_compile_definitions(${target_name} PRIVATE SYSTEM_ERROR2_NOT_POSIX=1 "SYSTEM_ERROR2_FATAL=::abort()")
target_compile_definitions(${target_name} PRIVATE SYSTEM_ERROR2_NOT_POSIX=1)
target_link_libraries(${target_name} PRIVATE outcome::hl)
if(${target_name} MATCHES "coroutine-support")
apply_cxx_coroutines_to(PRIVATE ${target_name})

View File

@@ -4,7 +4,47 @@ weight = 80
+++
---
## v2.2.14 ? (Boost 1.90) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.14)
## v2.2.15 ? (Boost 1.91) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.15)
### Enhancements:
- Added the beginnings of a complete reimplementation of Experimental Outcome
from scratch, but written 100% in C. This reimplementation will be usable from
any ISO C right back to 1989, and it is 100% ABI compatible with this C++
implementation i.e. you can cast pointers to the types from one
implementation to the other if you wish, and virtual functions do correctly
call into 100% C implementations and vice versa.
- The C reimplementation required that the C++ `status_code_domain`'s virtual
and thunk functions had to be refactored so calling them with `__thiscall`
would also work when called with `__cdecl` on all major architectures and
calling conventions. I also made them able to return failure without throwing
a C++ exception. If you have created your own `status_code_domain`'s
you will need to update your code (the refactoring is very minor), otherwise
you shouldn't notice any change.
- The C reimplementation simplified the implementation of `atomic_refcounted_string_ref`,
and the C++ implementation was updated to match. The biggest change is that the
input string is now ALWAYS COPIED and is no longer a `malloc` pointer whose
ownership is taken. If you have used `atomic_refcounted_string_ref` in your own
code, you'll need to refactor it to no longer allocate the input buffer using
`malloc` and instead pass in the original `char` buffer.
- The complete C reimplementation is currently **NOT YET FIT FOR PRODUCTION USE** and
you should use instead the current, mature, C API to the C++ implementation.
The documentation currently makes no reference to the complete C reimplementation,
though if/when it becomes production ready, it will be updated.
- Finally, the C reimplementation is what is being proposed for standardisation
into the C standard library, so expect the usual renaming and other mild changes
that standards committees like to do. As I no longer serve on WG21 C++ standards,
I don't expect any further changes from that standards commitee.
### Bug fixes:
---
## v2.2.14 10th December 2025 (Boost 1.90) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.14)
### Enhancements:
@@ -12,8 +52,6 @@ weight = 80
- Bump Boost min cmake required to 3.10 to match standalone Outcome. Also bump minium cmake to 3.10
everywhere else in Outcome, as CI is now failing due to us requested too old a cmake.
### Bug fixes:
---
## v2.2.13 6th August 2025 (Boost 1.89) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.13)

View File

@@ -23,5 +23,4 @@ particular, if you need to string search or slice it, you can construct a
Now you understand what `string_ref` does, returning the name of the
status code domain is self describing. Note we use the non-managing
constructor of `string_ref`, as the string `"file i/o error domain"`
is statically stored. We cache the returned value locally in static
storage.
is statically stored.

View File

@@ -27,8 +27,8 @@ Distributed under the Boost Software License, Version 1.0.
#if !defined(__GNUC__) || __GNUC__ > 6 // GCC 6 chokes on this
#include "../../../include/outcome/experimental/status_result.hpp"
#include "../../../include/outcome/experimental/status-code/include/status-code/nested_status_code.hpp"
#include "../../../include/outcome/experimental/status_result.hpp"
/* Original note to WG21:
@@ -91,45 +91,57 @@ public:
// unique id must be from a hard random number source
// Use https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h to get a hard random 64 bit id.
// Do NOT make up your own value. Do NOT use zero.
constexpr explicit _file_io_error_domain(typename _base::unique_id_type id = 0x230f170194fcc6c7) noexcept : _base(id) {}
constexpr explicit _file_io_error_domain(typename _base::unique_id_type id = 0x230f170194fcc6c7) noexcept
: _base(id)
{
}
static inline constexpr const _file_io_error_domain &get();
//! [constructor]
//! [string_ref]
// Return the name of our custom code domain
virtual _base::string_ref name() const noexcept override final // NOLINT
virtual int _do_name(_vtable_name_args &args) const noexcept override final
{
static string_ref v("file i/o error domain");
return v; // NOLINT
args.ret = string_ref("file i/o error domain");
return 0; // errno value if failed
}
//! [string_ref]
//! [message]
// Return a string describing a specific code. We will return the
// string returned by our POSIX code base domain, with the source
// file and line number appended
virtual _base::string_ref _do_message(const outcome_e::status_code<void> &code) const noexcept override final // NOLINT
virtual int _do_message(_vtable_message_args &args) const noexcept override final
{
assert(code.domain() == *this);
assert(args.code.domain() == *this);
// Fetch message from base domain (POSIX)
auto msg = _base::_do_message(code);
const auto &c1 = static_cast<const file_io_error &>(code); // NOLINT
const int errcode = _base::_do_message(args);
if(errcode != 0)
{
return errcode;
}
const auto msg = std::move(args.ret);
const auto &c1 = static_cast<const file_io_error &>(args.code); // NOLINT
const value_type &v = c1.value();
// Append my source file and line number
if(v.file == nullptr)
{
return msg;
args.ret = msg;
return 0; // errno value if failed
}
size_t length = strlen(v.file) + 16 + msg.size();
auto *p = static_cast<char *>(malloc(length)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to get message from system");
args.ret = _base::string_ref("failed to get message from system");
return ENOMEM;
}
sprintf(p, "%s (%s:%d)", msg.data(), v.file, v.lineno);
snprintf(p, length, "%s (%s:%d)", msg.data(), v.file, v.lineno);
// Return as atomically reference counted string
return _base::atomic_refcounted_string_ref(p, length);
args.ret = _base::atomic_refcounted_string_ref(p, length);
free(p);
return 0; // errno value if failed
}
};
//! [message]
@@ -229,14 +241,16 @@ int main(void)
{
auto e = std::move(r).error();
// A quick demonstration that the indirection works as indicated
printf("Returned error has a code domain of '%s', a message of '%s'\n", e.domain().name().c_str(), e.message().c_str());
printf("\nAnd semantically comparing it to 'errc::no_such_file_or_directory' = %d\n", e == outcome_e::errc::no_such_file_or_directory);
printf("Returned error has a code domain of '%s', a message of '%s'\n", e.domain().name().c_str(),
e.message().c_str());
printf("\nAnd semantically comparing it to 'errc::no_such_file_or_directory' = %d\n",
e == outcome_e::errc::no_such_file_or_directory);
}
}
//! [open_file]
#else
int main(void)
{
return 0;

View File

@@ -21,10 +21,14 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
#define SYSTEM_ERROR2_FATAL(msg) abort()
#include "../../include/outcome/experimental/status_outcome.hpp"
#define OUTCOME_PREVENT_CONVERSION_WORKAROUND std
template <class T, class S = SYSTEM_ERROR2_NAMESPACE::system_code, class P = OUTCOME_PREVENT_CONVERSION_WORKAROUND::exception_ptr> using outcome = OUTCOME_V2_NAMESPACE::experimental::status_outcome<T, S, P>;
template <class T, class S = SYSTEM_ERROR2_NAMESPACE::system_code,
class P = OUTCOME_PREVENT_CONVERSION_WORKAROUND::exception_ptr>
using outcome = OUTCOME_V2_NAMESPACE::experimental::status_outcome<T, S, P>;
using OUTCOME_V2_NAMESPACE::in_place_type;
#include "quickcpplib/boost/test/unit_test.hpp"
@@ -117,7 +121,8 @@ BOOST_OUTCOME_AUTO_TEST_CASE(works / status_code / outcome, "Tests that the outc
#if !defined(__APPLE__) || defined(__cpp_exceptions)
{ // excepted
OUTCOME_PREVENT_CONVERSION_WORKAROUND::error_code ec(5, OUTCOME_PREVENT_CONVERSION_WORKAROUND::system_category());
auto e = OUTCOME_PREVENT_CONVERSION_WORKAROUND::make_exception_ptr(OUTCOME_PREVENT_CONVERSION_WORKAROUND::system_error(ec)); // NOLINT
auto e = OUTCOME_PREVENT_CONVERSION_WORKAROUND::make_exception_ptr(
OUTCOME_PREVENT_CONVERSION_WORKAROUND::system_error(ec)); // NOLINT
outcome<int> m(e);
BOOST_CHECK(!m);
BOOST_CHECK(!m.has_value());
@@ -153,7 +158,8 @@ BOOST_OUTCOME_AUTO_TEST_CASE(works / status_code / outcome, "Tests that the outc
}
{ // outcome<void, void> should work
OUTCOME_PREVENT_CONVERSION_WORKAROUND::error_code ec(5, OUTCOME_PREVENT_CONVERSION_WORKAROUND::system_category());
auto e = OUTCOME_PREVENT_CONVERSION_WORKAROUND::make_exception_ptr(OUTCOME_PREVENT_CONVERSION_WORKAROUND::system_error(ec));
auto e =
OUTCOME_PREVENT_CONVERSION_WORKAROUND::make_exception_ptr(OUTCOME_PREVENT_CONVERSION_WORKAROUND::system_error(ec));
outcome<void, void> m(e);
BOOST_CHECK(!m);
BOOST_CHECK(!m.has_value());

View File

@@ -21,9 +21,13 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
#define SYSTEM_ERROR2_FATAL(msg) abort()
#include "../../include/outcome/experimental/status_result.hpp"
template <class T, class S = SYSTEM_ERROR2_NAMESPACE::system_code, class NoValuePolicy = OUTCOME_V2_NAMESPACE::experimental::policy::default_status_result_policy<T, S>> using result = OUTCOME_V2_NAMESPACE::experimental::status_result<T, S, NoValuePolicy>;
template <class T, class S = SYSTEM_ERROR2_NAMESPACE::system_code,
class NoValuePolicy = OUTCOME_V2_NAMESPACE::experimental::policy::default_status_result_policy<T, S>>
using result = OUTCOME_V2_NAMESPACE::experimental::status_result<T, S, NoValuePolicy>;
using OUTCOME_V2_NAMESPACE::in_place_type;
#include "quickcpplib/boost/test/unit_test.hpp"
@@ -66,25 +70,35 @@ public:
using string_ref = _base::string_ref;
public:
constexpr _payload_domain() noexcept : _base(0x7b782c8f935e34ba) {}
constexpr _payload_domain() noexcept
: _base(0x7b782c8f935e34ba)
{
}
static inline constexpr const _payload_domain &get();
virtual _base::string_ref name() const noexcept override final { return string_ref("payload domain"); } // NOLINT
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 *)};
}
protected:
virtual bool _do_failure(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
virtual int _do_name(_vtable_name_args &args) const noexcept override final
{
assert(code.domain() == *this); // NOLINT
return static_cast<const status_code_payload &>(code).value().ec != SYSTEM_ERROR2_NAMESPACE::errc::success; // NOLINT
args.ret = string_ref("payload domain");
return 0;
} // NOLINT
virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override final
{
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 *)};
}
virtual bool _do_equivalent(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code1, const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code2) const noexcept override final // NOLINT
virtual bool
_do_failure(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
{
assert(code.domain() == *this); // NOLINT
return static_cast<const status_code_payload &>(code).value().ec !=
SYSTEM_ERROR2_NAMESPACE::errc::success; // NOLINT
}
virtual bool
_do_equivalent(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code1,
const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code2) const noexcept override final // NOLINT
{
assert(code1.domain() == *this); // NOLINT
const auto &c1 = static_cast<const status_code_payload &>(code1); // NOLINT
@@ -95,18 +109,20 @@ protected:
}
return false;
}
virtual SYSTEM_ERROR2_NAMESPACE::generic_code _generic_code(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override final
{
assert(code.domain() == *this); // NOLINT
return static_cast<const status_code_payload &>(code).value().ec; // NOLINT
assert(args.code.domain() == *this); // NOLINT
args.ret = static_cast<const status_code_payload &>(args.code).value().ec; // NOLINT
}
virtual _base::string_ref _do_message(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
virtual int _do_message(_vtable_message_args &args) const noexcept override final
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const status_code_payload &>(code); // NOLINT
return string_ref(SYSTEM_ERROR2_NAMESPACE::detail::generic_code_message(c.value().ec));
assert(args.code.domain() == *this); // NOLINT
const auto &c = static_cast<const status_code_payload &>(args.code); // NOLINT
args.ret = string_ref(SYSTEM_ERROR2_NAMESPACE::detail::generic_code_message(c.value().ec));
return 0;
}
virtual void _do_throw_exception(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const override final // NOLINT
virtual void
_do_throw_exception(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const override final // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const status_code_payload &>(code); // NOLINT
@@ -210,9 +226,12 @@ BOOST_OUTCOME_AUTO_TEST_CASE(works / status_code / result, "Tests that the resul
{
int _v{0};
udt() = default;
udt(udt &&o) noexcept : _v(o._v) {}
udt(udt &&o) noexcept
: _v(o._v)
{
}
udt(const udt &o) // NOLINT
: _v(o._v)
: _v(o._v)
{
}
udt &operator=(udt &&o) noexcept

View File

@@ -21,6 +21,8 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
#define SYSTEM_ERROR2_FATAL(msg) abort()
#include "../../include/outcome/experimental/status_result.hpp"
#include <climits> // for INT_MAX
@@ -29,7 +31,8 @@ Distributed under the Boost Software License, Version 1.0.
// status_code<erased<intptr_t>>
using error = SYSTEM_ERROR2_NAMESPACE::error;
// Outcome's result must be told when it is dealing with an erased status code
template <class T, class E> using result = OUTCOME_V2_NAMESPACE::experimental::status_result<T, E, OUTCOME_V2_NAMESPACE::policy::all_narrow>;
template <class T, class E>
using result = OUTCOME_V2_NAMESPACE::experimental::status_result<T, E, OUTCOME_V2_NAMESPACE::policy::all_narrow>;
enum class arithmetic_errc
{
@@ -49,48 +52,64 @@ class _arithmetic_errc_domain : public SYSTEM_ERROR2_NAMESPACE::status_code_doma
public:
using value_type = arithmetic_errc;
constexpr explicit _arithmetic_errc_domain(typename _base::unique_id_type id = 0x290f170194f0c6c7) noexcept : _base(id) {}
constexpr explicit _arithmetic_errc_domain(typename _base::unique_id_type id = 0x290f170194f0c6c7) noexcept
: _base(id)
{
}
static inline constexpr const _arithmetic_errc_domain &get();
virtual _base::string_ref name() const noexcept override final // NOLINT
{
static string_ref v("arithmetic error domain");
return v; // NOLINT
}
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 *)};
}
protected:
virtual bool _do_failure(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
virtual int _do_name(_vtable_name_args &args) const noexcept override final
{
args.ret = string_ref("arithmetic error domain");
return 0;
}
virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override final
{
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 *)};
}
virtual bool
_do_failure(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c1 = static_cast<const arithmetic_errc_error &>(code); // NOLINT
return c1.value() != arithmetic_errc::success;
}
virtual bool _do_equivalent(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &, const SYSTEM_ERROR2_NAMESPACE::status_code<void> &) const noexcept override final { return false; } // NOLINT
virtual SYSTEM_ERROR2_NAMESPACE::generic_code _generic_code(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &) const noexcept override final { return {}; } // NOLINT
virtual _base::string_ref _do_message(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
virtual bool _do_equivalent(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &,
const SYSTEM_ERROR2_NAMESPACE::status_code<void> &) const noexcept override final
{
assert(code.domain() == *this); // NOLINT
const auto &c1 = static_cast<const arithmetic_errc_error &>(code); // NOLINT
return false;
} // NOLINT
virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override final { args.ret = {}; }
virtual int _do_message(_vtable_message_args &args) const noexcept override final
{
assert(args.code.domain() == *this); // NOLINT
const auto &c1 = static_cast<const arithmetic_errc_error &>(args.code); // NOLINT
switch(c1.value())
{
case arithmetic_errc::success:
return _base::string_ref("success");
args.ret = _base::string_ref("success");
return 0;
case arithmetic_errc::divide_by_zero:
return _base::string_ref("divide by zero");
args.ret = _base::string_ref("divide by zero");
return 0;
case arithmetic_errc::integer_divide_overflows:
return _base::string_ref("integer divide overflows");
args.ret = _base::string_ref("integer divide overflows");
return 0;
case arithmetic_errc::not_integer_division:
return _base::string_ref("not integer division");
args.ret = _base::string_ref("not integer division");
return 0;
}
return _base::string_ref("unknown");
args.ret = _base::string_ref("unknown");
return 0;
}
SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &) const override final { abort(); } // NOLINT
SYSTEM_ERROR2_NORETURN virtual void
_do_throw_exception(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &) const override final
{
abort();
} // NOLINT
};
constexpr _arithmetic_errc_domain arithmetic_errc_domain;
@@ -114,7 +133,7 @@ namespace trait
{
static constexpr bool value = true;
};
}
} // namespace trait
OUTCOME_V2_NAMESPACE_END
// And tell Outcome how to perform the implicit conversion
inline error make_error_code(arithmetic_errc e)

View File

@@ -21,6 +21,8 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
#define SYSTEM_ERROR2_FATAL(msg) abort()
#include "../../include/outcome/experimental/status_result.hpp"
#include "../../include/outcome/try.hpp"
#include "quickcpplib/boost/test/unit_test.hpp"
@@ -34,7 +36,10 @@ namespace issues220
template <class T> using PosixResult = outcome_e::status_result<T, outcome_e::posix_code>;
Result<int> convert(const PosixResult<int> &posix_result) { return Result<int>(posix_result); }
Result<int> convert(const PosixResult<int> &posix_result)
{
return Result<int>(posix_result);
}
} // namespace issues220
#endif

View File

@@ -21,6 +21,8 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
#define SYSTEM_ERROR2_FATAL(msg) abort()
#include "../../include/outcome/experimental/status_result.hpp"
#include "quickcpplib/boost/test/unit_test.hpp"
@@ -31,10 +33,14 @@ namespace issues255
{
namespace outcome_e = OUTCOME_V2_NAMESPACE::experimental;
static_assert(outcome_e::traits::is_move_bitcopying<outcome_e::error>::value, "outcome_e::error is not move bitcopying!");
static_assert(outcome_e::traits::is_move_bitcopying<outcome_e::error>::value,
"outcome_e::error is not move bitcopying!");
constexpr outcome_e::status_result<int> test() { return outcome_e::success(42); }
}
constexpr outcome_e::status_result<int> test()
{
return outcome_e::success(42);
}
} // namespace issues255
BOOST_OUTCOME_AUTO_TEST_CASE(issues / 0255 / test, "status_result<int> not usable from constexpr in C++ 20")
{