New upstream version 8.1.0

This commit is contained in:
geos_one
2025-08-10 01:34:16 +02:00
commit c891bb7105
4398 changed files with 838833 additions and 0 deletions

19
thirdparty/source/nu/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2017 Phoebe Buckheister
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

136
thirdparty/source/nu/Makefile vendored Normal file
View File

@@ -0,0 +1,136 @@
CXXFLAGS_COMMON := -std=c++14
CXXFLAGS_COMMON += -Wall -Wextra -Werror
CXXFLAGS_COMMON += -I include
LDFLAGS_COMMON := -std=c++14
SPHINX_BUILD := sphinx-build
tests := \
tests/error_or.cpp
override V := $(if $V,,@)
build_type ?= asan
build-types := asan ubsan coverage
ifeq (,$(filter $(build-types), $(build_type)))
$(error bad build type (allowed: $(build-types)))
endif
# don't require build tools for docs-only build
ifneq ($(MAKECMDGOALS),docs)
ifneq (0,$(shell pkg-config gtest && echo 0))
$(error need gtest)
endif
CXXFLAGS_COMMON += $(shell pkg-config --cflags gtest)
LDFLAGS_COMMON += $(shell pkg-config --libs gtest)
endif
CXXFLAGS[asan] := -O1 -fsanitize=address
LDFLAGS[asan] := -fsanitize=address
CXXFLAGS[ubsan] := -O1 -fsanitize=undefined
LDFLAGS[ubsan] := -fsanitize=undefined
CXXFLAGS[coverage] := --coverage -fno-inline-functions
LDFLAGS[coverage] := --coverage
build-dir := build/$(build_type)
bin-dir := $(build-dir)/bin
obj-dir := $(build-dir)/obj
docs-dir := build/docs
docs-extracted-dir := docs/extracted
docs-source := $(wildcard include/nu/*)
override CXXFLAGS += $(CXXFLAGS_COMMON) $(CXXFLAGS[$(build_type)])
override LDFLAGS += $(LDFLAGS_COMMON) $(LDFLAGS[$(build_type)])
sources[tests] := $(tests)
objects[tests] := $(addsuffix .o, $(addprefix $(obj-dir)/, $(sources[tests])))
executables[tests] := \
$(bin-dir)/tests/runner
objects-for[$(bin-dir)/tests/runner] := $(objects[tests])
sources := $(sources[tests])
executables := $(executables[tests])
objects := $(objects[tests])
.PHONY: all
all: $(executables)
.PHONY: clean
clean:
rm -rf build $(docs-extracted-dir)
.PHONY: cov-check
cov-check: -cov-check-pre -cov-check-run -cov-genhtml
.PHONY: -cov-check-pre
-cov-check-pre:
! [ -d "$(build-dir)" ] || find $(build-dir) -iname '*.gcda' -delete
rm -f $(build-dir)/cov-info
rm -rf $(build-dir)/cov-data
.PHONY: -cov-check-run
-cov-check-run: -cov-check-pre check
.PHONY: -cov-genhtml
-cov-genhtml: -cov-check-run
geninfo -o $(build-dir)/cov-info --no-external --rc lcov_branch_coverage=1 -b . $(build-dir)
genhtml -o $(build-dir)/cov-data --branch-coverage $(build-dir)/cov-info
.PHONY: check
check: $(addprefix -run-test-, $(executables[tests]))
.PHONY: check-all-profiles
check-all-profiles:
$(MAKE) build_type=asan check
$(MAKE) build_type=ubsan check
$(MAKE) build_type=coverage cov-check
.PHONY: $(addprefix -run-test-, $(executables[tests]))
define declare-test-run-rule
-run-test-$1: $1
./$1
endef
$(foreach test,$(executables[tests]),$(eval $(call declare-test-run-rule,$(test))))
.PHONY: docs
docs: $(patsubst %.hpp,$(docs-extracted-dir)/%.rst,$(docs-source)) | -create-dirs
rm -rf $(docs-dir)
$(SPHINX_BUILD) -n "docs" "$(docs-dir)"
$(docs-extracted-dir)/%.rst: %.hpp
mkdir -p $(dir $@)
awk -f docs/extract.awk <$< >$@
-print-cxxflags:
@echo -n $(CXXFLAGS_COMMON) $(CXXFLAGS_EXTRA[$(file)])
-include $(addsuffix .d, $(objects))
-create-dirs:
mkdir -p $(sort $(dir $(objects)))
mkdir -p $(sort $(dir $(executables)))
mkdir -p $(docs-dir)
$(obj-dir)/%.cpp.o: %.cpp Makefile | -create-dirs
@echo "[CXX] $<"
$V$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA[$<]) -MM -MP -MT $@ -MF $@.d $<
$V$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA[$<]) -c -o $@ $<
define declare-executable-link-rule
$1: $$(objects-for[$1]) | -create-dirs
@echo "[LD] $(patsubst $(bin-dir)/%,%,$1)"
$V$$(CXX) $$(LDFLAGS) -o $$@ $$^
endef
$(foreach exe,$(executables),$(eval $(call declare-executable-link-rule,$(exe))))

28
thirdparty/source/nu/docs/conf.py vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
needs_sphinx = '1.0'
project = 'nu'
copyright = '2016, dhivael'
author = 'dhivael'
release = '0.1.0'
version = release
language = 'en'
master_doc = 'index'
primary_domain = 'cpp'
source_suffix = '.rst'
highlight_language = 'cpp'
html_theme = 'agogo'
html_show_sourcelink = False
html_static_path = ['_static']
pygments_style = 'sphinx'
cpp_index_common_prefix = ['nu::']
nitpick_ignore = [
('cpp:typeOrConcept', 'std'),
('cpp:typeOrConcept', 'std::exception'),
('cpp:typeOrConcept', 'std::error_code'),
]

28
thirdparty/source/nu/docs/extract.awk vendored Normal file
View File

@@ -0,0 +1,28 @@
BEGIN {
indent = "";
}
$1 == "//-" {
sub("^ ", "", indent);
}
$1 ~ "^//[+-/0]$", $1 ~ "^//[+-/0]$" {
line = $0;
sub("^[ \t]*//. ?", "", line);
if (line ~ /↩$/)
sub("↩$", "\\", line);
if ($1 == "//0")
print line;
else
print indent line;
}
$1 == "//+" {
indent = indent " ";
}
$1 !~ "^//[+-/0]$" {
print "";
}

17
thirdparty/source/nu/docs/index.rst vendored Normal file
View File

@@ -0,0 +1,17 @@
Library overview
================
.. toctree::
:maxdepth: 1
:caption: Contents:
:glob:
extracted/include/nu/*
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@@ -0,0 +1,419 @@
#ifndef error_or_hpp_dKfa5ZhUpqI8k0sPhaQJmI
#define error_or_hpp_dKfa5ZhUpqI8k0sPhaQJmI
#include <system_error>
/// Error handling (`error_or`)
/// ===========================
/// .. namespace:: nu
namespace nu {
// LCOV_EXCL_START
/// .. class:: bad_error_or_access : public std::exception
///
/// Thrown by :class:`error_or` if an object is coerced to a value if it contains an error or if it is
/// coerced to an error if it contains a value.
struct bad_error_or_access : std::exception {
virtual const char* what() const noexcept override
{
return "bad_error_or_access";
}
};
template<typename Val>
static constexpr bool is_nothrow_swappable() noexcept
{
return noexcept(std::swap(std::declval<Val&>(), std::declval<Val&>()));
}
template<typename Val>
static constexpr bool is_value_type() noexcept
{
static_assert(!std::is_array<Val>::value, "Val must not be an array type");
static_assert(!std::is_reference<Val>::value, "Val must not be a reference type");
static_assert(!std::is_function<Val>::value, "Val must not be a function type");
static_assert(std::is_move_constructible<Val>::value, "Val must be move-constructible");
static_assert(sizeof(Val) > 0, "Val must be complete");
return true;
}
// LCOV_EXCL_STOP
/// .. class:: template<typename T, typename Err = std::error_code> error_or
///
/// An instance of :class:`error_or` may contain either an error (of type :any:`Err`) or a value
/// (of type :any:`T`).
///
/// In addition to methods to query whether an object contains an error or a value, :class:`error_or`
/// provides an *apply* operation and a *reduce* operation. The *apply* operation can be used to
/// transform an object that contains a value, but retain the error if the object does not contain a
/// value. The *reduce* operation can be used to transform an :class:`error_or` into a new value
/// regardless of whether it contained a value or an error.
///
/// The template types :any:`T` and :any:`Err` must both be complete types that are move-constructible
/// and are neither an array type, reference type, or function type.
/// :any:`Err` must also be default-constructible and copy-constructible; it is recommended to ensure
/// that :any:`Err` is trivially copy- and move-constructible.
/// All operations of :any:`Err` must be noexcept.
/// (This restriction is placed on :any:`Err` to make all operations on :any:`error_or` instances
/// with errors uniformly noexcept.)
///
/// Example:
///
/// .. include:: /../tests/error_or_example.cpp
//+ :code: c++
template<typename T, typename Err = std::error_code>
class error_or {
public:
static_assert(nu::is_value_type<T>(), "");
static_assert(nu::is_value_type<Err>(), "");
static_assert(std::is_nothrow_default_constructible<Err>::value, "Err must be default-constructible");
static_assert(std::is_nothrow_move_constructible<Err>::value, "Err must be nothrow move-constructible");
static_assert(std::is_nothrow_copy_constructible<Err>::value, "Err must be nothrow move-constructible");
static_assert(std::is_nothrow_move_assignable<Err>::value, "Err must be nothrow move-assignable");
static_assert(std::is_nothrow_destructible<Err>::value, "Err must be nothrow-destructible");
static_assert(nu::is_nothrow_swappable<Err>(), "Err must be nothrow-swappable");
/// .. function:: error_or() noexcept
///
/// Constructs the object holding a default-constructed :any:`Err` as its :func:`error`.
///
/// :noexcept: ``noexcept``
error_or() noexcept:
valid(false), u{with_error{}, {}}
{
}
/// .. function:: error_or(Err e) noexcept
///
/// Constructs the object holding a `e` as its :func:`error`. The value is moved into
/// the newly constructed object.
///
/// :noexcept: ``noexcept``
error_or(Err error) noexcept:
valid(false), u{with_error{}, std::move(error)}
{
}
/// .. function:: error_or(T value)
///
/// Constructs the object holding ``value`` as its :func:`value`. The provided value
/// is moved into the object.
///
/// :noexcept: ``noexcept`` if the move constructor of :any:`T` is ``noexcept``
error_or(T value) noexcept(std::is_nothrow_move_constructible<T>::value):
valid(true), u{with_data{}, std::move(value)}
{
}
/// .. function:: error_or(error_or&& other)
///
/// Constructs the object with the state of ``other``. The state of ``other`` is moved
/// into the newly constructed object; ``other`` is in an indefinite state afterwards.
///
/// :noexcept: |swap(T,T)-noexcept|
error_or(error_or&& other) noexcept(noexcept(other.swap(other))):
error_or()
{
swap(other);
}
~error_or() noexcept(std::is_nothrow_destructible<T>::value)
{
if (valid)
u.value.~T();
else
u.error.~Err();
}
/// .. function:: error_or& operator=(error_or&& other)
///
/// Moves the state of ``other`` into ``*this``. Afterwards, ``other`` is in an
/// indefinite state.
///
/// :noexcept: |swap(T,T)-noexcept|
error_or& operator=(error_or&& other)
noexcept(noexcept(error_or(std::move(other)).swap(std::declval<error_or&>())))
{
error_or(std::move(other)).swap(*this);
return *this;
}
/// .. function:: void swap(error_or& other)
///
/// Swaps the states of ``*this`` and ``other``.
///
/// :noexcept: |swap(T,T)-noexcept|
void swap(error_or& other) noexcept(nu::is_nothrow_swappable<T>())
{
using std::swap;
if (valid) {
if (other.valid) {
swap(u.value, other.u.value);
} else {
T tmp(std::move(u.value));
u.value.~T();
new (&u.error) Err(std::move(other.u.error)); // LCOV_EXCL_LINE (check for NULL)
other.u.error.~Err();
new (&other.u.value) T(std::move(tmp)); // LCOV_EXCL_LINE (check for NULL)
swap(valid, other.valid);
}
} else if (other.valid) {
other.swap(*this);
} else {
swap(u.error, other.u.error);
}
}
/// .. function:: explicit operator bool() const noexcept
///
/// :returns: ``true`` if ``*this`` contains a value, ``false`` otherwise
/// :noexcept: ``noexcept``
explicit operator bool() const noexcept { return valid; }
/// .. function:: bool operator!() const noexcept
///
/// :returns: ``false`` if ``*this`` contains a value, ``true`` otherwise
/// :noexcept: ``noexcept``
bool operator!() const noexcept { return !bool(*this); }
/// .. function:: template<typename Fn> auto operator/(Fn f) const
/// .. function:: template<typename Fn> auto operator/(Fn f)
///
/// If ``*this`` contains a value, calls ``f(get_or_release())`` and returns the result as an
/// ``error_or<R, Err>`` object, where ``R`` is the return type of :any:`Fn`.
///
/// These operators are useful because they can provide exception guarantees that the usual
/// sequences ``if (e) on_value(e.value()); else on_error(e.error());`` cannot provide, since
/// both :func:`value` and :func:`error` may throw.
///
/// :returns: ``*this ? error_or<R, Err>(f(get_or_release())) : error_or<R, Err>(error())``
/// :noexcept: ``noexcept`` if ``f`` is ``noexcept``
template<typename Fn>
auto operator/(Fn f) const noexcept(noexcept(f(std::declval<const T&>())))
-> error_or<typename std::result_of<Fn(const T&)>::type, Err>
{
return valid
? error_or<typename std::result_of<Fn(const T&)>::type, Err>{f(u.value)}
: error_or<typename std::result_of<Fn(const T&)>::type, Err>{u.error};
}
template<typename Fn>
auto operator/(Fn f) noexcept(noexcept(f(std::declval<T&&>())))
-> error_or<typename std::result_of<Fn(T&&)>::type, Err>
{
return valid
? error_or<typename std::result_of<Fn(T&&)>::type, Err>{f(std::move(u.value))}
: error_or<typename std::result_of<Fn(T&&)>::type, Err>{std::move(u.error)};
}
/// .. function:: template<typename R = void, typename TrueFn, typename FalseFn> ↩
/// auto reduce(TrueFn t, FalseFn f) const
/// .. function:: template<typename R = void, typename TrueFn, typename FalseFn> ↩
/// auto reduce(TrueFn t, FalseFn f)
///
/// If ``*this`` contains a value, calls ``t(get_or_release())``, otherwise calls
/// ``f(error())``.
/// The result of both ``t(get_or_release())`` and ``f(error())`` is converted to type
/// ``RT`` and returned, where ``RT`` is ``R`` if ``R`` is not ``void``, otherwise
/// ``std:common_type<TT, FT>::type`` where ``TT`` and ``FT`` are the result types of
/// ``t(get_or_release())`` and ``f(error())`` respectively.
/// The result types of ``t`` and ``f`` must each be a complete type that is neither
/// an array type, a reference type, or a function type.
///
/// These functions are useful because they can provide exception guarantees that the usual
/// sequences ``if (e) on_value(e.value()); else on_error(e.error());`` cannot provide, since
/// both :func:`value` and :func:`error` may throw.
///
/// :returns: ``*this ? t(get_or_release()) : f(error())``
/// :noexcept: ``noexcept`` if ``t`` is ``noexcept`` and ``f`` is ``noexcept``.
template<typename R = void, typename TrueFn, typename FalseFn>
auto reduce(TrueFn t, FalseFn f) const
noexcept(
noexcept(t(std::declval<const T&>()))
&& noexcept(f(std::declval<const Err&>())))
-> typename std::conditional<
std::is_void<R>::value,
typename std::common_type<
decltype(t(std::declval<const T&>())),
decltype(f(std::declval<const Err&>()))>::type,
R>::type
{
static_assert(nu::is_value_type<decltype(t(std::declval<const T&>()))>(), "");
static_assert(nu::is_value_type<decltype(f(std::declval<const Err&>()))>(), "");
return valid
? t(u.value)
: f(u.error);
}
template<typename R = void, typename TrueFn, typename FalseFn>
auto reduce(TrueFn t, FalseFn f)
noexcept(
noexcept(t(std::declval<T&&>()))
&& noexcept(f(std::declval<const Err&>())))
-> typename std::conditional<
std::is_void<R>::value,
typename std::common_type<
decltype(t(std::declval<T&&>())),
decltype(f(std::declval<const Err&>()))>::type,
R>::type
{
static_assert(nu::is_value_type<decltype(t(std::declval<T&&>()))>(), "");
static_assert(nu::is_value_type<decltype(f(std::declval<const Err&>()))>(), "");
return valid
? t(std::move(u.value))
: f(std::move(u.error));
}
/// .. function:: template<typename Fn> auto operator%(Fn f) const
/// .. function:: template<typename Fn> auto operator%(Fn f)
///
/// If ``*this`` contains a value, calls ``fn(get_or_release())`` and returns the result.
/// If ``*this`` does not contain a value, returns ``error()`` cast to the result type
/// of ``fn(get_or_release())``.
/// The result type must be a complete type that is neither an array type, a reference
/// type, or a function type.
///
/// These operators are provided as a shorthand for ``reduce(fn1, std::move<Err>)``,
/// where ``fn1`` would usually be a function that returns another instance of :any:`error_or`.
/// By using the ``%`` operator, multiple calls to functions returning :any:`error_or` can
/// be easily chained if only the last error in the chain is important, for example if a
/// program must open a file and invoke an ioctl on it in one operation that appears atomic
/// to the consumer.
///
/// :returns: ``*this ? fn(get_or_release()) : error()``
/// :noexcept: ``noexcept`` if ``fn`` is ``noexcept``.
template<typename Fn>
auto operator%(Fn fn) const noexcept(noexcept(fn(std::declval<const T&>())))
-> decltype(fn(std::declval<const T&>()))
{
static_assert(nu::is_value_type<decltype(fn(u.value))>(), "");
return valid
? fn(u.value)
: decltype(fn(u.value)){u.error};
}
template<typename Fn>
auto operator%(Fn fn) noexcept(noexcept(fn(std::declval<T&&>())))
-> decltype(fn(std::declval<T&&>()))
{
static_assert(nu::is_value_type<decltype(fn(std::move(u.value)))>(), "");
return valid
? fn(std::move(u.value))
: decltype(fn(std::move(u.value))){std::move(u.error)};
}
/// .. function:: const Err& error() const
///
/// :returns: The error stored in ``*this``.
/// :throws nu\:\:bad_error_or_access: If ``*this`` contains a value.
const Err& error() const
{
if (valid)
throw bad_error_or_access();
return u.error;
}
/// .. function:: const T& value() const &
///
/// :returns: The value stored in ``*this``.
/// :throws nu\:\:bad_error_or_access: If ``*this`` contains an error.
const T& value() const
{
if (!valid)
throw bad_error_or_access();
return u.value;
}
/// .. function:: T&& release_value()
///
/// :returns: The value stored in ``*this``.
/// :throws nu\:\:bad_error_or_access: If ``*this`` contains an error.
T&& release_value()
{
if (!valid)
throw bad_error_or_access();
valid = false;
return std::move(u.value);
}
private:
enum class with_error {};
enum class with_data {};
bool valid;
union U {
~U() {}
U(with_error, Err e) noexcept:
error(e)
{}
U(with_data, T value) noexcept(std::is_nothrow_move_constructible<T>::value):
value(std::move(value))
{}
Err error;
T value;
} u;
/// .. function:: private const T& get_or_release() const noexcept
/// .. function:: private T&& get_or_release() noexcept
///
/// **Exposition only**: if called by const reference, returns a const reference
/// to the stored value. If called by non-const reference, returns an rvalue reference
/// to the stored value.
};
//-
/// Free functions for :class:`error_or`
/// ------------------------------------
/// .. function:: template<typename T, typename Err> ↩
/// void swap(error_or<T, Err>& a, error_or<T, Err>& b)
///
/// Swap two :class:`error_or` instances.
///
/// :noexcept: |swap(T,T)-noexcept|
template<typename T, typename Err>
inline void swap(error_or<T, Err>& a, error_or<T, Err>& b) noexcept(noexcept(a.swap(b)))
{
a.swap(b);
}
/// .. function:: template<typename T, typename E = std::error_code, typename... Args> ↩
/// error_or<T> make_error_or(Args&&... args)
///
/// Creates a new :any:`error_or` instance with the given arguments as
/// ``error_or<T, E>(T{std::forward<Args>(args)...})``.
///
/// :noexcept: ``noexcept(error_or<T, E>(T{std::forward<Args>(args)...}))``
template<typename T, typename E = std::error_code, typename... Args>
inline error_or<T> make_error_or(Args&&... args)
noexcept(noexcept(error_or<T, E>(T{std::forward<Args>(args)...})))
{
return error_or<T, E>(T{std::forward<Args>(args)...});
}
}
/// .. |swap(T,T)-noexcept| replace::
/// ``noexcept`` if the function ``<unspecified> swap(T&, T&)`` is ``noexcept``,
/// where ``swap`` is either ``std::swap`` or found by ADL.
#endif

324
thirdparty/source/nu/tests/error_or.cpp vendored Normal file
View File

@@ -0,0 +1,324 @@
#include <nu/error_or.hpp>
#include <gtest/gtest.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
// LCOV_EXCL_START
namespace {
TEST(error_or, example)
{
#include "error_or_example.cpp"
}
using namespace nu;
struct ctor_throws {
ctor_throws(ctor_throws&&);
ctor_throws& operator=(ctor_throws&&) noexcept;
};
struct dtor_throws {
~dtor_throws() noexcept(false);
};
struct swap_throws {
};
[[gnu::unused]]
void swap(swap_throws&, swap_throws) {}
struct called_mask {
bool dtor;
bool swap;
};
struct track_calls {
called_mask* called;
int v;
track_calls(called_mask& c, int v): called(&c), v(v) {}
~track_calls() {
called->dtor = true;
}
friend void swap(track_calls& a, track_calls& b) noexcept {
std::swap(a, b);
a.called->swap = true;
b.called->swap = true;
}
};
}
TEST(error_or, noexceptness)
{
using std::declval;
EXPECT_TRUE(std::is_nothrow_default_constructible<error_or<int>>::value);
EXPECT_TRUE(std::is_nothrow_destructible<error_or<int>>::value);
EXPECT_FALSE(std::is_nothrow_destructible<error_or<dtor_throws>>::value);
EXPECT_TRUE((std::is_nothrow_constructible<error_or<int>, std::error_code>::value));
EXPECT_TRUE((std::is_nothrow_constructible<error_or<int>, int>::value));
EXPECT_FALSE((std::is_nothrow_constructible<error_or<ctor_throws>, ctor_throws>::value));
EXPECT_TRUE((std::is_nothrow_move_constructible<error_or<int>>::value));
EXPECT_FALSE((std::is_nothrow_move_constructible<error_or<ctor_throws>>::value));
EXPECT_TRUE(noexcept(swap(declval<error_or<int>&>(), declval<error_or<int>&>())));
EXPECT_FALSE(noexcept(swap(declval<error_or<swap_throws>&>(), declval<error_or<swap_throws>&>())));
EXPECT_TRUE((std::is_nothrow_move_assignable<error_or<int>>::value));
EXPECT_FALSE((std::is_nothrow_move_assignable<error_or<ctor_throws>>::value));
}
TEST(error_or, validity)
{
{
error_or<int> d;
EXPECT_FALSE(d);
EXPECT_TRUE(!d);
}
{
error_or<int> e(make_error_code(std::errc::invalid_argument));
EXPECT_FALSE(e);
EXPECT_TRUE(!e);
}
{
error_or<int> v(0);
EXPECT_TRUE(v);
EXPECT_FALSE(!v);
}
}
TEST(error_or, dtor)
{
{
called_mask called{};
{
error_or<track_calls> e;
}
EXPECT_FALSE(called.dtor);
}
{
called_mask called{};
{
error_or<track_calls> e(make_error_code(std::errc::invalid_argument));
}
EXPECT_FALSE(called.dtor);
}
{
called_mask called{};
{
error_or<track_calls> e{track_calls{called, 0}};
}
EXPECT_TRUE(called.dtor);
}
}
TEST(error_or, swap)
{
{
error_or<track_calls> d1(make_error_code(std::errc::no_message_available));
error_or<track_calls> d2(make_error_code(std::errc::argument_out_of_domain));
swap(d1, d2);
EXPECT_EQ(d1.error(), std::errc::argument_out_of_domain);
EXPECT_EQ(d2.error(), std::errc::no_message_available);
}
{
called_mask called = {};
error_or<track_calls> d1(make_error_code(std::errc::io_error));
error_or<track_calls> d2(track_calls{called, 1});
swap(d1, d2);
EXPECT_EQ(d1.value().v, 1);
EXPECT_EQ(d2.error(), std::errc::io_error);
}
{
called_mask called1 = {};
called_mask called2 = {};
error_or<track_calls> d1(track_calls{called1, 1});
error_or<track_calls> d2(track_calls{called2, 2});
swap(d1, d2);
EXPECT_EQ(d1.value().v, 2);
EXPECT_EQ(d2.value().v, 1);
}
}
TEST(error_or, move_construct)
{
{
error_or<track_calls> d1;
error_or<track_calls> d2(std::move(d1));
EXPECT_FALSE(d1);
EXPECT_FALSE(d2);
}
{
called_mask called = {};
error_or<track_calls> d2(track_calls{called, 1});
error_or<track_calls> d1(std::move(d2));
EXPECT_EQ(d1.value().v, 1);
EXPECT_FALSE(d2);
EXPECT_FALSE(called.swap);
}
}
TEST(error_or, move_assign)
{
{
error_or<track_calls> d1;
error_or<track_calls> d2;
d2 = std::move(d1);
EXPECT_FALSE(d1);
EXPECT_FALSE(d2);
}
{
called_mask called = {};
error_or<track_calls> d2(track_calls{called, 1});
error_or<track_calls> d1;
d1 = std::move(d2);
EXPECT_EQ(d1.value().v, 1);
EXPECT_FALSE(d2);
EXPECT_FALSE(called.swap);
}
}
TEST(error_or, value__error)
{
called_mask c;
error_or<track_calls> err(make_error_code(std::errc::operation_not_supported));
error_or<track_calls> value(track_calls{c, 1});
ASSERT_THROW(err.value(), bad_error_or_access);
ASSERT_THROW(value.error(), bad_error_or_access);
ASSERT_THROW(static_cast<const error_or<track_calls>&&>(err).value(), bad_error_or_access);
ASSERT_THROW(const_cast<const error_or<track_calls>&>(err).value(), bad_error_or_access);
ASSERT_THROW(err.release_value(), bad_error_or_access);
ASSERT_THROW(value.error(), bad_error_or_access);
ASSERT_EQ(value.value().v, 1);
ASSERT_EQ(const_cast<const error_or<track_calls>&>(value).value().v, 1);
ASSERT_EQ(static_cast<const error_or<track_calls>&&>(error_or<track_calls>(value.value())).value().v, 1);
ASSERT_EQ(error_or<track_calls>(value.value()).value().v, 1);
ASSERT_EQ(error_or<track_calls>(value.value()).release_value().v, 1);
}
TEST(error_or, apply)
{
called_mask c;
error_or<track_calls> e(make_error_code(std::errc::io_error));
error_or<track_calls> v(track_calls{c, 1});
auto conv = [] (track_calls x) { return track_calls{*x.called, x.v + 1}; };
auto convM = [] (track_calls&& x) { return track_calls{*x.called, x.v + 1}; };
auto convI = [] (track_calls) { return 0; };
auto convE = [] (std::error_code) { return 1.; };
auto convC = [] (track_calls) -> error_or<char> { return 17; };
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<const decltype(e)&>(e) / convI),
error_or<int>>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<decltype(e)&>(e) / convI),
error_or<int>>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<decltype(e)&&>(e) / convI),
error_or<int>>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<const decltype(e)&>(e).reduce(convI, convE)),
double>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<decltype(e)&>(e).reduce(convI, convE)),
double>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<decltype(e)&&>(e).reduce(convI, convE)),
double>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<const decltype(e)&>(e) % convC),
error_or<char>>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<decltype(e)&>(e) % convC),
error_or<char>>::value));
EXPECT_TRUE(
(std::is_same<
decltype(static_cast<decltype(e)&&>(e) % convC),
error_or<char>>::value));
{
EXPECT_EQ((static_cast<const decltype(e)&>(e) / conv).error(), std::errc::io_error);
EXPECT_EQ((static_cast<const decltype(e)&>(e) % convC).error(), std::errc::io_error);
EXPECT_EQ((static_cast<const decltype(e)&>(e).reduce(convI, convE)), 1);
EXPECT_EQ((static_cast<const decltype(v)&>(v) / conv).value().v, 2);
EXPECT_EQ((static_cast<const decltype(v)&>(v) % convC).value(), 17);
EXPECT_EQ((static_cast<const decltype(v)&>(v).reduce(convI, convE)), 0);
}
{
EXPECT_EQ((static_cast<decltype(e)&>(e) / conv).error(), std::errc::io_error);
EXPECT_EQ((static_cast<decltype(e)&>(e) % convC).error(), std::errc::io_error);
EXPECT_EQ((static_cast<decltype(e)&>(e).reduce(convI, convE)), 1);
EXPECT_EQ((static_cast<decltype(v)&>(v) / conv).value().v, 2);
EXPECT_EQ((static_cast<decltype(v)&>(v) % convC).value(), 17);
EXPECT_EQ((static_cast<decltype(v)&>(v).reduce(convI, convE)), 0);
}
{
EXPECT_EQ((decltype(e)(e.error()) / convM).error(), std::errc::io_error);
EXPECT_EQ((decltype(e)(e.error()) % convC).error(), std::errc::io_error);
EXPECT_EQ((decltype(e)(e.error()).reduce(convI, convE)), 1);
EXPECT_EQ((decltype(v)(v.value()) / convM).value().v, 2);
EXPECT_EQ((decltype(v)(v.value()) % convC).value(), 17);
EXPECT_EQ((decltype(v)(v.value()).reduce(convI, convE)), 0);
}
}

View File

@@ -0,0 +1,38 @@
using nu::error_or;
using nu::make_error_or;
// default-constructed objects contain Err(), which by default is std::error_code
error_or<int> e;
error_or<int> i(1);
// et will still contain Err()
error_or<int> et = e / [] (int i) { return i+1; };
ASSERT_EQ(et.error(), std::error_code());
// it will contain 2
error_or<int> it = i / [] (int i) { return i+1; };
ASSERT_EQ(it.value(), 2);
// er will be set to -1
int er = e.reduce(
[] (int i) { return i; },
[] (std::error_code) { return -1; });
ASSERT_EQ(er, -1);
// ir will be set to 1
int ir = i.reduce(
[] (int i) { return i; },
[] (std::error_code) { return -1; });
ASSERT_EQ(ir, 1);
// % can be used to chain operations that may themselves fail
error_or<double> d =
make_error_or<int>(1)
/ [] (int i) { return i - 1; }
% [] (int i) -> error_or<double> {
if (i != 0)
return i * 3.14;
else
return {std::make_error_code(std::errc::invalid_argument)};
};
ASSERT_FALSE(d);