// -*- C++ -*-
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
// -----------------------------------------------------------------------------------------------------

/*!\file
 * \brief The [\<memory\> header](https://en.cppreference.com/w/cpp/header/memory) from C++20's standard library.
 * \author Svenja Mehringer <svenja.mehringer AT fu-berlin.de>
 */

#pragma once

#if __has_include(<version>)
#include <version> // from C++20 all feature macros should be defined here
#endif

/*!\defgroup std_memory memory
 * \ingroup std
 * \brief The [\<memory\> header](https://en.cppreference.com/w/cpp/header/memory) from C++20's standard library.
 */

/*!\brief A workaround for __cpp_lib_to_address for gcc version >=8.0 and < 9.4 (in C++20 mode).
 *        Those versions implemented std::to_address, but did not define that feature detection macro.
 * \ingroup std_memory
 */
#ifdef SEQAN3_DOXYGEN_ONLY // needed as seqan3/core/platform.hpp might not be included
#if SEQAN3_DOXYGEN_ONLY(1)0
// documentation-only
#define SEQAN3_CPP_LIB_TO_ADDRESS IMPLEMENTATION_DEFINED
#undef SEQAN3_CPP_LIB_TO_ADDRESS
#endif // SEQAN3_DOXYGEN_ONLY(1)0
#endif // SEQAN3_DOXYGEN_ONLY

#ifndef SEQAN3_CPP_LIB_TO_ADDRESS
#   if defined(__cpp_lib_to_address)
#       define SEQAN3_CPP_LIB_TO_ADDRESS 1
#   elif defined(__GNUC__) && (__GNUC__ == 8 || __GNUC__ == 9) && __cplusplus > 201703L
#       define SEQAN3_CPP_LIB_TO_ADDRESS 1
#   endif
#endif

#include <memory>

#ifndef SEQAN3_CPP_LIB_TO_ADDRESS

namespace std
{

/*!\brief Obtain the address represented by p without forming a reference to the object pointed to by p.
 * \sa https://en.cppreference.com/w/cpp/memory/to_address
 * \ingroup std_memory
 */
template <typename T>
constexpr T * to_address(T * p) noexcept
{
    static_assert(!std::is_function_v<T>);
    return p;
}

/*!\brief Obtain the address represented by p without forming a reference to the object pointed to by p.
 * \sa https://en.cppreference.com/w/cpp/memory/to_address
 * \ingroup std_memory
 */
template <typename T>
constexpr auto to_address(const T & p) noexcept
{
    if constexpr (requires { std::pointer_traits<T>::to_address(p); })
        return std::pointer_traits<T>::to_address(p);
    else
        return std::to_address(p.operator->());
}

} // namespace std

#endif // SEQAN3_CPP_LIB_TO_ADDRESS
