[libc-commits] [libc] [llvm] [libc] Add simple 'tuple' type to CPP helpers (PR #157739)

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Tue Sep 9 14:51:41 PDT 2025


https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/157739

>From 8b820ad300021b9592b80e4c74f8b10eb57913f5 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 9 Sep 2025 14:54:51 -0500
Subject: [PATCH 1/3] [libc] Add simple 'tuple' type to CPP helpers

Summary:
This patch adds support for `cpp::tuple` with basic support for creating
and modifing tuples.
---
 libc/src/__support/CPP/CMakeLists.txt         |   8 +
 libc/src/__support/CPP/tuple.h                | 144 ++++++++++++++++++
 .../__support/CPP/utility/integer_sequence.h  |  10 ++
 libc/test/src/__support/CPP/CMakeLists.txt    |  10 ++
 libc/test/src/__support/CPP/tuple_test.cpp    |  71 +++++++++
 5 files changed, 243 insertions(+)
 create mode 100644 libc/src/__support/CPP/tuple.h
 create mode 100644 libc/test/src/__support/CPP/tuple_test.cpp

diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index a389a6d1702fe..13b2ef920efb8 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -211,6 +211,14 @@ add_object_library(
     libc.src.__support.macros.properties.os
 )
 
+add_header_library(
+  tuple
+  HDRS
+    tuple.h
+  DEPENDS
+    .utility
+)
+
 add_header_library(
   simd
   HDRS
diff --git a/libc/src/__support/CPP/tuple.h b/libc/src/__support/CPP/tuple.h
new file mode 100644
index 0000000000000..5c928cdcd693c
--- /dev/null
+++ b/libc/src/__support/CPP/tuple.h
@@ -0,0 +1,144 @@
+//===-- tuple utility -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_TUPLE_H
+#define LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_TUPLE_H
+
+#include "src/__support/CPP/type_traits/decay.h"
+#include "src/__support/CPP/utility/integer_sequence.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace cpp {
+
+template <typename... Ts> struct tuple;
+template <> struct tuple<> {};
+
+template <typename Head, typename... Tail>
+struct tuple<Head, Tail...> : tuple<Tail...> {
+  Head head;
+
+  LIBC_INLINE constexpr tuple() = default;
+
+  template <typename OHead, typename... OTail>
+  LIBC_INLINE constexpr tuple &operator=(const tuple<OHead, OTail...> &other) {
+    head = other.get_head();
+    this->get_tail() = other.get_tail();
+    return *this;
+  }
+
+  LIBC_INLINE constexpr tuple(const Head &h, const Tail &...t)
+      : tuple<Tail...>(t...), head(h) {}
+
+  LIBC_INLINE constexpr Head &get_head() { return head; }
+  LIBC_INLINE constexpr const Head &get_head() const { return head; }
+
+  LIBC_INLINE constexpr tuple<Tail...> &get_tail() { return *this; }
+  LIBC_INLINE constexpr const tuple<Tail...> &get_tail() const { return *this; }
+};
+
+template <typename... Ts> LIBC_INLINE constexpr auto make_tuple(Ts &&...args) {
+  return tuple<cpp::decay_t<Ts>...>(static_cast<Ts &&>(args)...);
+}
+template <typename... Ts> LIBC_INLINE constexpr auto tie(Ts &...args) {
+  return tuple<Ts &...>(args...);
+}
+
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr auto &get(tuple<Head, Tail...> &t) {
+  if constexpr (I == 0)
+    return t.get_head();
+  else
+    return get<I - 1>(t.get_tail());
+}
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr const auto &get(const tuple<Head, Tail...> &t) {
+  if constexpr (I == 0)
+    return t.get_head();
+  else
+    return get<I - 1>(t.get_tail());
+}
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr auto &&get(tuple<Head, Tail...> &&t) {
+  if constexpr (I == 0)
+    return static_cast<Head &&>(t.get_head());
+  else
+    return get<I - 1>(static_cast<tuple<Tail...> &&>(t.get_tail()));
+}
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr const auto &&get(const tuple<Head, Tail...> &&t) {
+  if constexpr (I == 0)
+    return static_cast<const Head &&>(t.get_head());
+  else
+    return get<I - 1>(static_cast<const tuple<Tail...> &&>(t.get_tail()));
+}
+
+template <typename T> struct tuple_size;
+template <typename... Ts> struct tuple_size<tuple<Ts...>> {
+  static constexpr size_t value = sizeof...(Ts);
+};
+
+template <size_t I, typename T> struct tuple_element;
+template <size_t I, typename Head, typename... Tail>
+struct tuple_element<I, tuple<Head, Tail...>>
+    : tuple_element<I - 1, tuple<Tail...>> {};
+template <typename Head, typename... Tail>
+struct tuple_element<0, tuple<Head, Tail...>> {
+  using type = cpp::remove_cv_t<cpp::remove_reference_t<Head>>;
+};
+
+namespace internal {
+template <typename... As, typename... Bs, size_t... I, size_t... J>
+LIBC_INLINE constexpr auto
+tuple_cat(const tuple<As...> &a, const tuple<Bs...> &b,
+          cpp::index_sequence<I...>, cpp::index_sequence<J...>) {
+  return tuple<As..., Bs...>(get<I>(a)..., get<J>(b)...);
+}
+
+template <typename First, typename Second, typename... Rest>
+LIBC_INLINE constexpr auto tuple_cat(const First &f, const Second &s,
+                                     const Rest &...rest) {
+  auto concat =
+      tuple_cat(f, s, cpp::make_index_sequence<tuple_size<First>::value>{},
+                cpp::make_index_sequence<tuple_size<Second>::value>{});
+  if constexpr (sizeof...(Rest))
+    return tuple_cat(concat, rest...);
+  else
+    return concat;
+}
+} // namespace internal
+
+template <typename... Tuples>
+LIBC_INLINE constexpr auto tuple_cat(const Tuples &...tuples) {
+  static_assert(sizeof...(Tuples) > 0, "need at least one element");
+  if constexpr (sizeof...(Tuples) == 1)
+    return (tuples, ...);
+  else
+    return internal::tuple_cat(tuples...);
+}
+
+} // namespace cpp
+} // namespace LIBC_NAMESPACE_DECL
+
+// For structured binding support.
+namespace std {
+
+template <class T> struct tuple_size;
+template <size_t I, class T> struct tuple_element;
+
+template <typename... Ts>
+struct tuple_size<LIBC_NAMESPACE::cpp::tuple<Ts...>>
+    : LIBC_NAMESPACE::cpp::tuple_size<LIBC_NAMESPACE::cpp::tuple<Ts...>> {};
+
+template <size_t I, typename... Ts>
+struct tuple_element<I, LIBC_NAMESPACE::cpp::tuple<Ts...>>
+    : LIBC_NAMESPACE::cpp::tuple_element<I, LIBC_NAMESPACE::cpp::tuple<Ts...>> {
+};
+
+} // namespace std
+
+#endif // LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_TUPLE_H
diff --git a/libc/src/__support/CPP/utility/integer_sequence.h b/libc/src/__support/CPP/utility/integer_sequence.h
index 06643d505aca0..17c3dbfd229cc 100644
--- a/libc/src/__support/CPP/utility/integer_sequence.h
+++ b/libc/src/__support/CPP/utility/integer_sequence.h
@@ -5,12 +5,15 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+
 #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_INTEGER_SEQUENCE_H
 #define LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_INTEGER_SEQUENCE_H
 
 #include "src/__support/CPP/type_traits/is_integral.h"
 #include "src/__support/macros/config.h"
 
+#include <stddef.h>
+
 namespace LIBC_NAMESPACE_DECL {
 namespace cpp {
 
@@ -34,6 +37,13 @@ template <typename T, int N>
 using make_integer_sequence =
     typename detail::make_integer_sequence<T, N - 1>::type;
 
+// index sequence
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+template <int N>
+using make_index_sequence =
+    typename detail::make_integer_sequence<size_t, N - 1>::type;
+
 } // namespace cpp
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt
index 3e1379d812c37..702f788febfe8 100644
--- a/libc/test/src/__support/CPP/CMakeLists.txt
+++ b/libc/test/src/__support/CPP/CMakeLists.txt
@@ -130,6 +130,16 @@ add_libc_test(
     libc.src.__support.CPP.optional
 )
 
+add_libc_test(
+  tuple_test
+  SUITE
+    libc-cpp-utils-tests
+  SRCS
+    tuple_test.cpp
+  DEPENDS
+    libc.src.__support.CPP.tuple
+)
+
 add_libc_test(
   span_test
   SUITE
diff --git a/libc/test/src/__support/CPP/tuple_test.cpp b/libc/test/src/__support/CPP/tuple_test.cpp
new file mode 100644
index 0000000000000..0b05ae4d97103
--- /dev/null
+++ b/libc/test/src/__support/CPP/tuple_test.cpp
@@ -0,0 +1,71 @@
+//===-- Unittests for cpp::tuple ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/tuple.h"
+#include <stddef.h>
+
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::cpp;
+
+TEST(LlvmLibcTupleTest, Construction) {
+  tuple<int, double> t(42, 3.14);
+  EXPECT_EQ(get<0>(t), 42);
+  EXPECT_FP_EQ(get<1>(t), 3.14);
+}
+
+TEST(LlvmLibcTupleTest, MakeTuple) {
+  auto t = make_tuple(1, 2.5, 'x');
+  EXPECT_EQ(get<0>(t), 1);
+  EXPECT_FP_EQ(get<1>(t), 2.5);
+  EXPECT_EQ(get<2>(t), 'x');
+}
+
+TEST(LlvmLibcTupleTest, TieAssignment) {
+  int a = 0;
+  double b = 0;
+  char c = 0;
+  auto t = make_tuple(7, 8.5, 'y');
+  tie(a, b, c) = t;
+  EXPECT_EQ(a, 7);
+  EXPECT_FP_EQ(b, 8.5);
+  EXPECT_EQ(c, 'y');
+}
+
+TEST(LlvmLibcTupleTest, StructuredBindings) {
+  auto t = make_tuple(7, 8.5, 'y');
+  auto [x, y, z] = t;
+  EXPECT_EQ(x, 7);
+  EXPECT_FP_EQ(y, 8.5);
+  EXPECT_EQ(z, 'y');
+}
+
+TEST(LlvmLibcTupleTest, TupleCat) {
+  tuple<int, double> t(42, 3.14);
+  auto t1 = make_tuple(1, 2.5, 'x');
+  auto t2 = tuple_cat(t, t1);
+  EXPECT_EQ(get<0>(t2), 42);
+  EXPECT_FP_EQ(get<1>(t2), 3.14);
+  EXPECT_EQ(get<2>(t2), 1);
+  EXPECT_FP_EQ(get<3>(t2), 2.5);
+  EXPECT_EQ(get<4>(t2), 'x');
+}
+
+TEST(LlvmLibcTupleTest, ConstTuple) {
+  const auto t = make_tuple(100, 200.5);
+  EXPECT_EQ(get<0>(t), 100);
+  EXPECT_FP_EQ(get<1>(t), 200.5);
+}
+
+TEST(LlvmLibcTupleTest, RvalueAssignment) {
+  auto t = make_tuple(0, 0.0);
+  t = make_tuple(9, 9.5);
+  EXPECT_EQ(get<0>(t), 9);
+  EXPECT_FP_EQ(get<1>(t), 9.5);
+}

>From df1ab2adc0b86325b2db3e07991647ab02a3f9e3 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 9 Sep 2025 16:12:57 -0500
Subject: [PATCH 2/3] bazel

---
 utils/bazel/llvm-project-overlay/libc/BUILD.bazel | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index bb76da4153108..46d416357c6cd 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -659,6 +659,14 @@ libc_support_library(
     ],
 )
 
+libc_support_library(
+    name = "__support_cpp_tuple",
+    hdrs = ["src/__support/CPP/tuple.h"],
+    deps = [
+        "__support_cpp_utility",
+    ],
+)
+
 libc_support_library(
     name = "__support_cpp_limits",
     hdrs = ["src/__support/CPP/limits.h"],

>From e0234fea3527841fde18a4b1dab2f12d3d9807af Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 9 Sep 2025 16:51:31 -0500
Subject: [PATCH 3/3] comment

---
 libc/src/__support/CPP/tuple.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/src/__support/CPP/tuple.h b/libc/src/__support/CPP/tuple.h
index 5c928cdcd693c..cce8e0ef2bfae 100644
--- a/libc/src/__support/CPP/tuple.h
+++ b/libc/src/__support/CPP/tuple.h
@@ -124,7 +124,7 @@ LIBC_INLINE constexpr auto tuple_cat(const Tuples &...tuples) {
 } // namespace cpp
 } // namespace LIBC_NAMESPACE_DECL
 
-// For structured binding support.
+// Standard namespace definitions required for structured binding support.
 namespace std {
 
 template <class T> struct tuple_size;



More information about the libc-commits mailing list