mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-29 14:14:45 +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
|
TestStringView.cpp
|
||||||
TestTime.cpp
|
TestTime.cpp
|
||||||
TestTrie.cpp
|
TestTrie.cpp
|
||||||
|
TestTuple.cpp
|
||||||
TestTypeTraits.cpp
|
TestTypeTraits.cpp
|
||||||
TestTypedTransfer.cpp
|
TestTypedTransfer.cpp
|
||||||
TestURL.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