// bslstl_error.h                                                     -*-C++-*-
#ifndef INCLUDED_BSLSTL_ERROR
#define INCLUDED_BSLSTL_ERROR

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

// BDE_VERIFY pragma: -TP25  // CLASSES are not defined in C++11

//@PURPOSE: Provide standard compliant versions of <system_error> classes.
//
//@CLASSES:
//  bsl::error_category: a standard compliant version of `error_category`
//  bsl::error_code: a standard complaint version of `error_code`
//  bsl::error_condition: a standard complaint version of `error_condition`
//
//@CANONICAL_HEADER: bsl_system_error.h
//
//@DESCRIPTION: This component defines classes `bsl::error_category`,
// `bsl::error_code`, and `bsl::error_condition`, global functions
// `bsl::generic_category`, `bsl::system_category`, `bsl::make_error_code`, and
// `bsl::make_error_condition`, and a variety of operators that provide
// implementations of the C++11 `system_error` facility.  In C++11 mode, the
// vendor-supplied `<system_error>` implementation is used instead, and the
// corresponding names from `std` are imported into `bsl`.
//
///Usage
///-----
// In this section we show intended use of this component.
//
///Example 1: TBD
///- - - - - - - - - - - - - - - - - -
// We are in the process of determining best practices for using error codes in
// our programming environment.

#include <bslscm_version.h>
#include <bslstl_errc.h>
#include <bslstl_hash.h>
#include <bslstl_iserrorcodeenum.h>
#include <bslstl_iserrorconditionenum.h>

#include <bslh_hash.h>

#include <bslmf_enableif.h>
#include <bslmf_integralconstant.h>

#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>
#include <bsls_platform.h>
#include <bsls_unspecifiedbool.h>

#include <errno.h>
#include <string.h>

#include <cstring>
#include <functional>
#include <iosfwd>
#include <stdexcept>
#include <string>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bsls_nativestd.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

#include <system_error>

namespace bsl {

using std::error_category;
using std::error_code;
using std::error_condition;
using std::generic_category;
using std::system_category;
using std::make_error_code;
using std::make_error_condition;

}  // close namespace bsl

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY

namespace bsl {

template <>
struct hash<error_code> : std::hash<error_code>
{
};

template <>
struct hash<error_condition> : std::hash<error_condition>
{
};

}  // close namespace bsl

#endif

#else

namespace bsl {

// FORWARD DECLARATIONS
class error_code;
class error_condition;

                            // ====================
                            // class error_category
                            // ====================

/// This `class` acts as a base for types that represent the source and
/// encoding of error categories.
class error_category {

  private:
    // PRIVATE CREATORS
    error_category(const error_category&);             // = delete

    // PRIVATE MANIPULATORS
    error_category& operator=(const error_category&);  // = delete

  public:
    // CREATORS

    /// Create an object of this type.
    error_category();

    /// Destroy this object.
    virtual ~error_category();

    // ACCESSORS

    /// Return an `error_condition` object initialized with the specified
    /// `value` and this object as the error category.
    virtual error_condition default_error_condition(int value) const
    BSLS_KEYWORD_NOEXCEPT;

    /// Return, for the error category defined by this object, whether the
    /// specified `code` and `condition` are considered equivalent.
    virtual bool equivalent(int code, const error_condition& condition) const
    BSLS_KEYWORD_NOEXCEPT;
    virtual bool equivalent(const error_code& code, int condition) const
    BSLS_KEYWORD_NOEXCEPT;

    /// Return a string describing the error condition denoted by the
    /// specified `value`.
    virtual std::string message(int value) const = 0;

    /// Return the name of this error category.
    virtual const char *name() const BSLS_KEYWORD_NOEXCEPT = 0;

    /// Return whether this object is the same as the specified `other`.
    bool operator==(const error_category& other) const BSLS_KEYWORD_NOEXCEPT;

    /// Return whether this object is different than the specified `other`.
    bool operator!=(const error_category& other) const BSLS_KEYWORD_NOEXCEPT;

    /// Return whether this object precedes the specified `other` in a total
    /// ordering of `error_category` objects.
    bool operator<(const error_category& other) const BSLS_KEYWORD_NOEXCEPT;
};

                              // ================
                              // class error_code
                              // ================

/// This `class` represents a system-specific error value.
class error_code {

  private:
    // PRIVATE TYPES
    typedef BloombergLP::bsls::UnspecifiedBool<error_code> UnspecifiedBool;
    typedef UnspecifiedBool::BoolType                      BoolType;

  public:
    // CREATORS

    /// Create an object of this type initialized with value 0 and system
    /// category.
    error_code();

    /// Create an object of this type initialized with the specified `value`
    /// and `category`.
    error_code(int value, const error_category& category);

    /// Construct an object of this type initialized with the specified
    /// `value` and its category (found from an overloaded call to
    /// `make_error_code`).  Note that this constructor exists only for
    /// those types designated as error codes via the `is_error_code_enum`
    /// trait template.
    template <class ERROR_CODE_ENUM>
    error_code(ERROR_CODE_ENUM value,
               typename enable_if<is_error_code_enum<ERROR_CODE_ENUM>::value,
                                  BoolType>::type = 0);             // IMPLICIT

    // MANIPULATORS

    /// Set this object to hold the specified `value` and `category`.
    void assign(int value, const error_category& category);

    /// Set this object to hold the specified `value` and its category
    /// (found from an overloaded call to `make_error_code`).  Note that
    /// this operator exists only for those types designated as error codes
    /// via the `is_error_code_enum` trait template.  Note that this object
    /// is set to the generic rather than system category, because that is
    /// what the standard specifies.
    template <class ERROR_CODE_ENUM>
    typename enable_if<is_error_code_enum<ERROR_CODE_ENUM>::value,
                       error_code&>::type
    operator=(ERROR_CODE_ENUM value);

    /// Set this object to hold the value 0 and the system category.
    void clear();

    // ACCESSORS

    /// Return a `const` reference to the category held by this object.
    const error_category& category() const;

    /// Return an `error_condition` object initialized with the value and
    /// category of this object.
    error_condition default_error_condition() const;

    /// Return a string describing this object.
    std::string message() const;

    /// Return the value held by this object.
    int value() const;

    /// Return whether the value held by this object is non-zero.
    operator BoolType() const;

  private:
    // DATA
    int                   d_value;       // error code value
    const error_category *d_category_p;  // error category
};

                           // =====================
                           // class error_condition
                           // =====================

/// This `class` represents a portable error value.
class error_condition {

  private:
    // PRIVATE TYPES
    typedef BloombergLP::bsls::UnspecifiedBool<error_condition>
                                      UnspecifiedBool;
    typedef UnspecifiedBool::BoolType BoolType;

  public:
    // CREATORS

    /// Create an object of this type initialized with value 0 and generic
    /// category.
    error_condition();

    /// Create an object of this type initialized with the specified `value`
    /// and `category`.
    error_condition(int value, const error_category& category);

    /// Construct an object of this type initialized with the specified
    /// `value` and its category (found from an overloaded call to
    /// `make_error_condition`).  Note that this constructor exists only for
    /// those types designated as error conditions via the
    /// `is_error_condition_enum` trait template.
    template <class ERROR_CONDITION_ENUM>
    error_condition(ERROR_CONDITION_ENUM value,
                    typename enable_if<
                        is_error_condition_enum<ERROR_CONDITION_ENUM>::value,
                        BoolType>::type = 0);                       // IMPLICIT

    // MANIPULATORS

    /// Set this object to hold the specified `value` and `category`.
    void assign(int value, const error_category& category);

    /// Set this object to hold the specified `value` and its category
    /// (found from an overloaded call to `make_error_condition`).  Note
    /// that this operator exists only for those types designated as error
    /// conditions via the `is_error_condition_enum` trait template.
    template <class ERROR_CONDITION_ENUM>
    typename enable_if<is_error_condition_enum<ERROR_CONDITION_ENUM>::value,
                       error_condition&>::type
    operator=(ERROR_CONDITION_ENUM value);

    /// Set this object to hold the value 0 and the generic category.
    void clear();

    // ACCESSORS

    /// Return a `const` reference to the category held by this object.
    const error_category& category() const;

    /// Return a string describing this object.
    std::string message() const;

    /// Return the value held by this object.
    int value() const;

    /// Return whether the value held by this object is non-zero.
    operator BoolType() const;

  private:
    // DATA
    int                   d_value;       // error code value
    const error_category *d_category_p;  // error category
};

// FREE FUNCTIONS

/// Return a `const` reference to the unique generic category object.
const error_category& generic_category();

/// Return a `const` reference to the unique system category object.
const error_category& system_category();

/// Return an `error_code` object holding the specified `value` and generic
/// category.  Note that the category is generic rather than system because
/// that is what the standard specifies.
error_code make_error_code(errc::Enum value);

/// Return an `error_condition` object holding the specified `value` and
/// generic category.
error_condition make_error_condition(errc::Enum value);

/// Hash the specified `object` using the specified `hashAlgorithm`.
template <class HASHALG>
void hashAppend(HASHALG& hashAlgorithm, const error_code& object);

/// Hash the specified `object` using the specified `hashAlgorithm`.
template <class HASHALG>
void hashAppend(HASHALG& hashAlgorithm, const error_condition& object);

// FREE OPERATORS

/// Return whether the specified `lhs` and `rhs` are equal or equivalent.
bool operator==(const error_code& lhs, const error_code& rhs);
bool operator==(const error_code& lhs, const error_condition& rhs);
bool operator==(const error_condition& lhs, const error_code& rhs);
bool operator==(const error_condition& lhs, const error_condition& rhs);

/// Return whether the specified `lhs` and `rhs` are not equal or equivalent.
bool operator!=(const error_code&, const error_code&);
bool operator!=(const error_code&, const error_condition&);
bool operator!=(const error_condition&, const error_code&);
bool operator!=(const error_condition&, const error_condition&);

/// Return whether the specified `lhs` is lexicographically less than the
/// specified `rhs`, ordered by category then value.
bool operator<(const error_code& lhs, const error_code& rhs);
bool operator<(const error_condition& lhs, const error_condition& rhs);

/// Write the specified `code` to `stream`.
template <class CHAR_TYPE, class CHAR_TRAITS>
inline
std::basic_ostream<CHAR_TYPE, CHAR_TRAITS>& operator<<(
                     std::basic_ostream<CHAR_TYPE, CHAR_TRAITS>& stream,
                     const error_code&                           code);

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                            // --------------------
                            // class error_category
                            // --------------------

//CREATORS
inline
error_category::error_category()
{
}

// ACCESSORS
inline
error_condition error_category::default_error_condition(
                                         int value) const BSLS_KEYWORD_NOEXCEPT
{
    return error_condition(value, *this);
}

inline
bool error_category::equivalent(
                  int                    code,
                  const error_condition& condition) const BSLS_KEYWORD_NOEXCEPT
{
    return default_error_condition(code) == condition;
}

inline
bool error_category::equivalent(
                       const error_code& code,
                       int               condition) const BSLS_KEYWORD_NOEXCEPT
{
    return *this == code.category() && code.value() == condition;
}

inline
std::string error_category::message(int value) const
{
    return strerror(value);
}

inline
const char *error_category::name() const BSLS_KEYWORD_NOEXCEPT
{
    return "error_category";
}

inline
bool error_category::operator==(
                       const error_category& other) const BSLS_KEYWORD_NOEXCEPT
{
    return this == &other;
}

inline
bool error_category::operator!=(
                       const error_category& other) const BSLS_KEYWORD_NOEXCEPT
{
    return !(*this == other);
}

inline
bool error_category::operator<(
                       const error_category& other) const BSLS_KEYWORD_NOEXCEPT
{
    return std::less<const error_category *>()(this, &other);
}

                              // ----------------
                              // class error_code
                              // ----------------

// CREATORS
inline
error_code::error_code()
: d_value(0)
, d_category_p(&system_category())
{
}

inline
error_code::error_code(int value, const error_category& category)
: d_value(value)
, d_category_p(&category)
{
}

template <class ERROR_CODE_ENUM>
inline
error_code::error_code(ERROR_CODE_ENUM value,
                       typename enable_if<
                           is_error_code_enum<ERROR_CODE_ENUM>::value,
                           BoolType>::type)                         // IMPLICIT
: d_value(make_error_code(value).value())
, d_category_p(&make_error_code(value).category())
{
}


// MANIPULATORS
inline
void error_code::assign(int value, const error_category& category)
{
    d_value = value;
    d_category_p = &category;
}

inline
void error_code::clear()
{
    d_value = 0;
    d_category_p = &system_category();
}

template <class ERROR_CODE_ENUM>
inline
typename enable_if<is_error_code_enum<ERROR_CODE_ENUM>::value,
                   error_code&>::type
error_code::operator=(ERROR_CODE_ENUM value)
{
    d_value = make_error_code(value).value();
    d_category_p = &make_error_code(value).category();
    return *this;
}

// ACCESSORS
inline
const error_category& error_code::category() const
{
    return *d_category_p;
}

inline
error_condition error_code::default_error_condition() const
{
    return category().default_error_condition(value());
}

inline
std::string error_code::message() const
{
    return category().message(value());
}

inline
int error_code::value() const
{
    return d_value;
}

inline
error_code::operator BoolType() const
{
    return UnspecifiedBool::makeValue(value());
}

// FREE FUNCTIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlgorithm, const error_code& object)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlgorithm, static_cast<const void *>(&object.category()));
    hashAppend(hashAlgorithm, object.value());
}

inline
error_code make_error_code(errc::Enum value)
{
    return error_code(static_cast<int>(value), generic_category());
}

// FREE OPERATORS
template <class CHAR_TYPE, class CHAR_TRAITS>
inline
std::basic_ostream<CHAR_TYPE, CHAR_TRAITS>& operator<<(
                     std::basic_ostream<CHAR_TYPE, CHAR_TRAITS>& stream,
                     const error_code&                           code)
{
    return stream << code.category().name() << ':' << code.value();
}

                           // ---------------------
                           // class error_condition
                           // ---------------------

// CREATORS
inline
error_condition::error_condition()
: d_value(0)
, d_category_p(&generic_category())
{
}

inline
error_condition::error_condition(int value, const error_category& category)
: d_value(value)
, d_category_p(&category)
{
}

template <class ERROR_CONDITION_ENUM>
inline
error_condition::error_condition(
       ERROR_CONDITION_ENUM value,
       typename enable_if<is_error_condition_enum<ERROR_CONDITION_ENUM>::value,
                          BoolType>::type)                          // IMPLICIT
: d_value(make_error_condition(value).value())
, d_category_p(&make_error_condition(value).category())
{
}

// MANIPULATORS
inline
void error_condition::assign(int value, const error_category& category)
{
    d_value = value;
    d_category_p = &category;
}

inline
void error_condition::clear()
{
    d_value = 0;
    d_category_p = &generic_category();
}

template <class ERROR_CONDITION_ENUM>
inline
typename enable_if<is_error_condition_enum<ERROR_CONDITION_ENUM>::value,
                   error_condition&>::type
error_condition::operator=(ERROR_CONDITION_ENUM value)
{
    d_value = make_error_condition(value).value();
    d_category_p = &make_error_condition(value).category();
    return *this;
}

// ACCESSORS
inline
const error_category& error_condition::category() const
{
    return *d_category_p;
}

inline
std::string error_condition::message() const
{
    return category().message(value());
}

inline
int error_condition::value() const
{
    return d_value;
}

inline
error_condition::operator BoolType() const
{
    return UnspecifiedBool::makeValue(value());
}

// FREE FUNCTIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlgorithm, const error_condition& object)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlgorithm, static_cast<const void *>(&object.category()));
    hashAppend(hashAlgorithm, object.value());
}

inline
error_condition make_error_condition(errc::Enum value)
{
    return error_condition(static_cast<int>(value), generic_category());
}

// FREE OPERATORS
inline
bool operator==(const error_code& lhs, const error_code& rhs)
{
    return lhs.category() == rhs.category() && lhs.value() == rhs.value();
}

inline
bool operator==(const error_code& lhs, const error_condition& rhs)
{
    return lhs.category().equivalent(lhs.value(), rhs) ||
           rhs.category().equivalent(lhs, rhs.value());
}

inline
bool operator==(const error_condition& lhs, const error_code& rhs)
{
    return rhs.category().equivalent(rhs.value(), lhs) ||
           lhs.category().equivalent(rhs, lhs.value());
}

inline
bool operator==(const error_condition& lhs, const error_condition& rhs)
{
    return lhs.category() == rhs.category() && lhs.value() == rhs.value();
}

inline
bool operator!=(const error_code& lhs, const error_code& rhs)
{
    return !(lhs == rhs);
}

inline
bool operator!=(const error_code& lhs, const error_condition& rhs)
{
    return !(lhs == rhs);
}

inline
bool operator!=(const error_condition& lhs, const error_code& rhs)
{
    return !(lhs == rhs);
}

inline
bool operator!=(const error_condition& lhs, const error_condition& rhs)
{
    return !(lhs == rhs);
}

inline
bool operator<(const error_code& lhs, const error_code& rhs)
{
    return lhs.category() < rhs.category() ||
           (lhs.category() == rhs.category() && lhs.value() < rhs.value());
}

inline
bool operator<(const error_condition& lhs, const error_condition& rhs)
{
    return lhs.category() < rhs.category() ||
           (lhs.category() == rhs.category() && lhs.value() < rhs.value());
}

template <>
struct hash<bsl::error_code> : BloombergLP::bslh::Hash<>
{
};

template <>
struct hash<bsl::error_condition> : BloombergLP::bslh::Hash<>
{
};

}  // close namespace bsl

namespace std {

#if !defined(BSLS_PLATFORM_OS_DARWIN) || defined (BSLS_PLATFORM_CMP_GNU)
  // On C++03 on Darwin, the template struct 'hash' is forward declared with
  // different attributes in <typetraits> that conflict with this forward
  // declaration.

  template <class TYPE>
  struct hash;
#endif

template <>
struct hash<bsl::error_code> : BloombergLP::bslh::Hash<>
{
};

template <>
struct hash<bsl::error_condition> : BloombergLP::bslh::Hash<>
{
};
}  // close namespace std

#endif
#endif

// ----------------------------------------------------------------------------
// Copyright 2019 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
