// bslmf_isbitwiseequalitycomparable.h                                -*-C++-*-
#ifndef INCLUDED_BSLMF_ISBITWISEEQUALITYCOMPARABLE
#define INCLUDED_BSLMF_ISBITWISEEQUALITYCOMPARABLE

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

//@PURPOSE: Provide a type trait for bitwise equality.
//
//@CLASSES:
//  bslmf::IsBitwiseEqualityComparable: trait metafunction
//
//@DESCRIPTION: This component provides a single trait metafunction,
// `bslmf::IsBitwiseEqualityComparable`, which allows generic code to determine
// whether objects of the specified `t_TYPE` can be compared using `memcmp`.
// Such types are said to be bitwise EqualityComparable.  Observe that this
// trait may be `true` only for object types, even though, for example,
// reference types may be guaranteed identical bit representations when they
// refer to the same object, just as the corresponding pointer type would be
// bitwise EqualityComparable.
//
// For a type to be bitwise EqalityComparable, each bit of its object
// representation must be significant in the value representation, and distinct
// sequences of bits represent different values, i.e., this trait is an
// assertion that the specified `t_TYPE` has unique representations for each
// possible value, and no padding bits.  This property is deemed to hold for
// `bool` and enumerations where, in practice, the compiler will enforce a
// value representation over all the seemingly unused bits.  For a C++17 tool
// chain, this trait should be equivalent to the
// `std::has_unique_object_representation` trait.
//
// Note that as arrays are not allowed to introduce padding, arrays of a
// bitwise EqualityComparable `t_TYPE` are also bitwise EqualityComparable,
// even though they do not provide an overloaded `operator==`.  While
// transforming comparisons of a single object using this trait into calls to
// `memcmp` is unlikely to be profitable, transforming comparisons of a whole
// array into a single `memcmp` call is more likely to be beneficial.
//
///Usage
///-----
//
///Example 1: Using the trait to optimize range comparison
///- - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we want to compare two sequences of the same object type to
// determine whether or not they hold the same values.  The simplest solution
// would be to iterate over both sequences, comparing each member, and return
// `false` as soon as any pair of elements do not compare equal; if we walk all
// the way to the end of both sequences, then they hold the same values.  If we
// want to perform this comparison most efficiently though, we would rather not
// invoke `operator==` on each member, and instead defer to the `memcmp`
// function in the standard library that is highly optimized (often to take
// advantage of platform-specific instructions) for comparing ranges of raw
// memory.  We can switch to this other technique only if we know that the
// value representations of a type are unique, rely on all of the bits in their
// representation, and do not have strange values like `NaN` that self-compare
// as `false`.  This property is denoted by the `IsBitwiseEqualityComparable`
// trait.
//
// First, we create a simple `struct` that contains a `char` and a `short` as
// its two data members, and supported comparison with `operator==`.  Note that
// there will be a byte of padding between the `char` and the `short` members
// to ensure proper alignment.  We insert telemetry to count the number of
// times `operator==` is called:
// ```
// namespace BloombergLP {
//
// struct SimpleType {
//     // This 'struct' holds two data members with a byte of padding, and can
//     // be compared using the overloaded 'operator=='.
//
//     char  d_dataC;
//     short d_dataS;
//
//     static int s_comparisons;
//
//     friend bool operator==(const SimpleType& a, const SimpleType& b)
//         // Return 'true' if the specified 'a' has the same value as the
//         // specified 'b'.  Two 'SimpleType' objects have the same value if
//         // their corresponding 'd_dataC' and 'd_dataS' members have the
//         // same value.  The static data member 's_comparisons' is
//         // incremented by one each time this function is called.
//     {
//         ++s_comparisons;
//         return a.d_dataC == b.d_dataC
//             && a.d_dataS == b.d_dataS;
//     }
//
//     friend bool operator!=(const SimpleType& a, const SimpleType& b)
//         // Return 'true' if the specified 'a' does not have the same value
//         // as the specified 'b'.  Two 'SimpleType' objects do not have the
//         // same value if their corresponding 'd_dataC' and 'd_dataS'
//         // members do not have the same value.  The static data member
//         // 's_comparisons' is incremented by one each time this function is
//         // called.
//     {
//         ++s_comparisons;
//         return a.d_dataC != b.d_dataC
//             || a.d_dataS != b.d_dataS;
//     }
// };
//
// int SimpleType::s_comparisons = 0;
// ```
// Then, we create another `struct` that wraps a single `int` as its only data
// member, and supports comparison with `operator==`, inserting telemetry to
// count the number of times `operator==` is called:
// ```
// struct SecondType {
//     // This 'struct' holds a single 'int' member, 'd_data', and can be
//     // compared using the overloaded 'operator=='.
// ```
// We associate the bitwise EqualityComparable trait with `SecondType` using
// the BDE nested trait declaration facility:
// ```
//     BSLMF_NESTED_TRAIT_DECLARATION(SecondType,
//                                    bslmf::IsBitwiseEqualityComparable);
//
//     int d_data;
//
//     static int s_comparisons;
//
//     friend bool operator==(const SecondType& a, const SecondType& b)
//         // Return 'true' if the specified 'a' has the same value as the
//         // specified 'b'.  Two 'SecondType' objects have the same value if
//         // their corresponding 'd_data' elements have the same value.  The
//         // static data member 's_comparisons' is incremented by one each
//         // time this function is called.
//     {
//         ++s_comparisons;
//         return a.d_data == b.d_data;
//     }
//
//     friend bool operator!=(const SecondType& a, const SecondType& b)
//         // Return 'true' if the specified 'a' does not have the same value
//         // as the specified 'b'.  Two 'SecondType' objects do not have the
//         // same value if their corresponding 'd_data' elements do not have
//         // the same value.  The static data member 's_comparisons' is
//         // incremented by one each time this function is called.
//     {
//         ++s_comparisons;
//         return a.d_data != b.d_data;
//     }
// };
//
// int SecondType::s_comparisons = 0;
// ```
// Next, we create another `struct` that wraps a single `int` as its only data
// member, and supports comparison with `operator==`, inserting telemetry to
// count the number of times `operator==` is called:
// ```
// struct ThirdType {
//     // This 'struct' holds a single 'int' member, 'd_data', and can be
//     // compared using the overloaded 'operator=='.
//
//     int d_data;
//
//     static int s_comparisons;
//
//     friend bool operator==(const ThirdType& a, const ThirdType& b)
//         // Return 'true' if the specified 'a' has the same value as the
//         // specified 'b'.  Two 'SecondType' objects have the same value if
//         // their corresponding 'd_data' elements have the same value.  The
//         // static data member 's_comparisons' is incremented by one each
//         // time this function is called.
//     {
//         ++s_comparisons;
//         return a.d_data == b.d_data;
//     }
//
//     friend bool operator!=(const ThirdType& a, const ThirdType& b)
//         // Return 'true' if the specified 'a' does not have the same value
//         // as the specified 'b'.  Two 'ThirdType' objects do not have the
//         // same value if their corresponding 'd_data' elements do not have
//         // the same value.  The static data member 's_comparisons' is
//         // incremented by one each time this function is called.
//     {
//         ++s_comparisons;
//         return a.d_data != b.d_data;
//     }
// };
//
// int ThirdType::s_comparisons = 0;
// ```
// We associate the bitwise EqualityComparable trait with `ThirdType` by
// explicitly specializing the trait:
// ```
// namespace bslmf {
//     template <>
//     struct IsBitwiseEqualityComparable<ThirdType> : bsl::true_type {};
// }  // close namespace bslmf
// ```
// Now, we write a function template to compare two arrays of the same type:
// ```
// template <class t_TYPE>
// bool rangeCompare(const t_TYPE *start, size_t length, const t_TYPE *other)
// {
// ```
// If we detect the bitwise EqualityComparable trait, we rely on the optimized
// `memcmp` function:
// ```
//     if (bslmf::IsBitwiseEqualityComparable<t_TYPE>::value) {
//         return 0 == memcmp(start,
//                            other,
//                            length * sizeof(t_TYPE));              // RETURN
//     }
// ```
// Otherwise we iterate over the range directly until we find a pair of
// elements that do not have the same value, and return `true` if we reach the
// end of the range.
// ```
//     if (0 != length)  {
//         while (*start++ == *other++) {
//             if (!--length) {
//                 return true;                                      // RETURN
//             }
//         }
//     }
//     return false;
// }
// ```
// Finally, we write a test to confirm that two arrays containing different
// values do not compare as equal (using our array comparison function), and
// that an array compares equal to itself, as it does comprise elements all
// having the same value.  By inspecting the static data members provided for
// telemetry, we can confirm that `operator==` is called only for `SimpleType`
// as the other two `struct`s dispatch to `memcmp` instead:
// ```
// int usageExample1()
// {
// ```
// We confirm the initial state of the telemetry:
// ```
//     assert(0 == SimpleType::s_comparisons);
//     assert(0 == SecondType::s_comparisons);
//     assert(0 == ThirdType ::s_comparisons);
// ```
// Then we create zero-initialized arrays for each of the types to be tested,
// and a second array for each type with a set of values distinct from all
// zeroes:
// ```
//     const SimpleType simpleZeroes[10] = { };
//     const SecondType secondZeroes[10] = { };
//     const ThirdType  thirdZeroes [10] = { };
//
//     const SimpleType simpleValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//     const SecondType secondValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//     const ThirdType  thirdValues [10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// ```
// Next we confirm that the two arrays (of each type) do not compare equal, and
// inspect the telemetry to confirm that the comparison operator was called for
// only the `SimpleType` without the bitwise EqualityComparable trait:
// ```
//     assert(!rangeCompare(simpleZeroes, 10u, simpleValues) );
//     assert(!rangeCompare(secondZeroes, 10u, secondValues) );
//     assert(!rangeCompare(thirdZeroes,  10u, thirdValues)  );
//
//     assert(0 <  SimpleType::s_comparisons);
//     assert(0 == SecondType::s_comparisons);
//     assert(0 == ThirdType ::s_comparisons);
//
// ```
// Then we reset the telemetry and confirm that an array of each type compares
// equal to itself, and inspect the telemetry to confirm that the comparison
// operator was called for only the `SimpleType` without the bitwise
// EqualityComparable trait:
// ```
//     SimpleType::s_comparisons = 0;
//
//     assert( rangeCompare(simpleValues, 10u, simpleValues) );
//     assert( rangeCompare(secondValues, 10u, secondValues) );
//     assert( rangeCompare(thirdZeroes,  10u, thirdZeroes)  );
//
//     assert(0 <  SimpleType::s_comparisons);
//     assert(0 == SecondType::s_comparisons);
//     assert(0 == ThirdType ::s_comparisons);
//
//     return 0;
// }
//
// }  // close enterprise namespace
// ```
//
///Example 2: Associating a Trait with a Class Template
/// - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example, we associate a trait not with a class, but with a class
// *template*.  We create a "control" template that is not bitwise
// EqualityComparable, and two class templates, each of which uses a different
// mechanisms for being associated with the `IsBitwiseEqualityComparable`
// trait.  First, we define a class template that is not bitwise
// EqualityComparable, `NotComparable`:
// ```
// namespace BloombergLP {
//
// template <class t_TYPE>
// struct NotComparable
// {
//     t_TYPE d_value;
// };
// ```
// Then, we define the class template `PotentiallyComparable1`, which uses
// partial template specialization to associate the
// `IsBitwiseEqualityComparable` trait with each instantiation on a `t_TYPE`
// that is itself bitwise EqualityComparable:
// ```
// template <class t_TYPE>
// struct PotentiallyComparable1
// {
//     t_TYPE d_value;
// };
//
// namespace bslmf {
//     template <class t_TYPE>
//     struct IsBitwiseEqualityComparable<PotentiallyComparable1<t_TYPE> >
//         : IsBitwiseEqualityComparable<t_TYPE>::type {
//     };
// }  // close namespace bslmf
// ```
// Next, we define the class template'PotentiallyComparable2', which uses the
// `BSLMF_NESTED_TRAIT_DECLARATION` macro to associate the
// `IsBitwiseEqualityComparable` trait with each instantiation on a `t_TYPE`
// that is itself bitwise EqualityComparable:
// ```
// template <class t_TYPE>
// struct PotentiallyComparable2
// {
//     t_TYPE d_value;
//
//     BSLMF_NESTED_TRAIT_DECLARATION_IF(
//                         PotentiallyComparable2,
//                         bslmf::IsBitwiseEqualityComparable,
//                         bslmf::IsBitwiseEqualityComparable<t_TYPE>::value);
// };
// ```
// Finally, we check that the traits are correctly associated by instantiating
// each template with types that are bitwise EqualityComparable and with types
// that are not not bitwise EqualityComparable, verifying the value of
// `IsBitwiseEqualityComparable<T>::value` in each case:
// ```
// int usageExample2()
// {
//     using namespace bslmf;
//
//     assert(!IsBitwiseEqualityComparable<NotComparable<int> >::value);
//     assert(!IsBitwiseEqualityComparable<
//                           NotComparable<NotComparable<int> > >::value);
//
//     assert( IsBitwiseEqualityComparable<
//                                PotentiallyComparable1<int> >::value);
//     assert(!IsBitwiseEqualityComparable<
//                  PotentiallyComparable1<NotComparable<int> > >::value);
//
//     assert( IsBitwiseEqualityComparable<
//                                PotentiallyComparable2<int> >::value);
//     assert(!IsBitwiseEqualityComparable<
//                  PotentiallyComparable2<NotComparable<int> > >::value);
//
//     return 0;
// }
//
// }  // close enterprise namespace
// ```

#include <bslscm_version.h>

#include <bslmf_detectnestedtrait.h>
#include <bslmf_integralconstant.h>
#include <bslmf_isconst.h>
#include <bslmf_voidtype.h>

#include <bsls_platform.h>

#include <stddef.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslmf_isenum.h>
#endif


namespace BloombergLP {
namespace bslmf {

template <class t_TYPE>
struct IsBitwiseEqualityComparable;

#if defined(BSLS_PLATFORM_CMP_IBM)
                     // =========================================
                     // struct IsBitwiseEqualityComparable_Scalar
                     // =========================================

template <class t_TYPE, class = void>
struct IsBitwiseEqualityComparable_Imp2 : bsl::false_type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable_Imp2<t_TYPE, BSLMF_VOIDTYPE(t_TYPE[])>
: bsl::true_type {
    // This implementation-detail trait determines whether 't_TYPE' is a scalar
    // type (an arithmetic type, enumeration, pointer, or pointer-to-member).
    // This implementation takes advantage of a previous layer of filtering
    // handling all class-types, so any remaining types that are valid as array
    // elements must be scalar types, i.e., the 'BSLMF_VOIDTYPE' test will
    // filter function types and reference types.
};

                     // ======================================
                     // struct IsBitwiseEqualityComparable_Imp
                     // ======================================

template <class t_TYPE, class = void>
struct IsBitwiseEqualityComparable_Imp
: IsBitwiseEqualityComparable_Imp2<t_TYPE>::type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable_Imp<t_TYPE, BSLMF_VOIDTYPE(int t_TYPE::*)>
: DetectNestedTrait<t_TYPE, IsBitwiseEqualityComparable>::type {
    // This trait 'struct' derives from 'bsl::true_type' if (the template
    // paramter) 't_TYPE' is a scalar type or a class with a nested trait
    // declaration for the 'bslmf::IsBitwiseEqualityComparable' trait, and from
    // 'bsl::false_type' otherwise.  Note that this template handles only the
    // class-type cases, delegating the final filtering for scalar types to a
    // further trait template.
};
#else
                     // ======================================
                     // struct IsBitwiseEqualityComparable_Imp
                     // ======================================

/// This trait `struct` derives from `bsl::true_type` if (the template
/// paramter) `t_TYPE` is a scalar type or a class with a nested trait
/// declaration for the `bslmf::IsBitwiseEqualityComparable` trait, and from
/// `bsl::false_type` otherwise.  Note that this implementation relies on
/// the fact that reference and function types cannot be cv-qualified, and
/// any other non-class type (that is not handled by specializations of the
/// primary template) will be a scalar type.
template <class t_TYPE, class = void>
struct IsBitwiseEqualityComparable_Imp : bsl::is_const<const t_TYPE>::type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable_Imp<t_TYPE, BSLMF_VOIDTYPE(int t_TYPE::*)>
: DetectNestedTrait<t_TYPE, IsBitwiseEqualityComparable>::type {
};
#endif

                     // ==================================
                     // struct IsBitwiseEqualityComparable
                     // ==================================

/// This trait `struct` is a metafunction that determines whether the
/// specified parameter `t_TYPE` is bitwise EqualityComparable.  If
/// `IsBitwiseEqualityComparable<t_TYPE>` is derived from `true_type` then
/// `t_TYPE` is bitwise EqualityComparable.  Otherwise, bitwise equality
/// comparability cannot be inferred for `t_TYPE`.  This trait can be
/// associated with a bitwise EqualityComparable user-defined class by
/// specializing this class or by using the `BSLMF_NESTED_TRAIT_DECLARATION`
/// macro.
template <class t_TYPE>
struct IsBitwiseEqualityComparable
: IsBitwiseEqualityComparable_Imp<t_TYPE>::type {
};

template <class t_TYPE>
struct IsBitwiseEqualityComparable<const t_TYPE>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable<volatile t_TYPE>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable<const volatile t_TYPE>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
    // Partial specializations for cv-qualified types channel to a single
    // instantiation of the implementation type.  Note that we cannot derive
    // through the 'Imp' type directly as we would not correctly handle
    // cv-qualified types that have been explicitly specialized by our users.

template <class t_TYPE, size_t t_LEN>
struct IsBitwiseEqualityComparable<t_TYPE[t_LEN]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE, size_t t_LEN>
struct IsBitwiseEqualityComparable<const t_TYPE[t_LEN]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE, size_t t_LEN>
struct IsBitwiseEqualityComparable<volatile t_TYPE[t_LEN]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE, size_t t_LEN>
struct IsBitwiseEqualityComparable<const volatile t_TYPE[t_LEN]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};

template <class t_TYPE>
struct IsBitwiseEqualityComparable<t_TYPE[]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable<const t_TYPE[]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable<volatile t_TYPE[]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
template <class t_TYPE>
struct IsBitwiseEqualityComparable<const volatile t_TYPE[]>
: IsBitwiseEqualityComparable<t_TYPE>::type {
};
    // Partial specializations for array types, as arrays have a contiguity
    // guarantee (that can inferred from pointer arithmetic rules) so this if a
    // type is bitwise equality comparable, we can be sure there is no padding
    // introduced by an array, so arrays of this type should have the same
    // property.

/// Explicit specialization to confirm that `void` types are never bitwise
/// EqualityComparable.  Note that cv-`void` types are covered by the
/// partial specialization for any cv-qualified type.
template <>
struct IsBitwiseEqualityComparable<void> : bsl::false_type {};

/// Revert of {DRQS 143286899}. Once clients are fixed, change to false.
/// Explicit specialization to confirm that floating point types are not
/// bitwise EqualityComparable, as they typically have specific problematic
/// values: NaNs do not compare equal with themselves, and there may be
/// multiple representations for zero (with negative zero).
template <>
struct IsBitwiseEqualityComparable<float> : bsl::true_type {};
template <>
struct IsBitwiseEqualityComparable<double> : bsl::true_type {};
template <>
struct IsBitwiseEqualityComparable<long double> : bsl::true_type {};

}  // close package namespace
}  // close enterprise namespace

#endif // ! defined(INCLUDED_BSLMF_ISBITWISEEQUALITYCOMPARABLE)

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