Skip to content

Commit 2d689e1

Browse files
committed
Add input range support for metafunctions as specified in P2996R3.
- can_substitute - substitute - test_type - reflect_invoke - define_class Closes llvm#50.
1 parent 00e3d9a commit 2d689e1

File tree

4 files changed

+149
-30
lines changed

4 files changed

+149
-30
lines changed

libcxx/include/experimental/meta

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ inline namespace reflection_v2
2626
// std::meta::info
2727
using info = decltype(^int);
2828
29+
// concept reflection_range
30+
template <typename R>
31+
concept reflection_range = see below;
32+
2933
// name and location
3034
consteval auto name_of(info) -> string_view;
3135
consteval auto display_name_of(info) -> string_view;
@@ -54,15 +58,27 @@ consteval auto subobjects_of(info class_type) -> vector<info>;
5458
consteval auto enumerators_of(info enum_type) -> vector<info>;
5559
5660
// substitute
57-
consteval auto can_substitute(info templ, span<info const>) -> bool;
58-
consteval auto substitute(info templ, span<info const>) -> info;
61+
template <reflection_range R = span<info const>>
62+
consteval auto can_substitute(info templ, R &&args) -> bool;
63+
template <reflection_range R = span<info const>>
64+
consteval auto substitute(info templ, R &&args) -> info;
65+
66+
// reflect_invoke
67+
template <reflection_range R = span<info const>>
68+
consteval auto reflect_invoke(info target, R &&args) -> info;
69+
70+
// reflect expression results
71+
consteval auto reflect_value(auto) -> info;
72+
consteval auto reflect_object(auto &) -> info;
73+
consteval auto reflect_function(auto &) -> info;
5974
6075
// extract<T>
6176
template <typename Ty>
6277
consteval auto extract(info) -> T;
6378
6479
// test_type
6580
consteval auto test_type(info templ, info type) -> bool;
81+
template <reflection_range R = span<info const>>
6682
consteval auto test_types(info templ, span<info const> types) -> bool;
6783
6884
// other type predicates
@@ -103,18 +119,11 @@ consteval auto is_constructor(info) -> bool;
103119
consteval auto is_destructor(info) -> bool;
104120
consteval auto is_special_member(info) -> bool;
105121
106-
// reflect expression results
107-
consteval auto reflect_value(auto) -> info;
108-
consteval auto reflect_object(auto &) -> info;
109-
consteval auto reflect_function(auto &) -> info;
110-
111-
// reflect_invoke
112-
consteval auto reflect_invoke(info target, span<info const> args) -> info;
113-
114122
// define_class
115123
struct data_member_options_t;
116124
consteval auto data_member_spec(info class_type,
117125
data_member_options = {}) -> info;
126+
template <reflection_range R = span<info const>>
118127
consteval auto define_class(info class_type, span<info const>) -> info;
119128
120129
// data layout
@@ -137,6 +146,7 @@ consteval auto has_default_argument(info) -> bool;
137146

138147
#include <experimental/__config>
139148
#include <optional>
149+
#include <ranges>
140150
#include <source_location>
141151
#include <span>
142152
#include <string_view>
@@ -150,6 +160,12 @@ _LIBCPP_BEGIN_NAMESPACE_META_V2
150160
// An opaque handle to a reflected entity.
151161
using info = decltype(^int);
152162

163+
template <typename R>
164+
concept reflection_range =
165+
ranges::input_range<R> &&
166+
same_as<ranges::range_value_t<R>, info> &&
167+
same_as<remove_cvref_t<ranges::range_reference_t<R>>, info>;
168+
153169
namespace detail {
154170
enum : unsigned {
155171

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

615631
// Returns whether 'templ' substituted with 'args' forms a valid template-id.
616-
consteval auto can_substitute(info templ, span<info const> args) -> bool {
617-
return __metafunction(detail::__metafn_can_substitute, templ,
618-
args.data(), args.size());
632+
template <reflection_range R = span<info const>>
633+
consteval auto can_substitute(info templ, R &&args) -> bool {
634+
if constexpr (ranges::contiguous_range<R>) {
635+
return __metafunction(detail::__metafn_can_substitute, templ,
636+
ranges::data(args), ranges::size(args));
637+
} else {
638+
vector vargs = args | ranges::to<vector>();
639+
return __metafunction(detail::__metafn_can_substitute, templ,
640+
vargs.data(), vargs.size());
641+
}
619642
}
620643

621644
// Returns a reflection representing the template instantiation of the entity
622645
// reflected by 'templ' with the entities reflected by 'args'.
623-
consteval auto substitute(info templ, span<info const> args) -> info {
624-
return __metafunction(detail::__metafn_substitute, templ,
625-
args.data(), args.size());
646+
template <reflection_range R = span<info const>>
647+
consteval auto substitute(info templ, R &&args) -> info {
648+
if constexpr (ranges::contiguous_range<R>) {
649+
return __metafunction(detail::__metafn_substitute, templ,
650+
ranges::data(args), ranges::size(args));
651+
} else {
652+
vector vargs = args | ranges::to<vector>();
653+
return __metafunction(detail::__metafn_substitute, templ, vargs.data(),
654+
vargs.size());
655+
}
626656
}
627657

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

635-
consteval auto test_types(info templ, span<info const> args) -> bool {
665+
template <reflection_range R = span<info const>>
666+
consteval auto test_types(info templ, R &&args) -> bool {
636667
return extract<bool>(substitute(templ, args));
637668
}
638669

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

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

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

815849
// Returns a reflection of the object designated by the provided argument.
816850
consteval auto reflect_object(auto &r) -> info {
851+
static_assert(is_lvalue_reference_v<decltype(r)>,
852+
"objects must be designated by lvalue references");
853+
817854
constexpr auto Ty = type_of(^r);
818855

819-
static_assert(!is_function_v<std::remove_reference<typename [:Ty:]>>,
856+
static_assert(!is_function_v<remove_reference<typename [:Ty:]>>,
820857
"use 'reflect_function' for references to functions");
821858
return __metafunction(detail::__metafn_reflect_result, Ty, r);
822859
}
823860

824861
// Returns a reflection of the object designated by the provided argument.
825862
consteval auto reflect_function(auto &r) -> info {
863+
static_assert(is_lvalue_reference_v<decltype(r)>,
864+
"functions must be designated by lvalue references");
865+
826866
constexpr auto Ty = type_of(^r);
827-
using UnrefTy = std::remove_reference_t<[:Ty:]>;
867+
using UnrefTy = remove_reference_t<[:Ty:]>;
828868

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

839879
// Returns a reflection of the constant value obtained from calling
840880
// target(args...)
841-
consteval auto reflect_invoke(info target, span<info const> args) -> info {
842-
return __metafunction(detail::__metafn_reflect_invoke, target,
843-
args.data(), args.size());
881+
template <reflection_range R = span<info const>>
882+
consteval auto reflect_invoke(info target, R &&args) -> info {
883+
if constexpr (ranges::contiguous_range<R>) {
884+
return __metafunction(detail::__metafn_reflect_invoke, target,
885+
ranges::data(args), ranges::size(args));
886+
} else {
887+
vector vargs = args | ranges::to<vector>();
888+
return __metafunction(detail::__metafn_reflect_invoke, target,
889+
vargs.data(), vargs.size());
890+
}
844891
}
845892

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

870917
// Completes the definition of the record type reflected by 'class_type' with
871918
// the members described by the reflections in the provided span.
872-
consteval auto define_class(info class_type,
873-
span<info const> members) -> info {
874-
return __metafunction(detail::__metafn_define_class, class_type,
875-
members.size(), members.data());
919+
template <reflection_range R = span<info const>>
920+
consteval auto define_class(info class_type, R &&members) -> info {
921+
if constexpr (ranges::contiguous_range<R>) {
922+
return __metafunction(detail::__metafn_define_class, class_type,
923+
ranges::size(members), ranges::data(members));
924+
} else {
925+
vector vmembers = members | ranges::to<vector>();
926+
return __metafunction(detail::__metafn_define_class, class_type,
927+
vmembers.size(), vmembers.data());
928+
}
876929
}
877930

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

933986
return __metafunction(detail::__metafn_reflect_result, dealias(RTy),
934987
static_cast<typename [:DTy:]>(r));

libcxx/test/std/experimental/reflection/define-class.pass.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,31 @@ constexpr foo<bool, char> f3 = {3};
203203
static_assert(f1.mem1 + f2.mem2 + f3.mem3 == 6);
204204
} // namespace completion_of_template_with_pack_param
205205

206+
// =========================
207+
// with_non_contiguous_range
208+
// =========================
209+
210+
namespace with_non_contiguous_range {
211+
struct foo;
212+
static_assert(is_type(define_class(
213+
^foo,
214+
std::views::join(std::vector<std::vector<std::pair<bool,
215+
std::meta::info>>> {
216+
{
217+
std::make_pair(true, std::meta::data_member_spec(^int, {.name="i"})),
218+
}, {
219+
std::make_pair(false, std::meta::data_member_spec(^std::string)),
220+
std::make_pair(true, std::meta::data_member_spec(^bool, {.name="b"})),
221+
}
222+
}) |
223+
std::views::filter([](auto P) { return P.first; }) |
224+
std::views::transform([](auto P) { return P.second; }))));
225+
226+
static_assert(type_of(^foo::i) == ^int);
227+
static_assert(type_of(^foo::b) == ^bool);
228+
static_assert(nonstatic_data_members_of(^foo).size() == 2);
229+
} // namespace with_non_contiguous_range
230+
206231
// ==================
207232
// static_data_member
208233
// ==================

libcxx/test/std/experimental/reflection/reflect-invoke.pass.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,20 @@ static_assert(v == std::meta::reflect_value(0));
171171

172172
} // namespace returning_references
173173

174+
// ==========================
175+
// with_non_contiguous_ranges
176+
// ==========================
177+
178+
namespace with_non_contiguous_ranges {
179+
consteval auto sum(auto... vs) { return (... + vs); }
180+
181+
static_assert(
182+
std::meta::reflect_value(20) ==
183+
reflect_invoke(^sum, std::ranges::iota_view{1, 10} |
184+
std::views::filter([](int v) {
185+
return v % 2 == 0;
186+
}) |
187+
std::views::transform(std::meta::reflect_value<int>)));
188+
} // namespace with_non_contiguous_ranges
189+
174190
int main() { }

libcxx/test/std/experimental/reflection/substitute-and-test-type.pass.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,32 @@ struct S { static constexpr int val = 4; };
380380
static_assert([:substitute(^Plus1, {static_data_members_of(^S)[0]}):] == 5);
381381
} // namespace with_reflection_of_declaration
382382

383+
// ==========================
384+
// with_non_contiguous_ranges
385+
// ==========================
386+
387+
namespace with_non_contiguous_ranges {
388+
template <char... Is> consteval std::string join() {
389+
return std::string{Is...};
390+
}
391+
static_assert(
392+
[:substitute(^join, "Hello, world!" |
393+
std::views::filter([](char c) {
394+
return (c >= 'A' && c <= 'Z') ||
395+
(c >= 'a' && c <= 'z') || c == ' ';
396+
}) |
397+
std::views::transform(std::meta::reflect_value<char>))
398+
:]() == "Hello world");
399+
400+
static_assert(!can_substitute(^join, std::vector {^int, ^std::array, ^bool} |
401+
std::views::filter(std::meta::is_type)));
402+
403+
static_assert(test_types(^std::is_same_v, std::vector {^int, ^void, ^int} |
404+
std::views::filter([](auto R) {
405+
return R != ^void;
406+
})));
407+
} // namespace with_non_contiguous_ranges
408+
383409
// ====================
384410
// invalid_template_ids
385411
// ====================
@@ -395,5 +421,4 @@ static_assert(!can_substitute(^Cls, {^int, ^bool, ^bool}));
395421
static_assert(!can_substitute(^Cls, {^int, ^bool, ^std::array, ^std::array}));
396422
} // namespace invalid_template_ids
397423

398-
399424
int main() { }

0 commit comments

Comments
 (0)