300 lines
9.3 KiB
C++
300 lines
9.3 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Copyright 2024 Bloomberg Finance L.P.
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// UNSUPPORTED: c++03 || c++11 || c++14 || c++17 || c++20
|
|
// ADDITIONAL_COMPILE_FLAGS: -fblocks
|
|
// ADDITIONAL_COMPILE_FLAGS: -freflection-latest
|
|
|
|
// <experimental/reflection>
|
|
//
|
|
// [reflection]
|
|
//
|
|
// RUN: %{build}
|
|
// RUN: %{exec} %t.exe > %t.stdout
|
|
|
|
#include <meta>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <print>
|
|
#include <ranges>
|
|
#include <tuple>
|
|
|
|
|
|
// ============================
|
|
// alexandrescu_lambda_to_tuple
|
|
// ============================
|
|
|
|
namespace alexandrescu_lambda_to_tuple {
|
|
using std::meta::access_context;
|
|
|
|
consteval auto struct_to_tuple_type(std::meta::info type) -> std::meta::info {
|
|
constexpr auto remove_cvref = [](std::meta::info r) consteval {
|
|
return substitute(^^std::remove_cvref_t, {r});
|
|
};
|
|
|
|
return substitute(^^std::tuple,
|
|
nonstatic_data_members_of(type, access_context::unchecked())
|
|
| std::views::transform(std::meta::type_of)
|
|
| std::views::transform(remove_cvref)
|
|
| std::ranges::to<std::vector>());
|
|
}
|
|
|
|
template <typename To, typename From, std::meta::info... members>
|
|
constexpr auto struct_to_tuple_helper(From const& from) -> To {
|
|
return To(from.[:members:]...);
|
|
}
|
|
|
|
template<typename From>
|
|
consteval auto get_struct_to_tuple_helper() {
|
|
using To = [: struct_to_tuple_type(^^From) :];
|
|
|
|
std::vector args = {^^To, ^^From};
|
|
for (auto mem : nonstatic_data_members_of(^^From,
|
|
access_context::unchecked())) {
|
|
args.push_back(reflect_constant(mem));
|
|
}
|
|
|
|
return extract<To(*)(From const&)>(substitute(^^struct_to_tuple_helper,
|
|
args));
|
|
}
|
|
|
|
template <typename From>
|
|
constexpr auto struct_to_tuple(From const& from) {
|
|
return get_struct_to_tuple_helper<From>()(from);
|
|
}
|
|
|
|
void run_test() {
|
|
struct S { bool i; int j; int k; };
|
|
|
|
int a = 1;
|
|
bool b = true;
|
|
[[maybe_unused]] int c = 32;
|
|
std::string d = "hello";
|
|
auto fn = [=]() {
|
|
(void) a; (void) b; (void) d;
|
|
};
|
|
|
|
// RUN: grep "Lambda state: <1, true, \"hello\">" %t.stdout
|
|
auto t = struct_to_tuple(fn);
|
|
std::println(R"(Lambda state: <{}, {}, "{}">)",
|
|
get<0>(t), get<1>(t), get<2>(t));
|
|
}
|
|
} // namespace alexandrescu_lambda_to_tuple
|
|
|
|
// =======================
|
|
// pdimov_sorted_type_list
|
|
// =======================
|
|
|
|
namespace pdimov_sorted_type_list {
|
|
template<class...> struct type_list { };
|
|
|
|
consteval auto sorted_impl(std::vector<std::meta::info> v)
|
|
{
|
|
std::ranges::sort(v, {}, std::meta::alignment_of);
|
|
return v;
|
|
}
|
|
|
|
template<class... Ts> consteval auto sorted()
|
|
{
|
|
using R = typename [:substitute(^^type_list, sorted_impl({^^Ts...})):];
|
|
return R{};
|
|
}
|
|
|
|
void run_test() {
|
|
static_assert(typeid(sorted<int, double, char>()) ==
|
|
typeid(type_list<char, int, double>));
|
|
}
|
|
} // namespace pdimov_sorted_type_list
|
|
|
|
// =======================
|
|
// alisdair_universal_swap
|
|
// =======================
|
|
|
|
namespace alisdair_universal_swap {
|
|
template <typename T>
|
|
void do_swap_representations(T& lhs, T& rhs) {
|
|
// This implementation cannot rebind references, and does not handle const
|
|
// data members --- still need to decide whether we support the latter
|
|
if constexpr (std::is_class_v<T>) {
|
|
// This implementation ensures that empty types do nothing
|
|
template for (constexpr auto base :
|
|
define_static_array(
|
|
bases_of(^^T, std::meta::access_context::unchecked()))) {
|
|
using Base = [:type_of(base):];
|
|
do_swap_representations<Base>((Base &)lhs, (Base &)rhs);
|
|
}
|
|
template for (constexpr auto mem :
|
|
define_static_array(
|
|
nonstatic_data_members_of(
|
|
^^T, std::meta::access_context::unchecked()))) {
|
|
do_swap_representations<typename [:type_of(mem):]>(lhs.[:mem:],
|
|
rhs.[:mem:]);
|
|
}
|
|
} else if constexpr (std::is_array_v<T>) {
|
|
static_assert(0 < std::rank_v<T>, "cannot swap arrays of unknown bound");
|
|
using MemT = std::decay_t<decltype(lhs[0])>;
|
|
template for (constexpr auto Idx :
|
|
define_static_array([] {
|
|
std::vector<size_t> result;
|
|
for (size_t idx = 0; idx < std::size(result); ++idx)
|
|
result.push_back(idx);
|
|
return result;
|
|
}())) {
|
|
do_swap_representations<MemT>(lhs[Idx], rhs[Idx]);
|
|
};
|
|
} else if constexpr (std::is_scalar_v<T> or std::is_union_v<T>) {
|
|
// correct for unions without tail padding, including swapping active
|
|
// element will need compiler magic to eliminate tail padding though
|
|
// language ensures to not overwrite tail padding for scalars
|
|
// May be broken if union overloads `operator=`
|
|
T intrm = lhs;
|
|
lhs = rhs;
|
|
rhs = intrm;
|
|
} else if constexpr (std::is_reference_v<T>) {
|
|
static_assert(false, "Does not yet rebind references");
|
|
} else if constexpr (sizeof(T) > 0) {
|
|
static_assert(false, "Unexpected type category");
|
|
}
|
|
}
|
|
|
|
void run_test() {
|
|
std::vector<int> a = {1, 2, 3};
|
|
std::vector<int> b = {3, 2, 1};
|
|
do_swap_representations(a, b);
|
|
|
|
// RUN: grep "universal-swap: a = \[3, 2, 1]" %t.stdout
|
|
// RUN: grep "universal-swap: b = \[1, 2, 3]" %t.stdout
|
|
std::println("universal-swap: a = [{}, {}, {}]", a[0], a[1], a[2]);
|
|
std::println("universal-swap: b = [{}, {}, {}]", b[0], b[1], b[2]);
|
|
}
|
|
} // namespace alisdair_universal_swap
|
|
|
|
// ==============================
|
|
// std_apply_with_function_splice
|
|
// ==============================
|
|
|
|
namespace std_apply_with_function_splice {
|
|
void run_test() {
|
|
struct S {
|
|
static void print(std::string_view sv) {
|
|
std::println("std::apply: {}", sv);
|
|
}
|
|
};
|
|
|
|
// RUN: grep "std::apply: Hello, world!" %t.stdout
|
|
std::apply([:^^S::print:], std::tuple {"Hello, world!"});
|
|
}
|
|
} // namespace std_apply_with_function_splice
|
|
|
|
// ==========================================
|
|
// array_with_default_initialized_reflections
|
|
// ==========================================
|
|
|
|
namespace array_with_default_initialized_reflections {
|
|
consteval auto fn() {
|
|
std::array<std::meta::info, 3> arr = {};
|
|
arr[0] = ^^int;
|
|
|
|
return arr;
|
|
}
|
|
|
|
[[maybe_unused]] constexpr auto rs = fn();
|
|
} // namespace array_with_default_initialized_reflections
|
|
|
|
// ======================
|
|
// compatible_with_blocks
|
|
// ======================
|
|
|
|
namespace compatible_with_blocks {
|
|
constexpr auto block = std::meta::reflect_constant(^int() { return 4; });
|
|
static_assert(type_of(block) == ^^int(^)());
|
|
|
|
void run_test() {
|
|
// RUN: grep "block result 1: 4" %t.stdout
|
|
std::println("block result 1: {}", [:block:]());
|
|
|
|
// RUN: grep "block result 2: 6" %t.stdout
|
|
std::println("block result 2: {}", 4 ^^(void){ return 2; }());
|
|
}
|
|
} // namespace compatible_with_blocks
|
|
|
|
// =========================
|
|
// expansions_over_std_tuple
|
|
// =========================
|
|
|
|
namespace expansions_over_std_tuple {
|
|
void run_test() {
|
|
std::tuple<int, bool, char> t;
|
|
template for (auto e : t)
|
|
(void) e;
|
|
|
|
template for (constexpr auto v : std::tuple{1, 2, 3})
|
|
(void) v;
|
|
|
|
static constexpr auto my_tuple = std::tuple{1, 2, 3};
|
|
template for (constexpr auto v : my_tuple)
|
|
(void) v;
|
|
}
|
|
} // namespace expansions_over_std_tuple
|
|
|
|
template <std::meta::info... Tests>
|
|
void run_tests() {
|
|
(..., [:Tests:]::run_test());
|
|
}
|
|
|
|
// ===============
|
|
// barry_alias_bug
|
|
// ===============
|
|
|
|
namespace barry_alias_bug {
|
|
template <std::meta::info R>
|
|
struct Reflection {
|
|
static constexpr auto value = R;
|
|
};
|
|
|
|
template <class... R>
|
|
struct Sequence { };
|
|
|
|
template <size_t I, typename Seq>
|
|
using get_element_t = [: template_arguments_of(^^Seq)[I] :];
|
|
|
|
template <class T>
|
|
using get_base_classes_t = [: [] {
|
|
std::vector<std::meta::info> args;
|
|
for (std::meta::info b : bases_of(T::value,
|
|
std::meta::access_context::unchecked())) {
|
|
args.push_back(substitute(^^Reflection,
|
|
{std::meta::reflect_constant(b)}));
|
|
}
|
|
return substitute(^^Sequence, args);
|
|
}() :];
|
|
|
|
|
|
struct B { };
|
|
struct D : B { };
|
|
|
|
constexpr auto b = bases_of(^^D, std::meta::access_context::unchecked())[0];
|
|
static_assert(std::same_as<
|
|
get_element_t<0, get_base_classes_t<Reflection<^^D>>>,
|
|
Reflection<b>>);
|
|
} // namespace barry_alias_bug
|
|
|
|
int main() {
|
|
run_tests<
|
|
^^alexandrescu_lambda_to_tuple,
|
|
^^pdimov_sorted_type_list,
|
|
^^alisdair_universal_swap,
|
|
^^std_apply_with_function_splice,
|
|
^^compatible_with_blocks,
|
|
^^expansions_over_std_tuple
|
|
>();
|
|
}
|