UgMisc 0.3
Miscellaneous C++ header library
Loading...
Searching...
No Matches
member.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: © 2025 Larry Chips <larry@larrychips.net>
3 * SPDX-Licence-Identifier: MIT
4 */
5#ifndef UGMISC_MEMBER_HPP
6#define UGMISC_MEMBER_HPP
7
125
126#include "ugmisc/typelist.hpp"
127#include <functional>
128#include <utility>
129
130
131
132
133#ifdef UGMISC_DOCS
134#error
135#endif
136
137
138
139
150#define UGMISC_DECL_MEMBER_ACCESS(TNAME, NAME) \
151class TNAME { \
152 struct ugmisc_priv { \
153 /* Static method call support. */ \
154 \
155 template<class ugmisc__T, class, class...ugmisc__A> \
156 struct static_caller_as_function { \
157 static constexpr bool is_callable = false; \
158 }; \
159 \
160 template<class ugmisc__T, class...ugmisc__A> \
161 struct static_caller_as_function<ugmisc__T, std::void_t<decltype(ugmisc__T::NAME(std::declval<ugmisc__A>()...))>, ugmisc__A...> { \
162 static constexpr bool is_callable = true; \
163 \
164 static constexpr decltype(auto) call(ugmisc__A...args) { \
165 return ugmisc__T::NAME(std::forward<ugmisc__A>(args)...); \
166 } \
167 }; \
168 \
169 template<class ugmisc__T, bool, class...ugmisc__A> \
170 struct static_caller_as_invoke { \
171 static constexpr bool is_callable = false; \
172 }; \
173 \
174 template<class ugmisc__T, class...ugmisc__A> \
175 struct static_caller_as_invoke<ugmisc__T, std::is_invocable_v<decltype((ugmisc__T::NAME)), ugmisc__A...>, ugmisc__A...> { \
176 static constexpr bool is_callable = true; \
177 \
178 static constexpr decltype(auto) call(ugmisc__A...args) { \
179 return std::invoke(ugmisc__T::NAME, std::forward<ugmisc__A>(args)...); \
180 } \
181 }; \
182 \
183 template<class ugmisc__T, class...ugmisc__A> \
184 using static_caller = std::conditional_t< \
185 static_caller_as_function<ugmisc__T, void, ugmisc__A...>::is_callable, \
186 static_caller_as_function<ugmisc__T, void, ugmisc__A...>, \
187 static_caller_as_invoke<ugmisc__T, true, ugmisc__A...> \
188 >; \
189 \
190 /* Non static method call support. */ \
191 \
192 template<class ugmisc__T, class, class...ugmisc__A> \
193 struct caller_as_method { \
194 static constexpr bool is_callable = false; \
195 }; \
196 \
197 template<class ugmisc__T, class...ugmisc__A> \
198 struct caller_as_method<ugmisc__T, std::void_t<decltype(std::declval<ugmisc__T>().NAME(std::declval<ugmisc__A>()...))>, ugmisc__A...> { \
199 static constexpr bool is_callable = true; \
200 \
201 static constexpr decltype(auto) call(ugmisc__T& obj, ugmisc__A...args) { \
202 return std::forward<ugmisc__T>(obj).NAME(std::forward<ugmisc__A>(args)...); \
203 } \
204 }; \
205 \
206 template<class ugmisc__T, bool I, class...ugmisc__A> \
207 struct caller_as_invoke { \
208 static constexpr bool is_callable = false; \
209 }; \
210 \
211 template<class ugmisc__T, class...ugmisc__A> \
212 struct caller_as_invoke<ugmisc__T, \
213 std::is_invocable_v<decltype(std::declval<ugmisc__T>().NAME), ugmisc__A...>, \
214 ugmisc__A... \
215 > \
216 { \
217 static constexpr bool is_callable = true; \
218 static constexpr decltype(auto) call(ugmisc__T& obj, ugmisc__A...args) { \
219 return std::invoke(std::forward<ugmisc__T>(obj).NAME, std::forward<ugmisc__A>(args)...); \
220 } \
221 }; \
222 \
223 template<class ugmisc__T, class...ugmisc__A> \
224 using caller = std::conditional_t< \
225 caller_as_method<ugmisc__T, void, ugmisc__A...>::is_callable, \
226 caller_as_method<ugmisc__T, void, ugmisc__A...>, \
227 caller_as_invoke<ugmisc__T, true, ugmisc__A...> \
228 >; \
229 /* Static data reference support. */ \
230 \
231 template<class ugmisc__T, class=void> \
232 struct static_member { \
233 static constexpr bool has_member = false; \
234 }; \
235 \
236 template<class ugmisc__T> \
237 struct static_member<ugmisc__T, std::void_t<decltype(ugmisc__T::NAME)>> { \
238 static constexpr bool has_member = true; \
239 \
240 static constexpr auto& get() { \
241 return ugmisc__T::NAME; \
242 } \
243 }; \
244 \
245 /* Non static data reference support. */ \
246 \
247 template<class ugmisc__T, class=void> \
248 struct member { \
249 static constexpr bool has_member = false; \
250 }; \
251 \
252 template<class ugmisc__T> \
253 struct member<ugmisc__T, std::void_t<decltype(std::declval<ugmisc__T>().NAME)>> { \
254 static constexpr bool has_member = true; \
255 \
256 static constexpr auto& get(ugmisc__T& obj) { \
257 return obj.NAME; \
258 } \
259 \
260 using member_type = decltype(std::declval<std::remove_reference_t<ugmisc__T>>().NAME); \
261 }; \
262 /* Type support. */ \
263 \
264 template< class ugmisc__T, class=void > struct GetType { \
265 static constexpr bool has_member_type = false; \
266 using safe_type = void; \
267 }; \
268 \
269 template< class ugmisc__T > struct GetType< ugmisc__T, std::void_t< typename ugmisc__T::NAME > > { \
270 static constexpr bool has_member_type = true; \
271 using safe_type = typename ugmisc__T::NAME; \
272 using type = typename ugmisc__T::NAME; \
273 }; \
274 \
275 }; \
276public: \
277 struct ugmisc { \
278 /* Static method call. */ \
279 template<class ugmisc__T, class...ugmisc__A> static constexpr bool is_static_callable = \
280 ugmisc_priv::template static_caller<ugmisc__T, ugmisc__A...>::is_callable; \
281 \
282 template<class ugmisc__T, class...ugmisc__A> \
283 static constexpr auto static_call(ugmisc__A&&...args) \
284 -> decltype( \
285 ugmisc_priv::template static_caller<ugmisc__T, ugmisc__A...>::call( \
286 std::forward<ugmisc__A>(args)... \
287 )) \
288 { \
289 return ugmisc_priv::template static_caller<ugmisc__T, ugmisc__A...>::call( \
290 std::forward<ugmisc__A>(args)... \
291 ); \
292 } \
293 \
294 /* Non static method call. */ \
295 template<class ugmisc__T, class...ugmisc__A> static constexpr bool is_callable = \
296 ugmisc_priv::template caller<ugmisc__T, ugmisc__A...>::is_callable; \
297 \
298 template<class ugmisc__T, class...ugmisc__A> \
299 static constexpr auto call(ugmisc__T& obj, ugmisc__A&&...args) \
300 -> decltype( \
301 ugmisc_priv::template caller<ugmisc__T, ugmisc__A...>::call( \
302 obj, \
303 std::forward<ugmisc__A>(args)... \
304 )) \
305 { \
306 return ugmisc_priv::template caller<ugmisc__T, ugmisc__A...>::call( \
307 obj, \
308 std::forward<ugmisc__A>(args)... \
309 ); \
310 } \
311 \
312 /* Static member reference. */ \
313 template< class ugmisc__T > \
314 static constexpr bool has_static_member \
315 = ugmisc_priv::template static_member<ugmisc__T>::has_member; \
316 template< class ugmisc__T> \
317 static constexpr auto& static_get() { \
318 return ugmisc_priv::template static_member<ugmisc__T>::get(); \
319 } \
320 \
321 /* Non static member reference. */ \
322 template< class ugmisc__T > \
323 static constexpr bool has_member \
324 = ugmisc_priv::template member<ugmisc__T>::has_member; \
325 template< class ugmisc__T > \
326 using member_type = typename ugmisc_priv::member<ugmisc__T>::member_type; \
327 template< class ugmisc__T> \
328 static constexpr auto& get(ugmisc__T&& obj) { \
329 return ugmisc_priv::template member<ugmisc__T>::get(obj); \
330 } \
331 \
332 /* Type. */ \
333 \
334 template< class ugmisc__T > using type = typename ugmisc_priv::GetType<ugmisc__T>::type; \
335 template< class ugmisc__T > using safe_type = \
336 typename ugmisc_priv::GetType<ugmisc__T>::safe_type; \
337 template< class ugmisc__T > static constexpr bool has_member_type = \
338 ugmisc_priv::template GetType<ugmisc__T>::has_member_type; \
339 }; \
340}
341
342
343
344
345namespace ugmisc {
346
347
348
349
353template<class T>
355 using type = T;
356};
357
358
359
360
362namespace member_ {
363
364
365template<class V, class F, class...A>
366struct can_simple_call : public std::false_type {};
367
368template<class F, class...A>
369struct can_simple_call< std::void_t<decltype(std::declval<F>()(std::declval<A>()...))>, F, A...> : public std::true_type {};
370
371template<class F, class...A>
372constexpr bool can_simple_call_v = can_simple_call<void, F, A...>::value;
373
374
375/*
376 * In C++17, we prefer the "direct" way to invoke a callable when it isn't a
377 * pointer to member, because std::invoke is not yet constexpr.
378 */
379template<class F, class...A>
380constexpr auto invoke(F&& func, A&&...args)
381-> std::invoke_result_t<F, A...>
382{
383#ifdef UGMISC_CXX_20
384 // C++20 std::invoke is constexpr.
385 return std::invoke(std::forward<F>(func), std::forward<A>(args)...);
386#else
387 // Don't actually try to implement constexpr std::invoke here.
388 // This isn't a replacement STL.
389 if constexpr ( can_simple_call_v<F, A...> ) {
390 return std::forward<F>(func)(std::forward<A>(args)...);
391 } else {
392 return std::invoke(std::forward<F>(func), std::forward<A>(args)...);
393 }
394#endif
395}
396
397
398template<class T>
399struct get_is_default_type : public std::false_type {};
400
401template<class T>
402struct get_is_default_type< default_type<T> > : public std::true_type {};
403
404template<class T>
405struct get_is_not_default_type {
406 using type = bool;
407 static constexpr bool value = !get_is_default_type<T>::value;
408};
409
410template<class T> static inline bool is_default_type =
411 get_is_default_type<T>::value;
412
413
414
415
436template<class StaticCaller, class Functor>
437class fallback_caller {
438 Functor m_functor;
439
440 /*
441 * The use of Dummy* for the first override and Dummy for the second, makes
442 * the first more specific.
443 */
444 template<class Dummy, class...T>
445 constexpr auto forward(Dummy*, T&&...args) const
446 -> decltype(invoke(m_functor, std::forward<T>(args)...))
447 {
448 return invoke(m_functor, std::forward<T>(args)...);
449 }
450
451 template<class Dummy, class...T>
452 constexpr decltype(auto) forward(Dummy, T&&...args) const {
453 return m_functor();
454 }
455
456
457public:
458 constexpr fallback_caller(Functor f) : m_functor(std::forward<Functor>(f)) {}
459
460 fallback_caller(fallback_caller&&) = default;
461 fallback_caller(const fallback_caller&) = delete;
462
463 template<class...T>
464 constexpr decltype(auto) call(T&&...args) const {
465 if constexpr ( StaticCaller::template is_matched_v<T...> ) {
466 return StaticCaller::call(std::forward<T>(args)...);
467 } else {
468 auto dummy = (int*)nullptr;
469 return forward(dummy, std::forward<T>(args)...);
470 }
471 }
472
473 template<class...T>
474 constexpr decltype(auto) operator()(T&&...args) const {
475 return call(std::forward<T>(args)...);
476 }
477};
478
479
480template<class Access, class T, class...A>
481inline constexpr bool is_static_callable_v =
482 Access::ugmisc::template is_static_callable<T, A...>;
483
484
485template<class Access, class T, class...A>
486constexpr bool is_static_callable(A&&...) {
487 return is_static_callable_v<Access, T, A...>;
488}
489
490
491} /* member_ (::ugmisc::member_) */
493
494
495
572template<class Access, class...T>
574private:
575 template<class V, class List, class...Args>
576 struct caller
577 : public caller<V, type_list_remove_prefix<1, List>, Args...> {
578 };
579
580 template<class...Args> struct caller<void, type_list<>, Args...> {
581 static constexpr bool is_callable = false;
582 using safe_type = void;
583 };
584
585 template<class List, class...Args>
586 struct caller<
587 std::enable_if_t< member_::is_static_callable_v<Access, type_list_member<0, List>, Args...>>,
588 List,
589 Args...
590 >
591 {
592 static constexpr bool is_callable = true;
593 using type = type_list_member<0, List>;
594 using safe_type = void;
595 };
596
597 using types_list = flatten_t<T...>;
598
599public:
605 template<class...Args>
606 static constexpr bool is_matched_v =
607 caller<void, types_list, Args...>::is_callable;
608
612 template<class...Args>
613 static constexpr bool is_matched(Args&&...) {
614 return is_matched_v<Args...>;
615 }
616
621 template<class...Args>
622 using matched_type = typename caller<void, types_list, Args...>::type;
623
630 template<class...Args>
632 typename caller<void, types_list, Args...>::safe_type;
633
643 template<class...Args>
644 static constexpr decltype(auto) call(Args&&...args) {
645 using type = matched_type<Args...>;
646 static_assert(
647 is_matched_v<Args...>,
648 "None of the types is callable with these argument types."
649 );
650 return Access::ugmisc::template static_call<type>(std::forward<Args>(args)...);
651 }
652
658 template<class...Args>
659 constexpr decltype(auto) operator()(Args&&...args) const {
660 return call(std::forward<Args>(args)...);
661 }
662
668 template<class F>
669 static constexpr
670 member_::fallback_caller<static_member_caller, F>
671 fallback(F&& functor) {
672 return functor;
673 }
674
679 template<class...Args>
680 static matched_type<Args...> declval_matched(Args&&...) noexcept;
681
686 template<class...Args>
687 static matched_type<Args...> declval_matched() noexcept;
688
694 template<class...Args>
695 static safe_matched_type<Args...> safe_declval_matched(Args&&...) noexcept;
696
700 template<class...Args>
701 static safe_matched_type<Args...> safe_declval_matched() noexcept;
702};
703
704
705
706
751template<class Access>
753private:
754 /*
755 * A wrapped objects instance contains references to objects.
756 * It only exists briefly. It is instantiated explicitly, because we want
757 * to know which objects were originally lvalues (those will be passed as
758 * reference types).
759 * However we always store references to the objects.
760 */
761 template<class...T>
762 struct wrapped_objects_base {
763 protected:
764 static_assert(
765 sizeof...(T) == 0,
766 "A specialisation should have been used instead of this template."
767 );
768
769 template<class...A> static constexpr bool is_matched_v = false;
770 template<class...A> static constexpr bool is_matched(A&&...) {
771 return false;
772 }
773
774 wrapped_objects_base() = default;
775 wrapped_objects_base(const wrapped_objects_base&) = default;
776
777 template<class...A>
778 constexpr void call(A&&...) const {
779 static_assert(false, "There are no more objects to call.");
780 }
781
782 static constexpr bool safe_refs = true;
783
784 template<class...A>
785 static constexpr bool matched_here = false;
786 };
787
788 template<class T1, class...T>
789 struct wrapped_objects_base<T1, T...> : public wrapped_objects_base<T...>
790 {
791 private:
792 T1& obj_ref;
793
794 protected:
795 static constexpr bool safe_refs =
796 std::is_lvalue_reference_v<T1> &&
797 wrapped_objects_base<T...>::safe_refs;
798 ;
799
800 template<class...A>
801 static constexpr bool matched_here =
802 Access::ugmisc::template is_callable<T1, A...>;
803
804 template<class...A>
805 constexpr decltype(auto) call(A&&...args) const {
806 if constexpr ( matched_here<A...> ) {
807 return Access::ugmisc::template call(
808 obj_ref,
809 std::forward<A>(args)...
810 );
811 } else {
812 return this->wrapped_objects_base<T...>::call(std::forward<A>(args)...);
813 }
814 }
815
816 public:
817 constexpr wrapped_objects_base(T1& obj, T&...t)
818 : wrapped_objects_base<T...>(t...), obj_ref(obj)
819 {}
820
821 wrapped_objects_base(wrapped_objects_base&&) = default;
822
823 constexpr wrapped_objects_base(const wrapped_objects_base& other)
824 : wrapped_objects_base<T...>((wrapped_objects_base<T...>)other),
825 obj_ref(other.obj_ref)
826 {
827 static_assert(
828 safe_refs,
829 "Can't copy a member_caller::wrapped_objects if the "
830 "referenced objects aren't all lvalues."
831 );
832 }
833
834 template<class...A>
835 static constexpr bool is_matched_v =
836 matched_here<A...> ||
837 wrapped_objects_base<T...>::template matched_here<A...>;
838
839 template<class...A>
840 static constexpr bool is_matched(A&&...) {
841 return is_matched_v<A...>;
842 }
843 };
844
845 /*
846 * wrapped_objects with a functor is like wrapped_objects without one, but
847 * it will default to calling the functor.
848 */
849 template<class F, class...T>
850 class wrapped_objects : public wrapped_objects<void, T...> {
851 F functor;
852 using wrapped_objects<void, T...>::wrapped_objects;
853
854 template<class, class...A> struct forwarder {
855 static constexpr decltype(auto) call(F& f, A&...args) {
856 return f();
857 }
858 };
859
860 template<class...A>
861 struct forwarder<
862 std::void_t< decltype(member_::invoke(std::declval<F&>(), std::declval<A>()...)) >,
863 A...
864 >
865 {
866 static constexpr decltype(auto) call(F& f, A&...args) {
867 return member_::invoke(f, std::forward<A>(args)...);
868 }
869 };
870
871 template<class...A>
872 constexpr decltype(auto) call_default(A&&...args) {
873 return forwarder<void, A...>::call(functor, args...);
874 }
875
876 public:
877 constexpr wrapped_objects(F f, T...t)
878 : wrapped_objects<void, T...>(t...),
879 functor(std::forward<F>(f))
880 {}
881
882 template<class...A>
883 constexpr decltype(auto) operator()(A&&...args) {
884 if constexpr ( wrapped_objects::template is_matched_v<A...> ) {
885 return this->call( std::forward<A>(args)... );
886 } else {
887 return call_default(std::forward<A>(args)...);
888 }
889 }
890 };
891
892 template<class...T>
893 class wrapped_objects<void, T...> : public wrapped_objects_base<T...> {
894 public:
895 using wrapped_objects_base<T...>::wrapped_objects_base;
896
897 template<class...A>
898 constexpr decltype(auto) operator() (A&&...args) const {
899 return
900 this->call(std::forward<A>(args)...);
901 }
902 };
903
904 /*
905 * This only has the job of creating a wrapped_objects.
906 * It is intended to be a short lived object. Where possible it is
907 * instanciated with a reference type, so the member functor is declared as
908 * a reference.
909 */
910 template<class F>
911 class default_function {
912 F functor;
913 public:
914 constexpr default_function(F f) : functor(std::forward<F>(f)) {}
915 default_function(default_function&&) = default;
916 default_function(const default_function&) = delete;
917
918 template<class...T>
919 constexpr wrapped_objects<F, T...> operator() (T&&...objs) const {
920 return {functor, objs...};
921 }
922 };
923
924public:
925 /*
926 * The public methods are static because member_caller itself holds all its
927 * defining characteristics in its type.
928 */
929
934 template<class F>
935 static constexpr default_function<F> fallback(F&& f) { return f; }
936
940 template<class...T>
941 static constexpr wrapped_objects<void, T...> with(T&&...objs) {
942 return {objs...};
943 }
944
945 template<class...T>
946 constexpr auto operator() (T&&...objs) const { return with<T...>(objs...); }
947};
948
949
950
951
960
961
962
963
1051template<class Access, member_value_copy CopyValue = MEMBER_COPY_AUTO>
1053 static_assert(
1054 (CopyValue == MEMBER_COPY_AUTO) ||
1055 (CopyValue == MEMBER_COPY_ALWAYS) ||
1056 (CopyValue == MEMBER_COPY_NEVER)
1057 );
1058private:
1059 struct empty {
1060
1061 template<class F, class...A>
1062 constexpr decltype(auto) get(F&& func, A&&...args) const {
1063 /*
1064 * We could get away with directly using std::invoke here but it
1065 * already works and I don't want to touch it.
1066 */
1067 return member_::invoke(
1068 std::forward<F>(func), std::forward<A>(args)...
1069 );
1070 }
1071
1072 static constexpr bool has_member = false;
1073
1074 constexpr void get() const {
1075 static_assert(
1076 false,
1077 "Can't get member that was not found in any object."
1078 );
1079 }
1080
1081 template<class...T>
1082 constexpr decltype(auto) operator() (T&&...args) const {
1083 return get(std::forward<T>(args)...);
1084 }
1085 };
1086
1087 template<class T>
1088 class wrapped_object_member {
1089 using object_type = T;
1090
1091 // The actual type of the member, as declared in T's definition.
1092 using member_type = typename Access::ugmisc::template member_type<T>;
1093
1094 using effective_member_type =
1095 decltype(Access::ugmisc::get(std::declval<T&>()));
1096
1097 using deref_effective_member_type =
1098 std::remove_reference_t<effective_member_type>;
1099
1100 static constexpr bool rvalue_object =
1101 !std::is_lvalue_reference_v<object_type>;
1102
1103 static constexpr bool const_object =
1104 std::is_const_v<object_type>;
1105
1106 static constexpr bool volatile_object =
1107 std::is_volatile_v<object_type>;
1108
1109 static constexpr bool member_is_reference =
1110 std::is_lvalue_reference_v<member_type>;
1111
1112 static constexpr bool volatile_member =
1113 std::is_volatile_v<deref_effective_member_type>;
1114
1115 static constexpr bool const_member =
1116 std::is_const_v<deref_effective_member_type>;
1117
1118 static constexpr bool rvalue_member =
1119 (! member_is_reference) && rvalue_object;
1120
1121 /*
1122 * copy_member == true: the member value is copied into the wrapper,
1123 * and returned by copy from get().
1124 */
1125 static constexpr bool copy_member =
1126 (CopyValue == MEMBER_COPY_ALWAYS)
1127 || (
1128 (CopyValue != MEMBER_COPY_NEVER)
1129 && (!volatile_member)
1130 && (const_member || rvalue_member)
1131 && std::is_trivially_copyable_v< member_type >
1132 );
1133
1134 /*
1135 * return rvalue_member == true: a reference to the member is stored,
1136 * and an rvalue reference is returned from get().
1137 */
1138 static constexpr bool return_rvalue_member =
1139 (!copy_member) && rvalue_member;
1140
1141 static constexpr bool return_lvalue_member =
1142 (!return_rvalue_member || copy_member);
1143
1144 using member_store_type = std::conditional_t<
1145 copy_member,
1146 const std::remove_volatile_t<deref_effective_member_type>,
1147 effective_member_type
1148 >;
1149
1150 using member_return_type = std::conditional_t<
1151 copy_member,
1152 std::remove_const_t<member_store_type>,
1153 std::conditional_t<
1154 return_rvalue_member,
1155 deref_effective_member_type&&,
1156 deref_effective_member_type&
1157 >
1158 >;
1159
1160 // This may be a copy of the member's value where that is safe to do.
1161 // Otherwise it is a reference to the member.
1162 member_store_type member;
1163 public:
1164 static constexpr bool has_member = true;
1165
1166 constexpr wrapped_object_member( object_type& obj )
1167 : member( Access::ugmisc::get(obj) )
1168 {}
1169
1170 wrapped_object_member(const wrapped_object_member&) = default;
1171 wrapped_object_member(wrapped_object_member&&) = default;
1172
1173 template<class...A>
1174 constexpr member_return_type get(A&&...args) const { return member; }
1175
1176 template<class...A>
1177 constexpr member_return_type operator()(A&&...args) const { return member; }
1178 };
1179
1180 static constexpr empty find_ref() { return {}; }
1181
1182 template<class T1, class...T>
1183 static constexpr auto find_ref(T1&& obj, T&&...objs) {
1184 if constexpr ( Access::ugmisc::template has_member<T1> ) {
1185 return wrapped_object_member<T1>{ obj };
1186 } else {
1187 return find_ref(std::forward<T>(objs)...);
1188 }
1189 }
1190
1191public:
1192 /*
1193 * The public methods are static because member_value itself holds all its
1194 * defining characteristics in its type.
1195 */
1196
1200 template<class...T>
1201 static constexpr auto with(T&&...objs) {
1202 return find_ref(std::forward<T>(objs)...);
1203 }
1204
1205 template<class...T>
1206 constexpr auto operator() (T&&...objs) const { return with<T...>(objs...); }
1207
1221
1231
1241};
1242
1243
1244
1245
1247namespace member_ {
1252template< class Access, class TypeList > class member_type_access_traits {
1253 using all_types = TypeList;
1254 using normal_types =
1255 filter_type_list<member_::get_is_default_type, all_types, invert_test>;
1256 using default_types =
1257 filter_type_list<member_::get_is_default_type, all_types>;
1258
1259 static_assert( default_types::size <= 1 );
1260 static_assert( default_types::size + normal_types::size == all_types::size );
1261
1262 using access = Access;
1263
1264 template<bool Found, class U> struct result {
1265 static constexpr bool found = Found;
1266 using type = U;
1267 };
1268
1269 /*
1270 * This allows us to avoid instantiating type_list_member<0, seq> where seq
1271 * is empty.
1272 */
1273 template< class seq = normal_types >
1274 static constexpr auto find_type_pair() {
1275
1276 if constexpr ( seq::size == 0 ) {
1277 return result<false, void>{};
1278 } else {
1279 using first_type = type_list_member<0, seq>;
1280 if constexpr ( access::ugmisc::template has_member_type<first_type> ) {
1281 return result<
1282 true,
1283 typename access::ugmisc::template type<first_type>
1284 >{};
1285 } else {
1286 return find_type_pair< type_list_remove_prefix<1, seq> >();
1287 }
1288 }
1289 }
1290
1291 using result_t = decltype(find_type_pair());
1292
1293 /*
1294 * This allows us to avoid instantiating type_list_member<default_types>
1295 * when default_types is empty.
1296 */
1297 static constexpr auto find_default_type_pair() {
1298 if constexpr ( default_types::size ) {
1299 return result<true, type_list_member<0, default_types>>{};
1300 } else {
1301 return result<false, void>{};
1302 }
1303 }
1304
1305 using default_t = decltype(find_default_type_pair());
1306
1307
1308public:
1309 static constexpr bool has_member = result_t::found;
1310 static constexpr bool has_default = default_t::found;
1311 static constexpr bool has_type = has_member || has_default;
1312 using type = std::conditional_t<
1313 has_member,
1314 typename result_t::type,
1315 typename default_t::type
1316 >;
1317};
1318
1319
1320
1321
1326template<
1327 class Access,
1328 class TypeList,
1329 bool HasType = member_type_access_traits<Access, TypeList>::has_type
1330 >
1331struct member_type_access {
1332 static_assert( ! HasType, "There is a missing specialisation." );
1333 static constexpr bool has_member = false;
1334 static constexpr bool has_default = false;
1335 static constexpr bool has_type = false;
1336 // Don't declare type!
1337 using safe_type = void;
1338};
1339
1340
1341template<class Access, class TypeList>
1342struct member_type_access<Access, TypeList, true> {
1343 static constexpr bool has_member
1344 = member_type_access_traits<Access, TypeList>::has_member;
1345
1346 static constexpr bool has_default
1347 = member_type_access_traits<Access, TypeList>::has_default;
1348
1349 static constexpr bool has_type = true;
1350
1351 using type
1352 = typename member_type_access_traits<Access, TypeList>::type;
1353
1354 using safe_type = type;
1355};
1356
1357
1358
1359
1360template<class Access, class...T>
1361class static_member_value_base {
1362 template<class U> struct access {
1363 using type = U;
1364 static constexpr bool has_member = Access::ugmisc::template has_static_member<U>;
1365 static constexpr auto& get() { return Access::ugmisc::template static_get<U>(); }
1366 };
1367
1368 struct void_wrapper {
1369 using type = void;
1370 };
1371
1372 template<bool Found, class Type=void_wrapper>
1373 struct result {
1374 using type = Type;
1375 static constexpr bool found = Found;
1376 };
1377
1378 using accessor_types = flatten_apply_each_class<access, T...>;
1379
1380 template<class accessors = accessor_types>
1381 static constexpr auto find_matching_type() {
1382 if constexpr ( accessors::size == 0 ) {
1383 return result<false>{};
1384 } else {
1385 using first_type = type_list_member<0, accessors>;
1386 if constexpr ( first_type::has_member ) {
1387 return result<true, first_type>{};
1388 } else {
1389 return find_matching_type<type_list_remove_prefix<1, accessors>>();
1390 }
1391 }
1392 }
1393
1394 using result_type = decltype(find_matching_type());
1395 using getter_type = typename result_type::type;
1396
1397public:
1398 static constexpr bool has_member = result_type::found;
1399
1400 template<class F, class...A>
1401 static constexpr decltype(auto) get(F&& default_get, A&&...args) {
1402 if constexpr ( has_member ) {
1403 return getter_type::get();
1404 } else {
1405 return default_get(std::forward<A>(args)...);
1406 }
1407 }
1408
1409 static constexpr auto& get() { return getter_type::get(); }
1410
1411 using safe_matched_type = typename getter_type::type;
1412};
1413
1414
1415template<bool WithType, class Access, class...T>
1416struct static_member_value;
1417
1418template<class Access, class...T>
1419struct static_member_value<true, Access, T...>
1420: public static_member_value_base<Access, T...>
1421{
1422 using matched_type =
1423 typename static_member_value_base<Access, T...>::safe_matched_type;
1424};
1425
1426template<class Access, class...T>
1427struct static_member_value<false, Access, T...>
1428: public static_member_value_base<Access, T...>
1429{ };
1430
1431
1432template<class Access, class...T>
1433using static_member_value_t =
1434 static_member_value<
1435 static_member_value_base<Access, T...>::has_member,
1436 Access, T...
1437 >;
1438
1439
1440} /* member_ (::ugmisc::member_) */
1442
1443
1444
1445#ifndef UGMISC_DOCS
1446template< class Access, class...T > class member_type_access
1447: public member_::member_type_access<Access, flatten_t<T...>>
1448{
1449};
1450#else
1451
1487template< class Access, class...T > struct member_type_access {
1492 static constexpr bool has_member = ???;
1493
1499 static constexpr bool has_default = ???;
1500
1505 static constexpr bool has_type = has_member || has_default;
1506
1518 using type = ???;
1519
1524 using safe_type = ???;
1525};
1526#endif
1527
1528
1529
1530
1568template<class Access, class...T>
1569struct static_member_value : public member_::static_member_value_t<Access, T...>
1570{
1571#ifdef UGMISC_DOCS
1576 static constexpr bool has_member = ???;
1577
1588 template<class F, class...A>
1589 static constexpr decltype(auto) get(F&& default_get, A&&...args);
1590
1596 static constexpr auto& get() { return getter_type::get(); }
1597
1605
1611 using matched_type = ???;
1612#endif
1613};
1614
1615
1616
1617
1618}
1619#endif /* UGMISC_MEMBER_HPP */
member_value_copy
Definition member.hpp:955
@ MEMBER_COPY_NEVER
Always take a reference to the original object.
Definition member.hpp:957
@ MEMBER_COPY_AUTO
Default. Copy value if safe and trivial to do so.
Definition member.hpp:956
@ MEMBER_COPY_ALWAYS
Always copy the value.
Definition member.hpp:958
static constexpr wrapped_objects< void, T... > with(T &&...objs)
Definition member.hpp:941
static constexpr default_function< F > fallback(F &&f)
Definition member.hpp:935
static constexpr bool has_member
Definition member.hpp:1492
static constexpr bool has_default
Definition member.hpp:1499
static constexpr bool has_type
Definition member.hpp:1505
static constexpr auto with(T &&...objs)
Definition member.hpp:1201
static constexpr member_value< Access, MEMBER_COPY_AUTO > copy_auto
Definition member.hpp:1220
static constexpr member_value< Access, MEMBER_COPY_ALWAYS > copy_always
Definition member.hpp:1230
static constexpr member_value< Access, MEMBER_COPY_NEVER > copy_never
Definition member.hpp:1240
constexpr decltype(auto) operator()(Args &&...args) const
Definition member.hpp:659
typename caller< void, types_list, Args... >::safe_type safe_matched_type
Definition member.hpp:631
static constexpr bool is_matched(Args &&...)
Definition member.hpp:613
static matched_type< Args... > declval_matched(Args &&...) noexcept
static constexpr bool is_matched_v
Definition member.hpp:606
static constexpr decltype(auto) call(Args &&...args)
Definition member.hpp:644
typename caller< void, types_list, Args... >::type matched_type
Definition member.hpp:622
static safe_matched_type< Args... > safe_declval_matched(Args &&...) noexcept
static constexpr member_::fallback_caller< static_member_caller, F > fallback(F &&functor)
Definition member.hpp:671
static constexpr decltype(auto) get(F &&default_get, A &&...args)
static constexpr auto & get()
Definition member.hpp:1596
static constexpr bool has_member
Definition member.hpp:1576
Encapsulates a list of types.
Definition typelist.hpp:860
Lists of types which may be used in some of the ugmisc templates where a single type would usually be...
typename flatten< T... >::type flatten_t
Converts the template parameters into a type_list.
Definition typelist.hpp:239
type_list_apply_each_class< F, flatten_t< T... > > flatten_apply_each_class
Definition typelist.hpp:411
typename typelist_::get_type_list_member< I, T >::type type_list_member
Definition typelist.hpp:195