Program Listing for File tuple_traits.hpp

Return to documentation for file (include/beluga/type_traits/tuple_traits.hpp)

// Copyright 2023-2024 Ekumen, Inc.
//
// 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.

#ifndef BELUGA_TYPE_TRAITS_TUPLE_TRAITS_HPP
#define BELUGA_TYPE_TRAITS_TUPLE_TRAITS_HPP

#include <tuple>
#include <type_traits>
#include <utility>

namespace beluga {

namespace detail {


template <typename, typename = void>
struct is_complete : std::false_type {};

template <typename T>
struct is_complete<T, std::void_t<decltype(sizeof(T))>> : std::true_type {};


}  // namespace detail


template <typename T>
struct is_tuple_like : detail::is_complete<std::tuple_size<std::decay_t<T>>> {};

template <class T>
inline constexpr bool is_tuple_like_v = is_tuple_like<T>::value;


template <
    typename T,
    typename U,
    typename Is = std::enable_if_t<
        std::tuple_size_v<std::decay_t<T>> == std::tuple_size_v<std::decay_t<U>>,
        std::make_index_sequence<std::tuple_size_v<std::decay_t<T>>>>>
struct common_tuple_type;

template <typename T, typename U, std::size_t... I>
struct common_tuple_type<T, U, std::index_sequence<I...>> {
  using type = std::tuple<
      std::common_type_t<std::tuple_element_t<I, std::decay_t<T>>, std::tuple_element_t<I, std::decay_t<U>>>...>;
};

template <typename T, typename U>
using common_tuple_type_t = typename common_tuple_type<T, U>::type;

template <typename, typename, typename = void>
struct has_common_tuple_type : std::false_type {};

template <typename T, typename U>
struct has_common_tuple_type<T, U, std::void_t<common_tuple_type_t<T, U>>> : std::true_type {};

template <typename T, typename U>
inline constexpr bool has_common_tuple_type_v = has_common_tuple_type<T, U>::value;

namespace detail {

static constexpr std::size_t kTupleIndexNotFound = static_cast<std::size_t>(-1);

static constexpr std::size_t kTupleIndexAmbiguous = static_cast<std::size_t>(-2);


template <class T, class... Args>
constexpr std::size_t tuple_index_helper() noexcept {
  constexpr bool kMatches[sizeof...(Args)] =  // NOLINT(modernize-avoid-c-arrays)
      {std::is_same_v<T, std::decay_t<Args>>...};
  std::size_t selected = kTupleIndexNotFound;
  for (std::size_t i = 0; i < sizeof...(Args); ++i) {
    if (kMatches[i]) {
      if (selected == kTupleIndexNotFound) {
        selected = i;
      } else {
        return kTupleIndexAmbiguous;
      }
    }
  }
  return selected;
}

template <class T, class... Args>
constexpr bool tuple_index_found() noexcept {
  constexpr std::size_t kIndex = tuple_index_helper<T, Args...>();
  return kIndex != kTupleIndexNotFound && kIndex != kTupleIndexAmbiguous;
}

}  // namespace detail

template <class T, class TupleLike, class = void>
struct tuple_index;

template <class T, template <class...> class TupleLike, class... Args>
struct tuple_index<
    T,
    TupleLike<Args...>,
    std::enable_if_t<is_tuple_like_v<std::decay_t<TupleLike<Args...>>> && detail::tuple_index_found<T, Args...>()>>
    : std::integral_constant<std::size_t, detail::tuple_index_helper<T, Args...>()> {};

template <class T, class TupleLike>
inline constexpr std::size_t tuple_index_v = tuple_index<T, TupleLike>::value;

template <class T, class TupleLike>
using tuple_index_t = typename tuple_index<T, TupleLike>::type;

template <class T, class TupleLike, class = void>
struct has_single_element : std::false_type {};

template <class T, template <class...> class TupleLike, class... Args>
struct has_single_element<
    T,
    TupleLike<Args...>,
    std::enable_if_t<is_tuple_like_v<std::decay_t<TupleLike<Args...>>> && detail::tuple_index_found<T, Args...>()>>
    : std::true_type {};

template <class T, class TupleLike>
inline constexpr bool has_single_element_v = has_single_element<T, TupleLike>::value;

template <class T, class TupleLike>
constexpr decltype(auto) element(TupleLike&& tuple) noexcept {
  constexpr std::size_t kIndex = tuple_index_v<T, std::decay_t<TupleLike>>;
  return std::get<kIndex>(std::forward<TupleLike>(tuple));
}

template <class T, class = void>
struct decay_tuple_like;

template <template <class...> class TupleLike, class... Args>
struct decay_tuple_like<TupleLike<Args...>, std::enable_if_t<is_tuple_like_v<std::decay_t<TupleLike<Args...>>>>> {
  using type = std::decay_t<TupleLike<std::decay_t<Args>...>>;
};

template <class T>
using decay_tuple_like_t = typename decay_tuple_like<T>::type;

}  // namespace beluga

#endif