[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