mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-28 21:54:40 +03:00
AK: Add a Tuple implementation
Please don't use this outside of metaprogramming needs, *please*.
This commit is contained in:
parent
4fdbac236d
commit
02de813950
Notes:
sideshowbarker
2024-07-18 18:20:26 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/02de813950f Pull-request: https://github.com/SerenityOS/serenity/pull/6978 Reviewed-by: https://github.com/Quaker762 ✅ Reviewed-by: https://github.com/sunverwerth ✅
218
AK/Tuple.h
Normal file
218
AK/Tuple.h
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/TypeList.h>
|
||||
|
||||
namespace AK::Detail {
|
||||
|
||||
template<typename... Ts>
|
||||
struct Tuple {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Tuple<T> {
|
||||
Tuple(T&& value) requires(!IsSame<T&&, const T&>)
|
||||
: value(forward<T>(value))
|
||||
{
|
||||
}
|
||||
|
||||
Tuple(const T& value)
|
||||
: value(value)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U& get()
|
||||
{
|
||||
static_assert(IsSame<T, U>, "Invalid tuple access");
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
const U& get() const
|
||||
{
|
||||
return const_cast<Tuple<T>&>(*this).get<U>();
|
||||
}
|
||||
|
||||
template<typename U, unsigned index>
|
||||
U& get_with_index()
|
||||
{
|
||||
static_assert(IsSame<T, U> && index == 0, "Invalid tuple access");
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename U, unsigned index>
|
||||
const U& get_with_index() const
|
||||
{
|
||||
return const_cast<Tuple<T>&>(*this).get_with_index<U, index>();
|
||||
}
|
||||
|
||||
private:
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename T, typename... TRest>
|
||||
struct Tuple<T, TRest...> : Tuple<TRest...> {
|
||||
Tuple(T&& first, TRest&&... rest)
|
||||
: Tuple<TRest...>(forward<TRest>(rest)...)
|
||||
, value(forward<T>(first))
|
||||
{
|
||||
}
|
||||
|
||||
Tuple(const T& first, const TRest&... rest)
|
||||
: Tuple<TRest...>(rest...)
|
||||
, value(first)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U& get()
|
||||
{
|
||||
if constexpr (IsSame<T, U>)
|
||||
return value;
|
||||
else
|
||||
return Tuple<TRest...>::template get<U>();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
const U& get() const
|
||||
{
|
||||
return const_cast<Tuple<T, TRest...>&>(*this).get<U>();
|
||||
}
|
||||
|
||||
template<typename U, unsigned index>
|
||||
U& get_with_index()
|
||||
{
|
||||
if constexpr (IsSame<T, U> && index == 0)
|
||||
return value;
|
||||
else
|
||||
return Tuple<TRest...>::template get_with_index<U, index - 1>();
|
||||
}
|
||||
|
||||
template<typename U, unsigned index>
|
||||
const U& get_with_index() const
|
||||
{
|
||||
return const_cast<Tuple<T, TRest...>&>(*this).get_with_index<U, index>();
|
||||
}
|
||||
|
||||
private:
|
||||
T value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename... Ts>
|
||||
struct Tuple : Detail::Tuple<Ts...> {
|
||||
using Types = TypeList<Ts...>;
|
||||
using Detail::Tuple<Ts...>::Tuple;
|
||||
using Indices = MakeIndexSequence<sizeof...(Ts)>;
|
||||
|
||||
Tuple(Tuple&& other)
|
||||
: Tuple(move(other), Indices())
|
||||
{
|
||||
}
|
||||
|
||||
Tuple(const Tuple& other)
|
||||
: Tuple(other, Indices())
|
||||
{
|
||||
}
|
||||
|
||||
Tuple& operator=(Tuple&& other)
|
||||
{
|
||||
set(move(other), Indices());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Tuple& operator=(const Tuple& other)
|
||||
{
|
||||
set(other, Indices());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto& get()
|
||||
{
|
||||
return Detail::Tuple<Ts...>::template get<T>();
|
||||
}
|
||||
|
||||
template<unsigned index>
|
||||
auto& get()
|
||||
{
|
||||
return Detail::Tuple<Ts...>::template get_with_index<typename Types::template Type<index>, index>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto& get() const
|
||||
{
|
||||
return Detail::Tuple<Ts...>::template get<T>();
|
||||
}
|
||||
|
||||
template<unsigned index>
|
||||
auto& get() const
|
||||
{
|
||||
return Detail::Tuple<Ts...>::template get_with_index<typename Types::template Type<index>, index>();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
auto apply_as_args(F&& f)
|
||||
{
|
||||
return apply_as_args(forward<F>(f), Indices());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
auto apply_as_args(F&& f) const
|
||||
{
|
||||
return apply_as_args(forward<F>(f), Indices());
|
||||
}
|
||||
|
||||
static constexpr auto size() { return sizeof...(Ts); }
|
||||
|
||||
private:
|
||||
template<unsigned... Is>
|
||||
Tuple(Tuple&& other, IndexSequence<Is...>)
|
||||
: Detail::Tuple<Ts...>(move(other.get<Is>())...)
|
||||
{
|
||||
}
|
||||
|
||||
template<unsigned... Is>
|
||||
Tuple(const Tuple& other, IndexSequence<Is...>)
|
||||
: Detail::Tuple<Ts...>(other.get<Is>()...)
|
||||
{
|
||||
}
|
||||
|
||||
template<unsigned... Is>
|
||||
void set(Tuple&& other, IndexSequence<Is...>)
|
||||
{
|
||||
((get<Is>() = move(other.get<Is>())), ...);
|
||||
}
|
||||
|
||||
template<unsigned... Is>
|
||||
void set(const Tuple& other, IndexSequence<Is...>)
|
||||
{
|
||||
((get<Is>() = other.get<Is>()), ...);
|
||||
}
|
||||
|
||||
template<typename F, unsigned... Is>
|
||||
auto apply_as_args(F&& f, IndexSequence<Is...>)
|
||||
{
|
||||
return forward<F>(f)(get<Is>()...);
|
||||
}
|
||||
|
||||
template<typename F, unsigned... Is>
|
||||
auto apply_as_args(F&& f, IndexSequence<Is...>) const
|
||||
{
|
||||
return forward<F>(f)(get<Is>()...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::Tuple;
|
@ -52,6 +52,7 @@ set(AK_TEST_SOURCES
|
||||
TestStringView.cpp
|
||||
TestTime.cpp
|
||||
TestTrie.cpp
|
||||
TestTuple.cpp
|
||||
TestTypeTraits.cpp
|
||||
TestTypedTransfer.cpp
|
||||
TestURL.cpp
|
||||
|
109
Tests/AK/TestTuple.cpp
Normal file
109
Tests/AK/TestTuple.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenity.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestSuite.h>
|
||||
|
||||
#include <AK/Tuple.h>
|
||||
|
||||
TEST_CASE(basic)
|
||||
{
|
||||
Tuple<int, String> value { 1, "foo" };
|
||||
EXPECT_EQ(value.get<int>(), 1);
|
||||
EXPECT_EQ(value.get<String>(), "foo");
|
||||
EXPECT_EQ(value.get<0>(), 1);
|
||||
EXPECT_EQ(value.get<1>(), "foo");
|
||||
|
||||
// Move assignment
|
||||
value = { 2, "bar" };
|
||||
EXPECT_EQ(value.get<int>(), 2);
|
||||
EXPECT_EQ(value.get<String>(), "bar");
|
||||
EXPECT_EQ(value.get<0>(), 2);
|
||||
EXPECT_EQ(value.get<1>(), "bar");
|
||||
|
||||
// Copy ctor
|
||||
auto other_value { value };
|
||||
EXPECT_EQ(other_value.get<int>(), 2);
|
||||
EXPECT_EQ(other_value.get<String>(), "bar");
|
||||
EXPECT_EQ(other_value.get<0>(), 2);
|
||||
EXPECT_EQ(other_value.get<1>(), "bar");
|
||||
|
||||
// Move ctor
|
||||
auto moved_to_value { move(value) };
|
||||
EXPECT_EQ(moved_to_value.get<int>(), 2);
|
||||
EXPECT_EQ(moved_to_value.get<String>(), "bar");
|
||||
EXPECT_EQ(moved_to_value.get<0>(), 2);
|
||||
EXPECT_EQ(moved_to_value.get<1>(), "bar");
|
||||
|
||||
// Copy assignment
|
||||
value = moved_to_value;
|
||||
EXPECT_EQ(moved_to_value.get<int>(), 2);
|
||||
EXPECT_EQ(moved_to_value.get<String>(), "bar");
|
||||
EXPECT_EQ(moved_to_value.get<0>(), 2);
|
||||
EXPECT_EQ(moved_to_value.get<1>(), "bar");
|
||||
EXPECT_EQ(value.get<int>(), 2);
|
||||
EXPECT_EQ(value.get<String>(), "bar");
|
||||
EXPECT_EQ(value.get<0>(), 2);
|
||||
EXPECT_EQ(value.get<1>(), "bar");
|
||||
}
|
||||
|
||||
TEST_CASE(no_copy)
|
||||
{
|
||||
struct NoCopy {
|
||||
AK_MAKE_NONCOPYABLE(NoCopy);
|
||||
|
||||
public:
|
||||
NoCopy(NoCopy&&) = default;
|
||||
NoCopy() = default;
|
||||
};
|
||||
|
||||
// Deleted copy ctor should not cause an issue so long as the value isn't copied.
|
||||
Tuple<NoCopy, int, int> value { {}, 1, 2 };
|
||||
auto foo = move(value);
|
||||
EXPECT_EQ(foo.get<1>(), 1);
|
||||
EXPECT_EQ(foo.get<2>(), 2);
|
||||
}
|
||||
|
||||
TEST_CASE(apply)
|
||||
{
|
||||
Tuple<int, int, String> args { 1, 2, "foo" };
|
||||
|
||||
// With copy
|
||||
{
|
||||
bool was_called = false;
|
||||
args.apply_as_args([&](int a, int b, String c) {
|
||||
was_called = true;
|
||||
EXPECT_EQ(a, 1);
|
||||
EXPECT_EQ(b, 2);
|
||||
EXPECT_EQ(c, "foo");
|
||||
});
|
||||
EXPECT(was_called);
|
||||
}
|
||||
|
||||
// With reference
|
||||
{
|
||||
bool was_called = false;
|
||||
args.apply_as_args([&](int& a, int& b, String& c) {
|
||||
was_called = true;
|
||||
EXPECT_EQ(a, 1);
|
||||
EXPECT_EQ(b, 2);
|
||||
EXPECT_EQ(c, "foo");
|
||||
});
|
||||
EXPECT(was_called);
|
||||
}
|
||||
|
||||
// With const reference, taken from a const tuple
|
||||
{
|
||||
bool was_called = false;
|
||||
const auto& args_ref = args;
|
||||
args_ref.apply_as_args([&](const int& a, const int& b, const String& c) {
|
||||
was_called = true;
|
||||
EXPECT_EQ(a, 1);
|
||||
EXPECT_EQ(b, 2);
|
||||
EXPECT_EQ(c, "foo");
|
||||
});
|
||||
EXPECT(was_called);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user