Header file status_code.hpp

#include "status_code_domain.hpp"

namespace system_error2
{
    struct in_place_t;

    constexpr system_error2::in_place_t const in_place;
}

namespace system_error2
{
    namespace mixins
    {
        template <class Base, class T>
        struct mixin;
    }

    template <class ErasedType, typename std::enable_if<traits::is_move_bitcopying<ErasedType>::value, bool>::type = true>
    struct erased;

    template <class StatusCode>
    struct ptr;

    template <class T>
    struct is_status_code;

    template <>
    class status_code<void>;

    template <class DomainType>
    class status_code;

    namespace traits
    {
        template <class DomainType>
        struct is_move_bitcopying<status_code<DomainType>;
    }

    template <class ErasedType>
    class status_code<erased<ErasedType>;

    namespace traits
    {
        template <class ErasedType>
        struct is_move_relocating<status_code<erased<ErasedType>>>;
    }
}

Struct system_error2::in_place_t

struct in_place_t
{
    explicit in_place_t() = default;
};

Aliases std::in_place_t if on C++ 17 or later, else defined locally.


Variable system_error2::in_place

constexpr system_error2::in_place_t const in_place;

Aliases std::in_place if on C++ 17 or later, else defined locally.


Namespace mixins

namespace mixins
{
    template <class Base, class T>
    struct mixin;
}

Namespace for user injected mixins


Struct system_error2::erased

template <class ErasedType, typename std::enable_if<traits::is_move_bitcopying<ErasedType>::value, bool>::type = true>
struct erased
{
    using value_type = ErasedType;
};

A tag for an erased value type for status_code<D>.

Available only if ErasedType satisfies traits::is_move_bitcopying<ErasedType>::value.


Struct system_error2::ptr

template <class StatusCode>
struct ptr
{
    using value_type = StatusCode*;
};

A tag for an unique pointer to a StatusCode.


Struct system_error2::is_status_code

template <class T>
struct is_status_code
{
    static constexpr bool const value;
};

Trait returning true if the type is a status code.


Class system_error2::status_code

template <>
class status_code<void>
{
public:
    using domain_type = void;

    using value_type = void;

    using string_ref = typename status_code_domain::string_ref;

protected:
    system_error2::status_code_domain const* _domain;

    status_code() = default;

    status_code(system_error2::status_code<void> const&) = default;

    status_code(system_error2::status_code<void>&&) = default;

    system_error2::status_code<void>& operator=(system_error2::status_code<void> const&) = default;

    system_error2::status_code<void>& operator=(system_error2::status_code<void>&&) = default;

    ~status_code() = default;

    constexpr status_code(system_error2::status_code_domain const* v) noexcept;

public:
    constexpr system_error2::status_code_domain const& domain() const noexcept;

    constexpr bool empty() const noexcept;

    string_ref message() const noexcept;

    bool success() const noexcept;

    bool failure() const noexcept;

    template <class T>
    bool strictly_equivalent(status_code<T> const& o) const noexcept;

    template <class T>
    bool equivalent(status_code<T> const& o) const noexcept;

    void throw_exception() const;
};

A type erased lightweight status code reflecting empty, success, or failure.

Differs from status_code<erased<>> by being always available irrespective of the domain’s value type, but cannot be copied, moved, nor destructed. Thus one always passes this around by const lvalue reference.

Type alias system_error2::status_code::domain_type

using domain_type = void;

The type of the domain.


Type alias system_error2::status_code::value_type

using value_type = void;

The type of the status code.


Type alias system_error2::status_code::string_ref

using string_ref = typename status_code_domain::string_ref;

The type of a reference to a message string.


Constructor system_error2::status_code::status_code

status_code() = default;

No default construction at type erased level


Constructor system_error2::status_code::status_code

status_code(system_error2::status_code<void> const&) = default;

No public copying at type erased level


Constructor system_error2::status_code::status_code

status_code(system_error2::status_code<void>&&) = default;

No public moving at type erased level


Function system_error2::status_code::operator=

system_error2::status_code<void>& operator=(system_error2::status_code<void> const&) = default;

No public assignment at type erased level


Function system_error2::status_code::operator=

system_error2::status_code<void>& operator=(system_error2::status_code<void>&&) = default;

No public assignment at type erased level


Destructor system_error2::status_code::~status_code

~status_code() = default;

No public destruction at type erased level


Constructor system_error2::status_code::status_code

constexpr status_code(system_error2::status_code_domain const* v) noexcept;

Used to construct a non-empty type erased status code


Function system_error2::status_code::domain

constexpr system_error2::status_code_domain const& domain() const noexcept;

Return the status code domain.


Function system_error2::status_code::empty

constexpr bool empty() const noexcept;

True if the status code is empty.


Function system_error2::status_code::message

string_ref message() const noexcept;

Return a reference to a string textually representing a code.


Function system_error2::status_code::success

bool success() const noexcept;

True if code means success.


Function system_error2::status_code::failure

bool failure() const noexcept;

True if code means failure.


Function system_error2::status_code::strictly_equivalent

template <class T>
bool strictly_equivalent(status_code<T> const& o) const noexcept;

True if code is strictly (and potentially non-transitively) semantically equivalent to another code in another domain.

Note that usually non-semantic i.e. pure value comparison is used when the other status code has the same domain. As equivalent() will try mapping to generic code, this usually captures when two codes have the same semantic meaning in equivalent().


Function system_error2::status_code::equivalent

template <class T>
bool equivalent(status_code<T> const& o) const noexcept;

True if code is equivalent, by any means, to another code in another domain (guaranteed transitive).

Firstly strictly_equivalent() is run in both directions. If neither succeeds, each domain is asked for the equivalent generic code and those are compared.


Function system_error2::status_code::throw_exception

void throw_exception() const;

Throw a code as a C++ exception.



Class system_error2::status_code

template <class DomainType>
class status_code
: public mixins::mixin<detail::status_code_storage<DomainType>,DomainType>
{
public:
    using domain_type = DomainType;

    using value_type = typename domain_type::value_type;

    using string_ref = typename domain_type::string_ref;

    status_code() = default;

    status_code(const status_code<DomainType>&) = default;

    status_code(status_code<DomainType>&&) = default;

    status_code<DomainType>& operator=(const status_code<DomainType>&) = default;

    status_code<DomainType>& operator=(status_code<DomainType>&&) = default;

    ~status_code() = default;

    constexpr status_code<DomainType> clone() const;

    template <class T, class ... Args, class MakeStatusCodeOutType = decltype(make_status_code(std::declval<T>(),std::declval<Args>()...)), typename std::enable_if<!std::is_same<typename std::decay<T>::type, status_code<DomainType> >::value && is_status_code<MakeStatusCodeOutType>::value && std::is_constructible<status_code<DomainType>, MakeStatusCodeOutType>::value, bool>::type = true>
    constexpr status_code(T&& v, Args &&... args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...)));

    template <class ... Args>
    constexpr status_code(system_error2::in_place_t, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, Args && ...>::value);

    template <class T, class ... Args>
    constexpr status_code(system_error2::in_place_t, std::initializer_list<T> il, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<T>, Args && ...>::value);

    constexpr status_code(system_error2::status_code::value_type const& v) noexcept(std::is_nothrow_copy_constructible<value_type>::value);

    constexpr status_code(system_error2::status_code::value_type&& v) noexcept(std::is_nothrow_move_constructible<value_type>::value);

    template <class ErasedType, typename std::enable_if<detail::type_erasure_is_safe<ErasedType, value_type>::value, bool>::type = true>
    constexpr status_code(status_code<erased<ErasedType>> const& v) noexcept(std::is_nothrow_copy_constructible<value_type>::value);

    constexpr status_code<DomainType>& operator=(system_error2::status_code::value_type const& v) noexcept(std::is_nothrow_copy_assignable<value_type>::value);

    string_ref message() const noexcept;
};

A lightweight, typed, status code reflecting empty, success, or failure.

This is the main workhorse of the system_error2 library. Its characteristics reflect the value type set by its domain type, so if that value type is move-only or trivial, so is this.

An ADL discovered helper function make_status_code(T, Args...) is looked up by one of the constructors. If it is found, and it generates a status code compatible with this status code, implicit construction is made available.

You may mix in custom member functions and member function overrides by injecting a specialisation of mixins::mixin<Base, YourDomainType>. Your mixin must inherit from Base.

Type alias system_error2::status_code::domain_type

using domain_type = DomainType;

The type of the domain.


Type alias system_error2::status_code::value_type

using value_type = typename domain_type::value_type;

The type of the status code.


Type alias system_error2::status_code::string_ref

using string_ref = typename domain_type::string_ref;

The type of a reference to a message string.


Constructor system_error2::status_code::status_code

status_code() = default;

Default construction to empty


Constructor system_error2::status_code::status_code

status_code(const status_code<DomainType>&) = default;

Copy constructor


Constructor system_error2::status_code::status_code

status_code(status_code<DomainType>&&) = default;

Move constructor


Function system_error2::status_code::operator=

status_code<DomainType>& operator=(const status_code<DomainType>&) = default;

Copy assignment


Function system_error2::status_code::operator=

status_code<DomainType>& operator=(status_code<DomainType>&&) = default;

Move assignment


Function system_error2::status_code::clone

constexpr status_code<DomainType> clone() const;

Return a copy of the code.


Constructor system_error2::status_code::status_code

template <class T, class ... Args, class MakeStatusCodeOutType = decltype(make_status_code(std::declval<T>(),std::declval<Args>()...)), typename std::enable_if<!std::is_same<typename std::decay<T>::type, status_code<DomainType> >::value && is_status_code<MakeStatusCodeOutType>::value && std::is_constructible<status_code<DomainType>, MakeStatusCodeOutType>::value, bool>::type = true>
constexpr status_code(T&& v, Args &&... args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...)));

Implicit construction from any type where an ADL discovered make_status_code(T, Args ...) returns a status_code.


Constructor system_error2::status_code::status_code

template <class ... Args>
constexpr status_code(system_error2::in_place_t, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, Args && ...>::value);

Explicit in-place construction.


Constructor system_error2::status_code::status_code

template <class T, class ... Args>
constexpr status_code(system_error2::in_place_t, std::initializer_list<T> il, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<T>, Args && ...>::value);

Explicit in-place construction from initialiser list.


Constructor system_error2::status_code::status_code

constexpr status_code(system_error2::status_code::value_type const& v) noexcept(std::is_nothrow_copy_constructible<value_type>::value);

Explicit copy construction from a value_type.


Constructor system_error2::status_code::status_code

constexpr status_code(system_error2::status_code::value_type&& v) noexcept(std::is_nothrow_move_constructible<value_type>::value);

Explicit move construction from a value_type.


Constructor system_error2::status_code::status_code

template <class ErasedType, typename std::enable_if<detail::type_erasure_is_safe<ErasedType, value_type>::value, bool>::type = true>
constexpr status_code(status_code<erased<ErasedType>> const& v) noexcept(std::is_nothrow_copy_constructible<value_type>::value);

Explicit construction from an erased status code. Available only if value_type is trivially copyable or move relocating, and sizeof(status_code) <= sizeof(status_code<erased<>>).

Does not check if domains are equal.


Function system_error2::status_code::operator=

constexpr status_code<DomainType>& operator=(system_error2::status_code::value_type const& v) noexcept(std::is_nothrow_copy_assignable<value_type>::value);

Assignment from a value_type.


Function system_error2::status_code::message

string_ref message() const noexcept;

Return a reference to a string textually representing a code.



Class system_error2::status_code

template <class ErasedType>
class status_code<erased<ErasedType>
: public mixins::mixin<detail::status_code_storage<erased<ErasedType>>,erased<ErasedType>>
{
public:
    using domain_type = void;

    using value_type = ErasedType;

    using string_ref = typename _base::string_ref;

    status_code() = default;

    status_code(status_code<erased<type-parameter-0-0, true>> const&) = delete;

    status_code(status_code<erased<type-parameter-0-0, true>>&&) = default;

    status_code<erased<type-parameter-0-0, true>>& operator=(status_code<erased<type-parameter-0-0, true>> const&) = delete;

    status_code<erased<type-parameter-0-0, true>>& operator=(status_code<erased<type-parameter-0-0, true>>&&) = default;

    ~status_code();

    status_code<erased<type-parameter-0-0, true>> clone() const;

    template <class DomainType, typename std::enable_if<detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value, bool>::type = true>
    constexpr status_code(status_code<DomainType>&& v) noexcept;

    template <class T, class ... Args, class MakeStatusCodeOutType = decltype(make_status_code(std::declval<T>(),std::declval<Args>()...)), typename std::enable_if<!std::is_same<typename std::decay<T>::type, status_code<erased<type-parameter-0-0, true> > >::value && !std::is_same<typename std::decay<T>::type, value_type>::value && is_status_code<MakeStatusCodeOutType>::value && std::is_constructible<status_code<erased<type-parameter-0-0, true> >, MakeStatusCodeOutType>::value, bool>::type = true>
    constexpr status_code(T&& v, Args &&... args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...)));

    constexpr void clear() noexcept;

    constexpr value_type value() const noexcept;
};

Type erased, move-only status_code, unlike status_code<void> which cannot be moved nor destroyed. Available only if erased<> is available, which is when the domain’s type is trivially copyable or is move relocatable, and if the size of the domain’s typed error code is less than or equal to this erased error code. Copy construction is disabled, but if you want a copy call .clone().

An ADL discovered helper function make_status_code(T, Args...) is looked up by one of the constructors. If it is found, and it generates a status code compatible with this status code, implicit construction is made available.

Type alias system_error2::status_code::domain_type

using domain_type = void;

The type of the domain (void, as it is erased).


Type alias system_error2::status_code::value_type

using value_type = ErasedType;

The type of the erased status code.


Type alias system_error2::status_code::string_ref

using string_ref = typename _base::string_ref;

The type of a reference to a message string.


Constructor system_error2::status_code::status_code

status_code() = default;

Default construction to empty


Constructor system_error2::status_code::status_code

status_code(status_code<erased<type-parameter-0-0, true>> const&) = delete;

Copy constructor


Constructor system_error2::status_code::status_code

status_code(status_code<erased<type-parameter-0-0, true>>&&) = default;

Move constructor


Function system_error2::status_code::operator=

status_code<erased<type-parameter-0-0, true>>& operator=(status_code<erased<type-parameter-0-0, true>> const&) = delete;

Copy assignment


Function system_error2::status_code::operator=

status_code<erased<type-parameter-0-0, true>>& operator=(status_code<erased<type-parameter-0-0, true>>&&) = default;

Move assignment


Function system_error2::status_code::clone

status_code<erased<type-parameter-0-0, true>> clone() const;

Return a copy of the erased code by asking the domain to perform the erased copy.


Constructor system_error2::status_code::status_code

template <class DomainType, typename std::enable_if<detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value, bool>::type = true>
constexpr status_code(status_code<DomainType>&& v) noexcept;

Implicit move construction from any other status code if its value type is trivially copyable or move relocating and it would fit into our storage


Constructor system_error2::status_code::status_code

template <class T, class ... Args, class MakeStatusCodeOutType = decltype(make_status_code(std::declval<T>(),std::declval<Args>()...)), typename std::enable_if<!std::is_same<typename std::decay<T>::type, status_code<erased<type-parameter-0-0, true> > >::value && !std::is_same<typename std::decay<T>::type, value_type>::value && is_status_code<MakeStatusCodeOutType>::value && std::is_constructible<status_code<erased<type-parameter-0-0, true> >, MakeStatusCodeOutType>::value, bool>::type = true>
constexpr status_code(T&& v, Args &&... args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...)));

Implicit construction from any type where an ADL discovered make_status_code(T, Args ...) returns a status_code.


Function system_error2::status_code::clear

constexpr void clear() noexcept;

Reset the code to empty.


Function system_error2::status_code::value

constexpr value_type value() const noexcept;

Return the erased value_type by value.