UgMisc 0.2-128
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
123
124#include "ugmisc/typelist.hpp"
125#include <functional>
126#include <utility>
127
128
129
130
131#ifdef UGMISC_DOCS
132#error
133#endif
134
135
136
137
148#define UGMISC_DECL_MEMBER_ACCESS(TNAME, NAME) \
149class TNAME { \
150 struct ugmisc_priv { \
151 /* Static method call support. */ \
152 \
153 template<class T, class, class...A> \
154 struct static_caller_as_function { \
155 static constexpr bool is_callable = false; \
156 }; \
157 \
158 template<class T, class...A> \
159 struct static_caller_as_function<T, std::void_t<decltype(T::NAME(std::declval<A>()...))>, A...> { \
160 static constexpr bool is_callable = true; \
161 \
162 static constexpr decltype(auto) call(A...args) { \
163 return T::NAME(std::forward<A>(args)...); \
164 } \
165 }; \
166 \
167 template<class T, bool, class...A> \
168 struct static_caller_as_invoke { \
169 static constexpr bool is_callable = false; \
170 }; \
171 \
172 template<class T, class...A> \
173 struct static_caller_as_invoke<T, std::is_invocable_v<decltype((T::NAME)), A...>, A...> { \
174 static constexpr bool is_callable = true; \
175 \
176 static constexpr decltype(auto) call(A...args) { \
177 return std::invoke(T::NAME, std::forward<A>(args)...); \
178 } \
179 }; \
180 \
181 template<class T, class...A> \
182 using static_caller = std::conditional_t< \
183 static_caller_as_function<T, void, A...>::is_callable, \
184 static_caller_as_function<T, void, A...>, \
185 static_caller_as_invoke<T, true, A...> \
186 >; \
187 \
188 /* Non static method call support. */ \
189 \
190 template<class T, class, class...A> \
191 struct caller_as_method { \
192 static constexpr bool is_callable = false; \
193 }; \
194 \
195 template<class T, class...A> \
196 struct caller_as_method<T, std::void_t<decltype(std::declval<T>().NAME(std::declval<A>()...))>, A...> { \
197 static constexpr bool is_callable = true; \
198 \
199 static constexpr decltype(auto) call(T& obj, A...args) { \
200 return std::forward<T>(obj).NAME(std::forward<A>(args)...); \
201 } \
202 }; \
203 \
204 template<class T, bool I, class...A> \
205 struct caller_as_invoke { \
206 static constexpr bool is_callable = false; \
207 }; \
208 \
209 template<class T, class...A> \
210 struct caller_as_invoke<T, \
211 std::is_invocable_v<decltype(std::declval<T>().NAME), A...>, \
212 A... \
213 > \
214 { \
215 static constexpr bool is_callable = true; \
216 static constexpr decltype(auto) call(T& obj, A...args) { \
217 return std::invoke(std::forward<T>(obj).NAME, std::forward<A>(args)...); \
218 } \
219 }; \
220 \
221 template<class T, class...A> \
222 using caller = std::conditional_t< \
223 caller_as_method<T, void, A...>::is_callable, \
224 caller_as_method<T, void, A...>, \
225 caller_as_invoke<T, true, A...> \
226 >; \
227 /* Static data reference support. */ \
228 \
229 template<class T, class=void> \
230 struct static_member { \
231 static constexpr bool has_member = false; \
232 }; \
233 \
234 template<class T> \
235 struct static_member<T, std::void_t<decltype(T::NAME)>> { \
236 static constexpr bool has_member = true; \
237 \
238 static constexpr auto& get() { \
239 return T::NAME; \
240 } \
241 }; \
242 \
243 /* Non static data reference support. */ \
244 \
245 template<class T, class=void> \
246 struct member { \
247 static constexpr bool has_member = false; \
248 }; \
249 \
250 template<class T> \
251 struct member<T, std::void_t<decltype(std::declval<T>().NAME)>> { \
252 static constexpr bool has_member = true; \
253 \
254 static constexpr auto& get(T& obj) { \
255 return obj.NAME; \
256 } \
257 \
258 using member_type = decltype(std::declval<std::remove_reference_t<T>>().NAME); \
259 }; \
260 /* Type support. */ \
261 \
262 template< class T, class=void > struct GetType { \
263 static constexpr bool has_member_type = false; \
264 using safe_type = void; \
265 }; \
266 \
267 template< class T > struct GetType< T, std::void_t< typename T::NAME > > { \
268 static constexpr bool has_member_type = true; \
269 using safe_type = typename T::NAME; \
270 using type = typename T::NAME; \
271 }; \
272 \
273 }; \
274public: \
275 struct ugmisc { \
276 /* Static method call. */ \
277 template<class T, class...A> static constexpr bool is_static_callable = \
278 ugmisc_priv::static_caller<T, A...>::is_callable; \
279 \
280 template<class T, class...A> \
281 static constexpr auto static_call(A&&...args) \
282 -> decltype( \
283 ugmisc_priv::static_caller<T, A...>::call( \
284 std::forward<A>(args)... \
285 )) \
286 { \
287 return ugmisc_priv::static_caller<T, A...>::call( \
288 std::forward<A>(args)... \
289 ); \
290 } \
291 \
292 /* Non static method call. */ \
293 template<class T, class...A> static constexpr bool is_callable = \
294 ugmisc_priv::caller<T, A...>::is_callable; \
295 \
296 template<class T, class...A> \
297 static constexpr auto call(T& obj, A&&...args) \
298 -> decltype( \
299 ugmisc_priv::caller<T, A...>::call( \
300 obj, \
301 std::forward<A>(args)... \
302 )) \
303 { \
304 return ugmisc_priv::caller<T, A...>::call( \
305 obj, \
306 std::forward<A>(args)... \
307 ); \
308 } \
309 \
310 /* Static member reference. */ \
311 template< class T > \
312 static constexpr bool has_static_member \
313 = ugmisc_priv::static_member<T>::has_member; \
314 template< class T> \
315 static constexpr auto& static_get() { \
316 return ugmisc_priv::static_member<T>::get(); \
317 } \
318 \
319 /* Non static member reference. */ \
320 template< class T > \
321 static constexpr bool has_member \
322 = ugmisc_priv::member<T>::has_member; \
323 template< class T > \
324 using member_type = typename ugmisc_priv::member<T>::member_type; \
325 template< class T> \
326 static constexpr auto& get(T&& obj) { \
327 return ugmisc_priv::member<T>::get(obj); \
328 } \
329 \
330 /* Type. */ \
331 \
332 template< class T > using type = typename ugmisc_priv::GetType<T>::type; \
333 template< class T > using safe_type = \
334 typename ugmisc_priv::GetType<T>::safe_type; \
335 template< class T > static constexpr bool has_member_type = \
336 ugmisc_priv::GetType<T>::has_member_type; \
337 }; \
338}
339
340
341
342
343namespace ugmisc {
344
345
346
347
351template<class T>
353 using type = T;
354};
355
356
357
358
360namespace member_ {
361
362
363template<class V, class F, class...A>
364struct can_simple_call : public std::false_type {};
365
366template<class F, class...A>
367struct can_simple_call< std::void_t<decltype(std::declval<F>()(std::declval<A>()...))>, F, A...> : public std::true_type {};
368
369template<class F, class...A>
370constexpr bool can_simple_call_v = can_simple_call<void, F, A...>::value;
371
372
373/*
374 * In C++17, we prefer the "direct" way to invoke a callable when it isn't a
375 * pointer to member, because std::invoke is not yet constexpr.
376 */
377template<class F, class...A>
378constexpr auto invoke(F&& func, A&&...args)
379-> std::invoke_result_t<F, A...>
380{
381#ifdef UGMISC_CXX_20
382 // C++20 std::invoke is constexpr.
383 return std::invoke(std::forward<F>(func), std::forward<A>(args)...);
384#else
385 // Don't actually try to implement constexpr std::invoke here.
386 // This isn't a replacement STL.
387 if constexpr ( can_simple_call_v<F, A...> ) {
388 return std::forward<F>(func)(std::forward<A>(args)...);
389 } else {
390 return std::invoke(std::forward<F>(func), std::forward<A>(args)...);
391 }
392#endif
393}
394
395
396template<class T>
397struct get_is_default_type : public std::false_type {};
398
399template<class T>
400struct get_is_default_type< default_type<T> > : public std::true_type {};
401
402template<class T>
403struct get_is_not_default_type {
404 using type = bool;
405 static constexpr bool value = !get_is_default_type<T>::value;
406};
407
408template<class T> static inline bool is_default_type =
409 get_is_default_type<T>::value;
410
411
412
413
428template<class StaticCaller, class Functor>
429class fallback_caller {
430 Functor m_functor;
431
432 /*
433 * The use of Dummy* for the first override and Dummy for the second, makes
434 * the first more specific.
435 */
436 template<class Dummy, class...T>
437 constexpr auto forward(Dummy*, T&&...args) const
438 -> decltype(m_functor(std::forward<T>(args)...))
439 {
440 return invoke(m_functor, std::forward<T>(args)...);
441 }
442
443 template<class Dummy, class...T>
444 constexpr decltype(auto) forward(Dummy, T&&...args) const {
445 return m_functor();
446 }
447
448
449public:
450 constexpr fallback_caller(const Functor& f) : m_functor(f) {}
451 constexpr fallback_caller(Functor&& f) : m_functor(std::move(f)) {}
452
453 fallback_caller(fallback_caller&&) = default;
454 fallback_caller(const fallback_caller&) = delete;
455
456 template<class...T>
457 constexpr decltype(auto) call(T&&...args) const {
458 if constexpr ( StaticCaller::template is_matched_v<T...> ) {
459 return StaticCaller::call(std::forward<T>(args)...);
460 } else {
461 auto dummy = (int*)nullptr;
462 return forward(dummy, std::forward<T>(args)...);
463 }
464 }
465
466 template<class...T>
467 constexpr decltype(auto) operator()(T&&...args) const {
468 return call(std::forward<T>(args)...);
469 }
470};
471
472
473template<class Access, class T, class...A>
474inline constexpr bool is_static_callable_v =
475 Access::ugmisc::template is_static_callable<T, A...>;
476
477
478template<class Access, class T, class...A>
479constexpr bool is_static_callable(A&&...) {
480 return is_static_callable_v<Access, T, A...>;
481}
482
483
484} /* member_ (::ugmisc::member_) */
486
487
488
565template<class Access, class...T>
567private:
568 template<class V, class List, class...Args>
569 struct caller
570 : public caller<V, type_list_remove_prefix<1, List>, Args...> {
571 };
572
573 template<class...Args> struct caller<void, type_list<>, Args...> {
574 static constexpr bool is_callable = false;
575 using safe_type = void;
576 };
577
578 template<class List, class...Args>
579 struct caller<
580 std::enable_if_t< member_::is_static_callable_v<Access, type_list_member<0, List>, Args...>>,
581 List,
582 Args...
583 >
584 {
585 static constexpr bool is_callable = true;
586 using type = type_list_member<0, List>;
587 using safe_type = void;
588 };
589
590 using types_list = flatten_t<T...>;
591
592public:
598 template<class...Args>
599 static constexpr bool is_matched_v =
600 caller<void, types_list, Args...>::is_callable;
601
605 template<class...Args>
606 static constexpr bool is_matched(Args&&...) {
607 return is_matched_v<Args...>;
608 }
609
614 template<class...Args>
615 using matched_type = typename caller<void, types_list, Args...>::type;
616
623 template<class...Args>
624 using safe_matched_type =
625 typename caller<void, types_list, Args...>::safe_type;
626
636 template<class...Args>
637 static constexpr decltype(auto) call(Args&&...args) {
638 using type = matched_type<Args...>;
639 static_assert(
640 is_matched_v<Args...>,
641 "None of the types is callable with these argument types."
642 );
643 return Access::ugmisc::template static_call<type>(std::forward<Args>(args)...);
644 }
645
651 template<class...Args>
652 constexpr decltype(auto) operator()(Args&&...args) const {
653 return call(std::forward<Args>(args)...);
654 }
655
661 template<class F>
662 static constexpr
663 member_::fallback_caller<static_member_caller, std::remove_reference_t<F>>
664 fallback(F&& functor) {
665 return std::forward<F>(functor);
666 }
667
672 template<class...Args>
673 static matched_type<Args...> declval_matched(Args&&...) noexcept;
674
679 template<class...Args>
680 static matched_type<Args...> declval_matched() noexcept;
681
687 template<class...Args>
688 static safe_matched_type<Args...> safe_declval_matched(Args&&...) noexcept;
689
693 template<class...Args>
694 static safe_matched_type<Args...> safe_declval_matched() noexcept;
695};
696
697
698
699
744template<class Access>
746private:
747 /*
748 * A wrapped objects instance contains references to objects.
749 * It only exists briefly. It is instantiated explicitly, because we want
750 * to know which objects were originally lvalues (those will be passed as
751 * reference types).
752 * However we always store references to the objects.
753 */
754 template<class...T>
755 struct wrapped_objects_base {
756 protected:
757 static_assert(
758 sizeof...(T) == 0,
759 "A specialisation should have been used instead of this template."
760 );
761
762 template<class...A> static constexpr bool is_matched_v = false;
763 template<class...A> static constexpr bool is_matched(A&&...) {
764 return false;
765 }
766
767 wrapped_objects_base() = default;
768 wrapped_objects_base(const wrapped_objects_base&) = default;
769
770 template<class...A>
771 constexpr void call(A&&...) const {
772 static_assert(false, "There are no more objects to call.");
773 }
774
775 static constexpr bool safe_refs = true;
776
777 template<class...A>
778 static constexpr bool matched_here = false;
779 };
780
781 template<class T1, class...T>
782 struct wrapped_objects_base<T1, T...> : public wrapped_objects_base<T...>
783 {
784 private:
785 T1& obj_ref;
786
787 protected:
788 static constexpr bool safe_refs =
789 std::is_lvalue_reference_v<T1> &&
790 wrapped_objects_base<T...>::safe_refs;
791 ;
792
793 template<class...A>
794 static constexpr bool matched_here =
795 Access::ugmisc::template is_callable<T1, A...>;
796
797 template<class...A>
798 constexpr decltype(auto) call(A&&...args) const {
799 if constexpr ( matched_here<A...> ) {
800 return Access::ugmisc::template call(
801 obj_ref,
802 std::forward<A>(args)...
803 );
804 } else {
805 return this->wrapped_objects_base<T...>::call(std::forward<A>(args)...);
806 }
807 }
808
809 public:
810 constexpr wrapped_objects_base(T1& obj, T&...t)
811 : wrapped_objects_base<T...>(t...), obj_ref(obj)
812 {}
813
814 wrapped_objects_base(wrapped_objects_base&&) = default;
815
816 constexpr wrapped_objects_base(const wrapped_objects_base& other)
817 : wrapped_objects_base<T...>((wrapped_objects_base<T...>)other),
818 obj_ref(other.obj_ref)
819 {
820 static_assert(
821 safe_refs,
822 "Can't copy a member_caller::wrapped_objects if the "
823 "referenced objects aren't all lvalues."
824 );
825 }
826
827 template<class...A>
828 static constexpr bool is_matched_v =
829 matched_here<A...> ||
830 wrapped_objects_base<T...>::template matched_here<A...>;
831
832 template<class...A>
833 static constexpr bool is_matched(A&&...) {
834 return is_matched_v<A...>;
835 }
836 };
837
838 /*
839 * wrapped_objects with a functor is like wrapped_objects without one, but
840 * it will default to calling the functor.
841 */
842 template<class F, class...T>
843 class wrapped_objects : public wrapped_objects<void, T...> {
844 F functor;
845 using wrapped_objects<void, T...>::wrapped_objects;
846
847 template<class, class...A> struct forwarder {
848 static constexpr decltype(auto) call(F& f, A&...args) {
849 return f();
850 }
851 };
852
853 template<class...A>
854 struct forwarder<
855 std::void_t< decltype(member_::invoke(std::declval<F&>(), std::declval<A>()...)) >,
856 A...
857 >
858 {
859 static constexpr decltype(auto) call(F& f, A&...args) {
860 return member_::invoke(f, std::forward<A>(args)...);
861 }
862 };
863
864 template<class...A>
865 constexpr decltype(auto) call_default(A&&...args) const {
866 return forwarder<void, A...>::call(functor, args...);
867 }
868
869 public:
870 constexpr wrapped_objects(F& f, T...t)
871 : wrapped_objects<void, T...>(t...), functor(f)
872 {}
873
874 template<class...A>
875 constexpr decltype(auto) operator()(A&&...args) const {
876 if constexpr ( wrapped_objects::template is_matched_v<A...> ) {
877 return this->call( std::forward<A>(args)... );
878 } else {
879 return call_default(std::forward<A>(args)...);
880 }
881 }
882 };
883
884 template<class...T>
885 class wrapped_objects<void, T...> : public wrapped_objects_base<T...> {
886 public:
887 using wrapped_objects_base<T...>::wrapped_objects_base;
888
889 template<class...A>
890 constexpr decltype(auto) operator() (A&&...args) const {
891 return
892 this->call(std::forward<A>(args)...);
893 }
894 };
895
896 /*
897 * This only has the job of creating a wrapped_objects.
898 * It is intended to be a short lived object.
899 */
900 template<class F>
901 class default_function {
902 F& functor_ref;
903 public:
904 constexpr default_function(F& f) : functor_ref(f) {}
905 default_function(default_function&&) = default;
906 default_function(const default_function&) = delete;
907
908 template<class...T>
909 constexpr wrapped_objects<F, T...> operator() (T&&...objs) const {
910 return {functor_ref, objs...};
911 }
912 };
913
914public:
915 /*
916 * The public methods are static because member_caller itself holds all its
917 * defining characteristics in its type.
918 */
919
924 template<class F>
925 static constexpr default_function<F> fallback(F&& f) { return f; }
926
930 template<class...T>
931 static constexpr wrapped_objects<void, T...> with(T&&...objs) {
932 return {objs...};
933 }
934
935 template<class...T>
936 constexpr auto operator() (T&&...objs) const { return with<T...>(objs...); }
937};
938
939
940
941
950
951
952
953
1036template<class Access, member_value_copy CopyValue = MEMBER_COPY_AUTO>
1038 static_assert(
1039 (CopyValue == MEMBER_COPY_AUTO) ||
1040 (CopyValue == MEMBER_COPY_ALWAYS) ||
1041 (CopyValue == MEMBER_COPY_NEVER)
1042 );
1043private:
1044 struct empty {
1045 template<class F, class...A>
1046 constexpr decltype(auto) get(F&& func, A&&...args) const {
1047 return member_::invoke(
1048 std::forward<F>(func), std::forward<A>(args)...
1049 );
1050 }
1051
1052 static constexpr bool has_member = false;
1053
1054 constexpr void get() const {
1055 static_assert(
1056 false,
1057 "Can't get member that was not found in any object."
1058 );
1059 }
1060 };
1061
1062 template<class T>
1063 class wrapped_object_member {
1064 using object_type = T;
1065
1066 // The actual type of the member, as declared in T's definition.
1067 using member_type = typename Access::ugmisc::template member_type<T>;
1068
1069 using effective_member_type =
1070 decltype(Access::ugmisc::get(std::declval<T&>()));
1071
1072 using deref_effective_member_type =
1073 std::remove_reference_t<effective_member_type>;
1074
1075 static constexpr bool rvalue_object =
1076 !std::is_lvalue_reference_v<object_type>;
1077
1078 static constexpr bool const_object =
1079 std::is_const_v<object_type>;
1080
1081 static constexpr bool volatile_object =
1082 std::is_volatile_v<object_type>;
1083
1084 static constexpr bool member_is_reference =
1085 std::is_lvalue_reference_v<member_type>;
1086
1087 static constexpr bool volatile_member =
1088 std::is_volatile_v<deref_effective_member_type>;
1089
1090 static constexpr bool const_member =
1091 std::is_const_v<deref_effective_member_type>;
1092
1093 static constexpr bool rvalue_member =
1094 (! member_is_reference) && rvalue_object;
1095
1096 /*
1097 * copy_member == true: the member value is copied into the wrapper,
1098 * and returned by copy from get().
1099 */
1100 static constexpr bool copy_member =
1101 (CopyValue == MEMBER_COPY_ALWAYS)
1102 || (
1103 (CopyValue != MEMBER_COPY_NEVER)
1104 && (!volatile_member)
1105 && (const_member || rvalue_member)
1106 && std::is_trivially_copyable_v< member_type >
1107 );
1108
1109 /*
1110 * return rvalue_member == true: a reference to the member is stored,
1111 * and an rvalue reference is returned from get().
1112 */
1113 static constexpr bool return_rvalue_member =
1114 (!copy_member) && rvalue_member;
1115
1116 static constexpr bool return_lvalue_member =
1117 (!return_rvalue_member || copy_member);
1118
1119 using member_store_type = std::conditional_t<
1120 copy_member,
1121 const std::remove_volatile_t<deref_effective_member_type>,
1122 effective_member_type
1123 >;
1124
1125 using member_return_type = std::conditional_t<
1126 copy_member,
1127 std::remove_const_t<member_store_type>,
1128 std::conditional_t<
1129 return_rvalue_member,
1130 deref_effective_member_type&&,
1131 deref_effective_member_type&
1132 >
1133 >;
1134
1135 // This may be a copy of the member's value where that is safe to do.
1136 // Otherwise it is a reference to the member.
1137 member_store_type member;
1138 public:
1139 static constexpr bool has_member = true;
1140
1141 constexpr wrapped_object_member( object_type& obj )
1142 : member( Access::ugmisc::get(obj) )
1143 {}
1144
1145 wrapped_object_member(const wrapped_object_member&) = default;
1146 wrapped_object_member(wrapped_object_member&&) = default;
1147
1148 template<class...A>
1149 constexpr member_return_type get(A&&...args) const { return member; }
1150 };
1151
1152 static constexpr empty find_ref() { return {}; }
1153
1154 template<class T1, class...T>
1155 static constexpr auto find_ref(T1&& obj, T&&...objs) {
1156 if constexpr ( Access::ugmisc::template has_member<T1> ) {
1157 return wrapped_object_member<T1>{ obj };
1158 } else {
1159 return find_ref(std::forward<T>(objs)...);
1160 }
1161 }
1162
1163public:
1164 /*
1165 * The public methods are static because member_value itself holds all its
1166 * defining characteristics in its type.
1167 */
1168
1172 template<class...T>
1173 static constexpr auto with(T&&...objs) {
1174 return find_ref(std::forward<T>(objs)...);
1175 }
1176
1177 template<class...T>
1178 constexpr auto operator() (T&&...objs) const { return with<T...>(objs...); }
1179
1193
1203
1213};
1214
1215
1216
1217
1219namespace member_ {
1224template< class Access, class TypeList > class member_type_access_traits {
1225 using all_types = TypeList;
1226 using normal_types =
1227 filter_type_list<member_::get_is_default_type, all_types, invert_test>;
1228 using default_types =
1229 filter_type_list<member_::get_is_default_type, all_types>;
1230
1231 static_assert( default_types::size <= 1 );
1232 static_assert( default_types::size + normal_types::size == all_types::size );
1233
1234 using access = Access;
1235
1236 template<bool Found, class U> struct result {
1237 static constexpr bool found = Found;
1238 using type = U;
1239 };
1240
1241 /*
1242 * This allows us to avoid instantiating type_list_member<0, seq> where seq
1243 * is empty.
1244 */
1245 template< class seq = normal_types >
1246 static constexpr auto find_type_pair() {
1247
1248 if constexpr ( seq::size == 0 ) {
1249 return result<false, void>{};
1250 } else {
1251 using first_type = type_list_member<0, seq>;
1252 if constexpr ( access::ugmisc::template has_member_type<first_type> ) {
1253 return result<
1254 true,
1255 typename access::ugmisc::template type<first_type>
1256 >{};
1257 } else {
1258 return find_type_pair< type_list_remove_prefix<1, seq> >();
1259 }
1260 }
1261 }
1262
1263 using result_t = decltype(find_type_pair());
1264
1265 /*
1266 * This allows us to avoid instantiating type_list_member<default_types>
1267 * when default_types is empty.
1268 */
1269 static constexpr auto find_default_type_pair() {
1270 if constexpr ( default_types::size ) {
1271 return result<true, type_list_member<0, default_types>>{};
1272 } else {
1273 return result<false, void>{};
1274 }
1275 }
1276
1277 using default_t = decltype(find_default_type_pair());
1278
1279
1280public:
1281 static constexpr bool has_member = result_t::found;
1282 static constexpr bool has_default = default_t::found;
1283 static constexpr bool has_type = has_member || has_default;
1284 using type = std::conditional_t<
1285 has_member,
1286 typename result_t::type,
1287 typename default_t::type
1288 >;
1289};
1290
1291
1292
1293
1298template<
1299 class Access,
1300 class TypeList,
1301 bool HasType = member_type_access_traits<Access, TypeList>::has_type
1302 >
1303struct member_type_access {
1304 static_assert( ! HasType, "There is a missing specialisation." );
1305 static constexpr bool has_member = false;
1306 static constexpr bool has_default = false;
1307 static constexpr bool has_type = false;
1308 // Don't declare type!
1309 using safe_type = void;
1310};
1311
1312
1313template<class Access, class TypeList>
1314struct member_type_access<Access, TypeList, true> {
1315 static constexpr bool has_member
1316 = member_type_access_traits<Access, TypeList>::has_member;
1317
1318 static constexpr bool has_default
1319 = member_type_access_traits<Access, TypeList>::has_default;
1320
1321 static constexpr bool has_type = true;
1322
1323 using type
1324 = typename member_type_access_traits<Access, TypeList>::type;
1325
1326 using safe_type = type;
1327};
1328
1329
1330
1331
1332template<class Access, class...T>
1333class static_member_value_base {
1334 template<class U> struct access {
1335 using type = U;
1336 static constexpr bool has_member = Access::ugmisc::template has_static_member<U>;
1337 static constexpr auto& get() { return Access::ugmisc::template static_get<U>(); }
1338 };
1339
1340 struct void_wrapper {
1341 using type = void;
1342 };
1343
1344 template<bool Found, class Type=void_wrapper>
1345 struct result {
1346 using type = Type;
1347 static constexpr bool found = Found;
1348 };
1349
1350 using accessor_types = flatten_apply_each_class<access, T...>;
1351
1352 template<class accessors = accessor_types>
1353 static constexpr auto find_matching_type() {
1354 if constexpr ( accessors::size == 0 ) {
1355 return result<false>{};
1356 } else {
1357 using first_type = type_list_member<0, accessors>;
1358 if constexpr ( first_type::has_member ) {
1359 return result<true, first_type>{};
1360 } else {
1361 return find_matching_type<type_list_remove_prefix<1, accessors>>();
1362 }
1363 }
1364 }
1365
1366 using result_type = decltype(find_matching_type());
1367 using getter_type = typename result_type::type;
1368
1369public:
1370 static constexpr bool has_member = result_type::found;
1371
1372 template<class F, class...A>
1373 static constexpr decltype(auto) get(F&& default_get, A&&...args) {
1374 if constexpr ( has_member ) {
1375 return getter_type::get();
1376 } else {
1377 return default_get(std::forward<A>(args)...);
1378 }
1379 }
1380
1381 static constexpr auto& get() { return getter_type::get(); }
1382
1383 using safe_matched_type = typename getter_type::type;
1384};
1385
1386
1387template<bool WithType, class Access, class...T>
1388struct static_member_value;
1389
1390template<class Access, class...T>
1391struct static_member_value<true, Access, T...>
1392: public static_member_value_base<Access, T...>
1393{
1394 using matched_type =
1395 typename static_member_value_base<Access, T...>::safe_matched_type;
1396};
1397
1398template<class Access, class...T>
1399struct static_member_value<false, Access, T...>
1400: public static_member_value_base<Access, T...>
1401{ };
1402
1403
1404template<class Access, class...T>
1405using static_member_value_t =
1406 static_member_value<
1407 static_member_value_base<Access, T...>::has_member,
1408 Access, T...
1409 >;
1410
1411
1412} /* member_ (::ugmisc::member_) */
1414
1415
1416
1417#ifndef UGMISC_DOCS
1418template< class Access, class...T > class member_type_access
1419: public member_::member_type_access<Access, flatten_t<T...>>
1420{
1421};
1422#else
1423
1459template< class Access, class...T > struct member_type_access {
1464 static constexpr bool has_member = ???;
1465
1471 static constexpr bool has_default = ???;
1472
1477 static constexpr bool has_type = has_member || has_default;
1478
1490 using type = ???;
1491
1496 using safe_type = ???;
1497};
1498#endif
1499
1500
1501
1502
1540template<class Access, class...T>
1541struct static_member_value : public member_::static_member_value_t<Access, T...>
1542{
1543#ifdef UGMISC_DOCS
1548 static constexpr bool has_member = ???;
1549
1560 template<class F, class...A>
1561 static constexpr decltype(auto) get(F&& default_get, A&&...args);
1562
1568 static constexpr auto& get() { return getter_type::get(); }
1569
1577
1583 using matched_type = ???;
1584#endif
1585};
1586
1587
1588
1589
1590}
1591#endif /* UGMISC_MEMBER_HPP */
member_value_copy
Definition member.hpp:945
@ MEMBER_COPY_NEVER
Always take a reference to the original object.
Definition member.hpp:947
@ MEMBER_COPY_AUTO
Default. Copy value if safe and trivial to do so.
Definition member.hpp:946
@ MEMBER_COPY_ALWAYS
Always copy the value.
Definition member.hpp:948
Definition member.hpp:352
Definition member.hpp:745
static constexpr wrapped_objects< void, T... > with(T &&...objs)
Definition member.hpp:931
static constexpr default_function< F > fallback(F &&f)
Definition member.hpp:925
Definition member.hpp:1459
??? safe_type
Definition member.hpp:1496
static constexpr bool has_member
Definition member.hpp:1464
static constexpr bool has_default
Definition member.hpp:1471
??? type
Definition member.hpp:1490
static constexpr bool has_type
Definition member.hpp:1477
Definition member.hpp:1037
static constexpr auto with(T &&...objs)
Definition member.hpp:1173
static constexpr member_value< Access, MEMBER_COPY_AUTO > copy_auto
Definition member.hpp:1192
static constexpr member_value< Access, MEMBER_COPY_ALWAYS > copy_always
Definition member.hpp:1202
static constexpr member_value< Access, MEMBER_COPY_NEVER > copy_never
Definition member.hpp:1212
Definition member.hpp:566
constexpr decltype(auto) operator()(Args &&...args) const
Definition member.hpp:652
static constexpr member_::fallback_caller< static_member_caller, std::remove_reference_t< F > > fallback(F &&functor)
Definition member.hpp:664
static constexpr bool is_matched(Args &&...)
Definition member.hpp:606
static matched_type< Args... > declval_matched(Args &&...) noexcept
static constexpr bool is_matched_v
Definition member.hpp:599
static constexpr decltype(auto) call(Args &&...args)
Definition member.hpp:637
typename caller< void, types_list, Args... >::type matched_type
Definition member.hpp:615
static safe_matched_type< Args... > safe_declval_matched(Args &&...) noexcept
Definition member.hpp:1542
static constexpr decltype(auto) get(F &&default_get, A &&...args)
??? safe_matched_type
Definition member.hpp:1576
??? matched_type
Definition member.hpp:1583
static constexpr auto & get()
Definition member.hpp:1568
static constexpr bool has_member
Definition member.hpp:1548
Encapsulates a list of types.
Definition typelist.hpp:792
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:181
type_list_apply_each_class< F, flatten_t< T... > > flatten_apply_each_class
Definition typelist.hpp:353
typename typelist_::get_type_list_member< I, T >::type type_list_member
Definition typelist.hpp:153