// 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
                            // ====================

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

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

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

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

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

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

    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, for the error category defined by this object, whether the
        // specified 'code' and 'condition' are considered equivalent.

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

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

    bool operator==(const error_category& other) const BSLS_KEYWORD_NOEXCEPT;
        // 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.
};

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

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

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

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

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

    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
        // 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.

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

    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 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.

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

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

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

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

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

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

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

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

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

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

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

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

    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
        // 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.

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

    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 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.

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

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

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

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

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

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

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

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

error_code make_error_code(errc::Enum value);
    // 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_condition make_error_condition(errc::Enum value);
    // Return an 'error_condition' object holding the specified 'value' and
    // generic category.

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);
    // Hash the specified 'object' using the specified 'hashAlgorithm'.

// FREE OPERATORS
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 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' and 'rhs' are not equal or
    // equivalent.

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

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);
    // Write the specified 'code' to 'stream'.

// ============================================================================
//                             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)
    // Hash the specified 'object' using the specified 'hashAlgorithm'.
{
    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 ----------------------------------