// bslh_hashtuple.h                                                   -*-C++-*-
#ifndef INCLUDED_BSLH_HASHTUPLE
#define INCLUDED_BSLH_HASHTUPLE

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

//@PURPOSE: Provide `hashAppend` for `std::tuple`.
//
//@DESCRIPTION: This component provides a free function template,
// `bslh::hashAppend`, overloaded for the `std::tuple` class template.
// Including this function allows for `std::tuple` types (and types that
// contain them) to be used as keys in BDE hashed containers.
//
// Note that use of this component requires that the language standard be 2011
// or later, as that is when `std::tuple` first appears.
//
///Usage
///-----
// This section illustrates intended usage of this component.
//
///Example 1: Hashing a Tuple of Integer Values
/// - - - - - - - - - - - - - - - - - - - - - -
// Suppose one must compute has that combines the hashes of several integer
// values, each of a different type:
// ```
// char  c = 'a';
// short s = static_cast<short>(1);
// int   i = 2;
// long  l = 3L;
// ```
// First, we can make that calculation by repeated invocations of a
// 'bslh::DefaultHashAlogorithm object:
// ```
// bslh::DefaultHashAlgorithm hasherS;
// hasherS(&c, sizeof(char));
// hasherS(&s, sizeof(short));
// hasherS(&i, sizeof(int));
// hasherS(&l, sizeof(long));
//
// bslh::DefaultHashAlgorithm::result_type hashS = hasherS.computeHash();
// ```
// Now, the same calculation can be expressed more concisely if those same
// values are contained in a single `std::tuple` object.
// ```
// std::tuple<char, short, int, long> t = std::make_tuple(c, s, i, l);
//
// bslh::DefaultHashAlgorithm hasherT;
// bslh::hashAppend(hasherT, t);
//
// bslh::DefaultHashAlgorithm::result_type hashT = hasherT.computeHash();
// ```
// Finally, we confirm that we computed the same result.
// ```
// assert(hashS == hashT);
// ```

#include <bslscm_version.h>

#include <bsls_libraryfeatures.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE

#include <bslh_hash.h>

#include <type_traits> // 'std::remove_reference'
#include <tuple>       // 'std::tuple'
#include <cstddef>     // 'std::size_t'

namespace BloombergLP {
namespace bslh {

                        // =====================
                        // struct hashTuple_Util
                        // =====================

template <std::size_t BACK_INDEX, class HASH_ALGORITHM, class TUPLE_TYPE>
struct hashTuple_Util
{
    // CLASS METHODS

    /// Invoke the (appropriate) `hashAppend` function using the specified
    /// `algorithm` and the "current" element of the specified `input` tuple
    /// and then visit the next element.  The index of the current element
    /// is the size of (template parameter) `TUPLE_TYPE` minus (template
    /// parameter) `BACK_INDEX`.  `BACK_INDEX` is reduced on each recursion
    /// and eventually, when `BACK_INDEX` is 0, a "do nothing" function is
    /// called to stop the recursion.
    static void visit(HASH_ALGORITHM& algorithm, const TUPLE_TYPE& input);
};

            // The partial specialization that stops the recursion.

template <class HASH_ALGORITHM, class TUPLE_TYPE>
struct hashTuple_Util<0, HASH_ALGORITHM, TUPLE_TYPE>
{
    // CLASS METHODS

    /// Do nothing -- ignore the specified `algorithm`, ignore the specified
    /// `input`, do not try to visit the "next" element -- and return.  Note
    /// that this function is called only after the last element of `input`
    /// has been visited.
    static void visit(HASH_ALGORITHM& algorithm, const TUPLE_TYPE& input);
};

// FREE FUNCTIONS

/// Invoke `hashAppend`, with the specified `algorithm`, on each element of
/// the specified `input` in order of increasing index as defined by
/// `std::get`.
template <class HASH_ALGORITHM, class ... TYPE>
void hashAppend(HASH_ALGORITHM& algorithm, const std::tuple<TYPE ...> &input);

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

                        // ---------------------
                        // struct hashTuple_Util
                        // ---------------------

template <std::size_t BACK_INDEX, class HASH_ALGORITHM, class TUPLE_TYPE>
inline
void hashTuple_Util<BACK_INDEX, HASH_ALGORITHM, TUPLE_TYPE>::visit(
                                                   HASH_ALGORITHM&   algorithm,
                                                   const TUPLE_TYPE& input)
{
    // Hash current element.

    const std::size_t tupleSize = std::tuple_size<TUPLE_TYPE>::value;
    const std::size_t index      = tupleSize - BACK_INDEX;

    hashAppend(algorithm, std::get<index>(input));

    // Visit next element.

    const std::size_t nextBackIndex = BACK_INDEX - 1;

    hashTuple_Util<nextBackIndex, HASH_ALGORITHM, TUPLE_TYPE>::visit(algorithm,
                                                                     input);
}

template <class HASH_ALGORITHM, class TUPLE_TYPE>
inline
void hashTuple_Util<0, HASH_ALGORITHM, TUPLE_TYPE>::visit(HASH_ALGORITHM&   ,
                                                          const TUPLE_TYPE& )
{
}

// FREE FUNCTIONS
template <class HASH_ALGORITHM, class ... TYPE>
inline
void
hashAppend(HASH_ALGORITHM& algorithm, const std::tuple<TYPE ...> &input)
{
    using TupleType = typename std::remove_reference<decltype(input)>::type;

    const std::size_t tupleSize = std::tuple_size<TupleType>::value;

    if (0 == tupleSize) {
        return;                                                       // RETURN
    }

    hashTuple_Util<tupleSize, HASH_ALGORITHM, TupleType>::visit(algorithm,
                                                                input);
}

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

#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE
#endif

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