Skip to content

Commit

Permalink
Add input range support for metafunctions as specified in P2996R3.
Browse files Browse the repository at this point in the history
- can_substitute
- substitute
- test_type
- reflect_invoke
- define_class

Closes llvm#50.
  • Loading branch information
katzdm committed Jun 9, 2024
1 parent 00e3d9a commit 2d689e1
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 30 deletions.
111 changes: 82 additions & 29 deletions libcxx/include/experimental/meta
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ inline namespace reflection_v2
// std::meta::info
using info = decltype(^int);
// concept reflection_range
template <typename R>
concept reflection_range = see below;
// name and location
consteval auto name_of(info) -> string_view;
consteval auto display_name_of(info) -> string_view;
Expand Down Expand Up @@ -54,15 +58,27 @@ consteval auto subobjects_of(info class_type) -> vector<info>;
consteval auto enumerators_of(info enum_type) -> vector<info>;
// substitute
consteval auto can_substitute(info templ, span<info const>) -> bool;
consteval auto substitute(info templ, span<info const>) -> info;
template <reflection_range R = span<info const>>
consteval auto can_substitute(info templ, R &&args) -> bool;
template <reflection_range R = span<info const>>
consteval auto substitute(info templ, R &&args) -> info;
// reflect_invoke
template <reflection_range R = span<info const>>
consteval auto reflect_invoke(info target, R &&args) -> info;
// reflect expression results
consteval auto reflect_value(auto) -> info;
consteval auto reflect_object(auto &) -> info;
consteval auto reflect_function(auto &) -> info;
// extract<T>
template <typename Ty>
consteval auto extract(info) -> T;
// test_type
consteval auto test_type(info templ, info type) -> bool;
template <reflection_range R = span<info const>>
consteval auto test_types(info templ, span<info const> types) -> bool;
// other type predicates
Expand Down Expand Up @@ -103,18 +119,11 @@ consteval auto is_constructor(info) -> bool;
consteval auto is_destructor(info) -> bool;
consteval auto is_special_member(info) -> bool;
// reflect expression results
consteval auto reflect_value(auto) -> info;
consteval auto reflect_object(auto &) -> info;
consteval auto reflect_function(auto &) -> info;
// reflect_invoke
consteval auto reflect_invoke(info target, span<info const> args) -> info;
// define_class
struct data_member_options_t;
consteval auto data_member_spec(info class_type,
data_member_options = {}) -> info;
template <reflection_range R = span<info const>>
consteval auto define_class(info class_type, span<info const>) -> info;
// data layout
Expand All @@ -137,6 +146,7 @@ consteval auto has_default_argument(info) -> bool;

#include <experimental/__config>
#include <optional>
#include <ranges>
#include <source_location>
#include <span>
#include <string_view>
Expand All @@ -150,6 +160,12 @@ _LIBCPP_BEGIN_NAMESPACE_META_V2
// An opaque handle to a reflected entity.
using info = decltype(^int);

template <typename R>
concept reflection_range =
ranges::input_range<R> &&
same_as<ranges::range_value_t<R>, info> &&
same_as<remove_cvref_t<ranges::range_reference_t<R>>, info>;

namespace detail {
enum : unsigned {

Expand Down Expand Up @@ -613,26 +629,41 @@ consteval auto enumerators_of(info r) -> vector<info> {
}

// Returns whether 'templ' substituted with 'args' forms a valid template-id.
consteval auto can_substitute(info templ, span<info const> args) -> bool {
return __metafunction(detail::__metafn_can_substitute, templ,
args.data(), args.size());
template <reflection_range R = span<info const>>
consteval auto can_substitute(info templ, R &&args) -> bool {
if constexpr (ranges::contiguous_range<R>) {
return __metafunction(detail::__metafn_can_substitute, templ,
ranges::data(args), ranges::size(args));
} else {
vector vargs = args | ranges::to<vector>();
return __metafunction(detail::__metafn_can_substitute, templ,
vargs.data(), vargs.size());
}
}

// Returns a reflection representing the template instantiation of the entity
// reflected by 'templ' with the entities reflected by 'args'.
consteval auto substitute(info templ, span<info const> args) -> info {
return __metafunction(detail::__metafn_substitute, templ,
args.data(), args.size());
template <reflection_range R = span<info const>>
consteval auto substitute(info templ, R &&args) -> info {
if constexpr (ranges::contiguous_range<R>) {
return __metafunction(detail::__metafn_substitute, templ,
ranges::data(args), ranges::size(args));
} else {
vector vargs = args | ranges::to<vector>();
return __metafunction(detail::__metafn_substitute, templ, vargs.data(),
vargs.size());
}
}

// Returns the value or object from 'r' if 'r' is a reflection of a value
// or object having type 'T'.
template <typename Ty> requires (!std::is_rvalue_reference_v<Ty>)
template <typename Ty> requires (!is_rvalue_reference_v<Ty>)
consteval auto extract(info r) -> Ty {
return __metafunction(detail::__metafn_extract, ^Ty, r);
}

consteval auto test_types(info templ, span<info const> args) -> bool {
template <reflection_range R = span<info const>>
consteval auto test_types(info templ, R &&args) -> bool {
return extract<bool>(substitute(templ, args));
}

Expand Down Expand Up @@ -806,25 +837,34 @@ consteval auto is_special_member(info r) -> bool {

// Returns a reflection of the value held by the provided argument.
consteval auto reflect_value(auto r) -> info {
constexpr std::meta::info Ty = type_of(^r);
static_assert(!is_reference_v<decltype(r)>,
"values cannot have reference type");

constexpr info Ty = type_of(^r);

return __metafunction(detail::__metafn_reflect_result, Ty,
static_cast<typename [:Ty:]>(r));
}

// Returns a reflection of the object designated by the provided argument.
consteval auto reflect_object(auto &r) -> info {
static_assert(is_lvalue_reference_v<decltype(r)>,
"objects must be designated by lvalue references");

constexpr auto Ty = type_of(^r);

static_assert(!is_function_v<std::remove_reference<typename [:Ty:]>>,
static_assert(!is_function_v<remove_reference<typename [:Ty:]>>,
"use 'reflect_function' for references to functions");
return __metafunction(detail::__metafn_reflect_result, Ty, r);
}

// Returns a reflection of the object designated by the provided argument.
consteval auto reflect_function(auto &r) -> info {
static_assert(is_lvalue_reference_v<decltype(r)>,
"functions must be designated by lvalue references");

constexpr auto Ty = type_of(^r);
using UnrefTy = std::remove_reference_t<[:Ty:]>;
using UnrefTy = remove_reference_t<[:Ty:]>;

constexpr bool IsPtr = is_pointer_v<UnrefTy>;
static_assert(!IsPtr, "use 'reflect_value' for pointers to functions");
Expand All @@ -838,9 +878,16 @@ consteval auto reflect_function(auto &r) -> info {

// Returns a reflection of the constant value obtained from calling
// target(args...)
consteval auto reflect_invoke(info target, span<info const> args) -> info {
return __metafunction(detail::__metafn_reflect_invoke, target,
args.data(), args.size());
template <reflection_range R = span<info const>>
consteval auto reflect_invoke(info target, R &&args) -> info {
if constexpr (ranges::contiguous_range<R>) {
return __metafunction(detail::__metafn_reflect_invoke, target,
ranges::data(args), ranges::size(args));
} else {
vector vargs = args | ranges::to<vector>();
return __metafunction(detail::__metafn_reflect_invoke, target,
vargs.data(), vargs.size());
}
}

// Representation of a data member which may be passed to 'data_member_spec'.
Expand Down Expand Up @@ -869,10 +916,16 @@ consteval auto data_member_spec(info member_type,

// Completes the definition of the record type reflected by 'class_type' with
// the members described by the reflections in the provided span.
consteval auto define_class(info class_type,
span<info const> members) -> info {
return __metafunction(detail::__metafn_define_class, class_type,
members.size(), members.data());
template <reflection_range R = span<info const>>
consteval auto define_class(info class_type, R &&members) -> info {
if constexpr (ranges::contiguous_range<R>) {
return __metafunction(detail::__metafn_define_class, class_type,
ranges::size(members), ranges::data(members));
} else {
vector vmembers = members | ranges::to<vector>();
return __metafunction(detail::__metafn_define_class, class_type,
vmembers.size(), vmembers.data());
}
}

// Returns the offset of the reflected entity.
Expand Down Expand Up @@ -928,7 +981,7 @@ template <typename Ty>
consteval auto reflect_result(Ty r) -> info {
constexpr auto DTy = dealias(^Ty);
constexpr auto RTy = is_class_v<[:DTy:]> || is_reference_v<[:DTy:]> ?
DTy : ^std::remove_cv_t<[:DTy:]>;
DTy : ^remove_cv_t<[:DTy:]>;

return __metafunction(detail::__metafn_reflect_result, dealias(RTy),
static_cast<typename [:DTy:]>(r));
Expand Down
25 changes: 25 additions & 0 deletions libcxx/test/std/experimental/reflection/define-class.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,31 @@ constexpr foo<bool, char> f3 = {3};
static_assert(f1.mem1 + f2.mem2 + f3.mem3 == 6);
} // namespace completion_of_template_with_pack_param

// =========================
// with_non_contiguous_range
// =========================

namespace with_non_contiguous_range {
struct foo;
static_assert(is_type(define_class(
^foo,
std::views::join(std::vector<std::vector<std::pair<bool,
std::meta::info>>> {
{
std::make_pair(true, std::meta::data_member_spec(^int, {.name="i"})),
}, {
std::make_pair(false, std::meta::data_member_spec(^std::string)),
std::make_pair(true, std::meta::data_member_spec(^bool, {.name="b"})),
}
}) |
std::views::filter([](auto P) { return P.first; }) |
std::views::transform([](auto P) { return P.second; }))));

static_assert(type_of(^foo::i) == ^int);
static_assert(type_of(^foo::b) == ^bool);
static_assert(nonstatic_data_members_of(^foo).size() == 2);
} // namespace with_non_contiguous_range

// ==================
// static_data_member
// ==================
Expand Down
16 changes: 16 additions & 0 deletions libcxx/test/std/experimental/reflection/reflect-invoke.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,20 @@ static_assert(v == std::meta::reflect_value(0));

} // namespace returning_references

// ==========================
// with_non_contiguous_ranges
// ==========================

namespace with_non_contiguous_ranges {
consteval auto sum(auto... vs) { return (... + vs); }

static_assert(
std::meta::reflect_value(20) ==
reflect_invoke(^sum, std::ranges::iota_view{1, 10} |
std::views::filter([](int v) {
return v % 2 == 0;
}) |
std::views::transform(std::meta::reflect_value<int>)));
} // namespace with_non_contiguous_ranges

int main() { }
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,32 @@ struct S { static constexpr int val = 4; };
static_assert([:substitute(^Plus1, {static_data_members_of(^S)[0]}):] == 5);
} // namespace with_reflection_of_declaration

// ==========================
// with_non_contiguous_ranges
// ==========================

namespace with_non_contiguous_ranges {
template <char... Is> consteval std::string join() {
return std::string{Is...};
}
static_assert(
[:substitute(^join, "Hello, world!" |
std::views::filter([](char c) {
return (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') || c == ' ';
}) |
std::views::transform(std::meta::reflect_value<char>))
:]() == "Hello world");

static_assert(!can_substitute(^join, std::vector {^int, ^std::array, ^bool} |
std::views::filter(std::meta::is_type)));

static_assert(test_types(^std::is_same_v, std::vector {^int, ^void, ^int} |
std::views::filter([](auto R) {
return R != ^void;
})));
} // namespace with_non_contiguous_ranges

// ====================
// invalid_template_ids
// ====================
Expand All @@ -395,5 +421,4 @@ static_assert(!can_substitute(^Cls, {^int, ^bool, ^bool}));
static_assert(!can_substitute(^Cls, {^int, ^bool, ^std::array, ^std::array}));
} // namespace invalid_template_ids


int main() { }

0 comments on commit 2d689e1

Please sign in to comment.