New upstream version 8.1.0
This commit is contained in:
19
thirdparty/source/nu/LICENSE
vendored
Normal file
19
thirdparty/source/nu/LICENSE
vendored
Normal 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
136
thirdparty/source/nu/Makefile
vendored
Normal 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
28
thirdparty/source/nu/docs/conf.py
vendored
Normal 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
28
thirdparty/source/nu/docs/extract.awk
vendored
Normal 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
17
thirdparty/source/nu/docs/index.rst
vendored
Normal 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`
|
||||
419
thirdparty/source/nu/include/nu/error_or.hpp
vendored
Normal file
419
thirdparty/source/nu/include/nu/error_or.hpp
vendored
Normal 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
324
thirdparty/source/nu/tests/error_or.cpp
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
38
thirdparty/source/nu/tests/error_or_example.cpp
vendored
Normal file
38
thirdparty/source/nu/tests/error_or_example.cpp
vendored
Normal 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);
|
||||
Reference in New Issue
Block a user