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
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 ugmisc__T, class, class...ugmisc__A> \
154 struct static_caller_as_function { \
155 static constexpr bool is_callable = false; \
156 }; \
157 \
158 template<class ugmisc__T, class...ugmisc__A> \
159 struct static_caller_as_function<ugmisc__T, std::void_t<decltype(ugmisc__T::NAME(std::declval<ugmisc__A>()...))>, ugmisc__A...> { \
160 static constexpr bool is_callable = true; \
161 \
162 static constexpr decltype(auto) call(ugmisc__A...args) { \
163 return ugmisc__T::NAME(std::forward<ugmisc__A>(args)...); \
164 } \
165 }; \
166 \
167 template<class ugmisc__T, bool, class...ugmisc__A> \
168 struct static_caller_as_invoke { \
169 static constexpr bool is_callable = false; \
170 }; \
171 \
172 template<class ugmisc__T, class...ugmisc__A> \
173 struct static_caller_as_invoke<ugmisc__T, std::is_invocable_v<decltype((ugmisc__T::NAME)), ugmisc__A...>, ugmisc__A...> { \
174 static constexpr bool is_callable = true; \
175 \
176 static constexpr decltype(auto) call(ugmisc__A...args) { \
177 return std::invoke(ugmisc__T::NAME, std::forward<ugmisc__A>(args)...); \
178 } \
179 }; \
180 \
181 template<class ugmisc__T, class...ugmisc__A> \
182 using static_caller = std::conditional_t< \
183 static_caller_as_function<ugmisc__T, void, ugmisc__A...>::is_callable, \
184 static_caller_as_function<ugmisc__T, void, ugmisc__A...>, \
185 static_caller_as_invoke<ugmisc__T, true, ugmisc__A...> \
186 >; \
187 \
188 /* Non static method call support. */ \
189 \
190 template<class ugmisc__T, class, class...ugmisc__A> \
191 struct caller_as_method { \
192 static constexpr bool is_callable = false; \
193 }; \
194 \
195 template<class ugmisc__T, class...ugmisc__A> \
196 struct caller_as_method<ugmisc__T, std::void_t<decltype(std::declval<ugmisc__T>().NAME(std::declval<ugmisc__A>()...))>, ugmisc__A...> { \
197 static constexpr bool is_callable = true; \
198 \
199 static constexpr decltype(auto) call(ugmisc__T& obj, ugmisc__A...args) { \
200 return std::forward<ugmisc__T>(obj).NAME(std::forward<ugmisc__A>(args)...); \
201 } \
202 }; \
203 \
204 template<class ugmisc__T, bool I, class...ugmisc__A> \
205 struct caller_as_invoke { \
206 static constexpr bool is_callable = false; \
207 }; \
208 \
209 template<class ugmisc__T, class...ugmisc__A> \
210 struct caller_as_invoke<ugmisc__T, \
211 std::is_invocable_v<decltype(std::declval<ugmisc__T>().NAME), ugmisc__A...>, \
212 ugmisc__A... \
213 > \
214 { \
215 static constexpr bool is_callable = true; \
216 static constexpr decltype(auto) call(ugmisc__T& obj, ugmisc__A...args) { \
217 return std::invoke(std::forward<ugmisc__T>(obj).NAME, std::forward<ugmisc__A>(args)...); \
218 } \
219 }; \
220 \
221 template<class ugmisc__T, class...ugmisc__A> \
222 using caller = std::conditional_t< \
223 caller_as_method<ugmisc__T, void, ugmisc__A...>::is_callable, \
224 caller_as_method<ugmisc__T, void, ugmisc__A...>, \
225 caller_as_invoke<ugmisc__T, true, ugmisc__A...> \
226 >; \
227 /* Static data reference support. */ \
228 \
229 template<class ugmisc__T, class=void> \
230 struct static_member { \
231 static constexpr bool has_member = false; \
232 }; \
233 \
234 template<class ugmisc__T> \
235 struct static_member<ugmisc__T, std::void_t<decltype(ugmisc__T::NAME)>> { \
236 static constexpr bool has_member = true; \
237 \
238 static constexpr auto& get() { \
239 return ugmisc__T::NAME; \
240 } \
241 }; \
242 \
243 /* Non static data reference support. */ \
244 \
245 template<class ugmisc__T, class=void> \
246 struct member { \
247 static constexpr bool has_member = false; \
248 }; \
249 \
250 template<class ugmisc__T> \
251 struct member<ugmisc__T, std::void_t<decltype(std::declval<ugmisc__T>().NAME)>> { \
252 static constexpr bool has_member = true; \
253 \
254 static constexpr auto& get(ugmisc__T& obj) { \
255 return obj.NAME; \
256 } \
257 \
258 using member_type = decltype(std::declval<std::remove_reference_t<ugmisc__T>>().NAME); \
259 }; \
260 /* Type support. */ \
261 \
262 template< class ugmisc__T, class=void > struct GetType { \
263 static constexpr bool has_member_type = false; \
264 using safe_type = void; \
265 }; \
266 \
267 template< class ugmisc__T > struct GetType< ugmisc__T, std::void_t< typename ugmisc__T::NAME > > { \
268 static constexpr bool has_member_type = true; \
269 using safe_type = typename ugmisc__T::NAME; \
270 using type = typename ugmisc__T::NAME; \
271 }; \
272 \
273 }; \
274public: \
275 struct ugmisc { \
276 /* Static method call. */ \
277 template<class ugmisc__T, class...ugmisc__A> static constexpr bool is_static_callable = \
278 ugmisc_priv::template static_caller<ugmisc__T, ugmisc__A...>::is_callable; \
279 \
280 template<class ugmisc__T, class...ugmisc__A> \
281 static constexpr auto static_call(ugmisc__A&&...args) \
282 -> decltype( \
283 ugmisc_priv::template static_caller<ugmisc__T, ugmisc__A...>::call( \
284 std::forward<ugmisc__A>(args)... \
285 )) \
286 { \
287 return ugmisc_priv::template static_caller<ugmisc__T, ugmisc__A...>::call( \
288 std::forward<ugmisc__A>(args)... \
289 ); \
290 } \
291 \
292 /* Non static method call. */ \
293 template<class ugmisc__T, class...ugmisc__A> static constexpr bool is_callable = \
294 ugmisc_priv::template caller<ugmisc__T, ugmisc__A...>::is_callable; \
295 \
296 template<class ugmisc__T, class...ugmisc__A> \
297 static constexpr auto call(ugmisc__T& obj, ugmisc__A&&...args) \
298 -> decltype( \
299 ugmisc_priv::template caller<ugmisc__T, ugmisc__A...>::call( \
300 obj, \
301 std::forward<ugmisc__A>(args)... \
302 )) \
303 { \
304 return ugmisc_priv::template caller<ugmisc__T, ugmisc__A...>::call( \
305 obj, \
306 std::forward<ugmisc__A>(args)... \
307 ); \
308 } \
309 \
310 /* Static member reference. */ \
311 template< class ugmisc__T > \
312 static constexpr bool has_static_member \
313 = ugmisc_priv::template static_member<ugmisc__T>::has_member; \
314 template< class ugmisc__T> \
315 static constexpr auto& static_get() { \
316 return ugmisc_priv::template static_member<ugmisc__T>::get(); \
317 } \
318 \
319 /* Non static member reference. */ \
320 template< class ugmisc__T > \
321 static constexpr bool has_member \
322 = ugmisc_priv::template member<ugmisc__T>::has_member; \
323 template< class ugmisc__T > \
324 using member_type = typename ugmisc_priv::member<ugmisc__T>::member_type; \
325 template< class ugmisc__T> \
326 static constexpr auto& get(ugmisc__T&& obj) { \
327 return ugmisc_priv::template member<ugmisc__T>::get(obj); \
328 } \
329 \
330 /* Type. */ \
331 \
332 template< class ugmisc__T > using type = typename ugmisc_priv::GetType<ugmisc__T>::type; \
333 template< class ugmisc__T > using safe_type = \
334 typename ugmisc_priv::GetType<ugmisc__T>::safe_type; \
335 template< class ugmisc__T > static constexpr bool has_member_type = \
336 ugmisc_priv::template GetType<ugmisc__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
434template<class StaticCaller, class Functor>
435class fallback_caller {
436 Functor m_functor;
437
438 /*
439 * The use of Dummy* for the first override and Dummy for the second, makes
440 * the first more specific.
441 */
442 template<class Dummy, class...T>
443 constexpr auto forward(Dummy*, T&&...args) const
444 -> decltype(invoke(m_functor, std::forward<T>(args)...))
445 {
446 return invoke(m_functor, std::forward<T>(args)...);
447 }
448
449 template<class Dummy, class...T>
450 constexpr decltype(auto) forward(Dummy, T&&...args) const {
451 return m_functor();
452 }
453
454
455public:
456 constexpr fallback_caller(Functor f) : m_functor(std::forward<Functor>(f)) {}
457
458 fallback_caller(fallback_caller&&) = default;
459 fallback_caller(const fallback_caller&) = delete;
460
461 template<class...T>
462 constexpr decltype(auto) call(T&&...args) const {
463 if constexpr ( StaticCaller::template is_matched_v<T...> ) {
464 return StaticCaller::call(std::forward<T>(args)...);
465 } else {
466 auto dummy = (int*)nullptr;
467 return forward(dummy, std::forward<T>(args)...);
468 }
469 }
470
471 template<class...T>
472 constexpr decltype(auto) operator()(T&&...args) const {
473 return call(std::forward<T>(args)...);
474 }
475};
476
477
478template<class Access, class T, class...A>
479inline constexpr bool is_static_callable_v =
480 Access::ugmisc::template is_static_callable<T, A...>;
481
482
483template<class Access, class T, class...A>
484constexpr bool is_static_callable(A&&...) {
485 return is_static_callable_v<Access, T, A...>;
486}
487
488
489} /* member_ (::ugmisc::member_) */
491
492
493
570template<class Access, class...T>
572private:
573 template<class V, class List, class...Args>
574 struct caller
575 : public caller<V, type_list_remove_prefix<1, List>, Args...> {
576 };
577
578 template<class...Args> struct caller<void, type_list<>, Args...> {
579 static constexpr bool is_callable = false;
580 using safe_type = void;
581 };
582
583 template<class List, class...Args>
584 struct caller<
585 std::enable_if_t< member_::is_static_callable_v<Access, type_list_member<0, List>, Args...>>,
586 List,
587 Args...
588 >
589 {
590 static constexpr bool is_callable = true;
591 using type = type_list_member<0, List>;
592 using safe_type = void;
593 };
594
595 using types_list = flatten_t<T...>;
596
597public:
603 template<class...Args>
604 static constexpr bool is_matched_v =
605 caller<void, types_list, Args...>::is_callable;
606
610 template<class...Args>
611 static constexpr bool is_matched(Args&&...) {
612 return is_matched_v<Args...>;
613 }
614
619 template<class...Args>
620 using matched_type = typename caller<void, types_list, Args...>::type;
621
628 template<class...Args>
629 using safe_matched_type =
630 typename caller<void, types_list, Args...>::safe_type;
631
641 template<class...Args>
642 static constexpr decltype(auto) call(Args&&...args) {
643 using type = matched_type<Args...>;
644 static_assert(
645 is_matched_v<Args...>,
646 "None of the types is callable with these argument types."
647 );
648 return Access::ugmisc::template static_call<type>(std::forward<Args>(args)...);
649 }
650
656 template<class...Args>
657 constexpr decltype(auto) operator()(Args&&...args) const {
658 return call(std::forward<Args>(args)...);
659 }
660
666 template<class F>
667 static constexpr
668 member_::fallback_caller<static_member_caller, F>
669 fallback(F&& functor) {
670 return functor;
671 }
672
677 template<class...Args>
678 static matched_type<Args...> declval_matched(Args&&...) noexcept;
679
684 template<class...Args>
685 static matched_type<Args...> declval_matched() noexcept;
686
692 template<class...Args>
693 static safe_matched_type<Args...> safe_declval_matched(Args&&...) noexcept;
694
698 template<class...Args>
699 static safe_matched_type<Args...> safe_declval_matched() noexcept;
700};
701
702
703
704
749template<class Access>
751private:
752 /*
753 * A wrapped objects instance contains references to objects.
754 * It only exists briefly. It is instantiated explicitly, because we want
755 * to know which objects were originally lvalues (those will be passed as
756 * reference types).
757 * However we always store references to the objects.
758 */
759 template<class...T>
760 struct wrapped_objects_base {
761 protected:
762 static_assert(
763 sizeof...(T) == 0,
764 "A specialisation should have been used instead of this template."
765 );
766
767 template<class...A> static constexpr bool is_matched_v = false;
768 template<class...A> static constexpr bool is_matched(A&&...) {
769 return false;
770 }
771
772 wrapped_objects_base() = default;
773 wrapped_objects_base(const wrapped_objects_base&) = default;
774
775 template<class...A>
776 constexpr void call(A&&...) const {
777 static_assert(false, "There are no more objects to call.");
778 }
779
780 static constexpr bool safe_refs = true;
781
782 template<class...A>
783 static constexpr bool matched_here = false;
784 };
785
786 template<class T1, class...T>
787 struct wrapped_objects_base<T1, T...> : public wrapped_objects_base<T...>
788 {
789 private:
790 T1& obj_ref;
791
792 protected:
793 static constexpr bool safe_refs =
794 std::is_lvalue_reference_v<T1> &&
795 wrapped_objects_base<T...>::safe_refs;
796 ;
797
798 template<class...A>
799 static constexpr bool matched_here =
800 Access::ugmisc::template is_callable<T1, A...>;
801
802 template<class...A>
803 constexpr decltype(auto) call(A&&...args) const {
804 if constexpr ( matched_here<A...> ) {
805 return Access::ugmisc::template call(
806 obj_ref,
807 std::forward<A>(args)...
808 );
809 } else {
810 return this->wrapped_objects_base<T...>::call(std::forward<A>(args)...);
811 }
812 }
813
814 public:
815 constexpr wrapped_objects_base(T1& obj, T&...t)
816 : wrapped_objects_base<T...>(t...), obj_ref(obj)
817 {}
818
819 wrapped_objects_base(wrapped_objects_base&&) = default;
820
821 constexpr wrapped_objects_base(const wrapped_objects_base& other)
822 : wrapped_objects_base<T...>((wrapped_objects_base<T...>)other),
823 obj_ref(other.obj_ref)
824 {
825 static_assert(
826 safe_refs,
827 "Can't copy a member_caller::wrapped_objects if the "
828 "referenced objects aren't all lvalues."
829 );
830 }
831
832 template<class...A>
833 static constexpr bool is_matched_v =
834 matched_here<A...> ||
835 wrapped_objects_base<T...>::template matched_here<A...>;
836
837 template<class...A>
838 static constexpr bool is_matched(A&&...) {
839 return is_matched_v<A...>;
840 }
841 };
842
843 /*
844 * wrapped_objects with a functor is like wrapped_objects without one, but
845 * it will default to calling the functor.
846 */
847 template<class F, class...T>
848 class wrapped_objects : public wrapped_objects<void, T...> {
849 F functor;
850 using wrapped_objects<void, T...>::wrapped_objects;
851
852 template<class, class...A> struct forwarder {
853 static constexpr decltype(auto) call(F& f, A&...args) {
854 return f();
855 }
856 };
857
858 template<class...A>
859 struct forwarder<
860 std::void_t< decltype(member_::invoke(std::declval<F&>(), std::declval<A>()...)) >,
861 A...
862 >
863 {
864 static constexpr decltype(auto) call(F& f, A&...args) {
865 return member_::invoke(f, std::forward<A>(args)...);
866 }
867 };
868
869 template<class...A>
870 constexpr decltype(auto) call_default(A&&...args) {
871 return forwarder<void, A...>::call(functor, args...);
872 }
873
874 public:
875 constexpr wrapped_objects(F f, T...t)
876 : wrapped_objects<void, T...>(t...),
877 functor(std::forward<F>(f))
878 {}
879
880 template<class...A>
881 constexpr decltype(auto) operator()(A&&...args) {
882 if constexpr ( wrapped_objects::template is_matched_v<A...> ) {
883 return this->call( std::forward<A>(args)... );
884 } else {
885 return call_default(std::forward<A>(args)...);
886 }
887 }
888 };
889
890 template<class...T>
891 class wrapped_objects<void, T...> : public wrapped_objects_base<T...> {
892 public:
893 using wrapped_objects_base<T...>::wrapped_objects_base;
894
895 template<class...A>
896 constexpr decltype(auto) operator() (A&&...args) const {
897 return
898 this->call(std::forward<A>(args)...);
899 }
900 };
901
902 /*
903 * This only has the job of creating a wrapped_objects.
904 * It is intended to be a short lived object. Where possible it is
905 * instanciated with a reference type, so the member functor is declared as
906 * a reference.
907 */
908 template<class F>
909 class default_function {
910 F functor;
911 public:
912 constexpr default_function(F f) : functor(std::forward<F>(f)) {}
913 default_function(default_function&&) = default;
914 default_function(const default_function&) = delete;
915
916 template<class...T>
917 constexpr wrapped_objects<F, T...> operator() (T&&...objs) const {
918 return {functor, objs...};
919 }
920 };
921
922public:
923 /*
924 * The public methods are static because member_caller itself holds all its
925 * defining characteristics in its type.
926 */
927
932 template<class F>
933 static constexpr default_function<F> fallback(F&& f) { return f; }
934
938 template<class...T>
939 static constexpr wrapped_objects<void, T...> with(T&&...objs) {
940 return {objs...};
941 }
942
943 template<class...T>
944 constexpr auto operator() (T&&...objs) const { return with<T...>(objs...); }
945};
946
947
948
949
958
959
960
961
1049template<class Access, member_value_copy CopyValue = MEMBER_COPY_AUTO>
1051 static_assert(
1052 (CopyValue == MEMBER_COPY_AUTO) ||
1053 (CopyValue == MEMBER_COPY_ALWAYS) ||
1054 (CopyValue == MEMBER_COPY_NEVER)
1055 );
1056private:
1057 struct empty {
1058 template<class F, class...A>
1059 constexpr decltype(auto) get(F&& func, A&&...args) const {
1060 return member_::invoke(
1061 std::forward<F>(func), std::forward<A>(args)...
1062 );
1063 }
1064
1065 static constexpr bool has_member = false;
1066
1067 constexpr void get() const {
1068 static_assert(
1069 false,
1070 "Can't get member that was not found in any object."
1071 );
1072 }
1073
1074 template<class...T>
1075 constexpr decltype(auto) operator() (T&&...args) const {
1076 return get(std::forward<T>(args)...);
1077 }
1078 };
1079
1080 template<class T>
1081 class wrapped_object_member {
1082 using object_type = T;
1083
1084 // The actual type of the member, as declared in T's definition.
1085 using member_type = typename Access::ugmisc::template member_type<T>;
1086
1087 using effective_member_type =
1088 decltype(Access::ugmisc::get(std::declval<T&>()));
1089
1090 using deref_effective_member_type =
1091 std::remove_reference_t<effective_member_type>;
1092
1093 static constexpr bool rvalue_object =
1094 !std::is_lvalue_reference_v<object_type>;
1095
1096 static constexpr bool const_object =
1097 std::is_const_v<object_type>;
1098
1099 static constexpr bool volatile_object =
1100 std::is_volatile_v<object_type>;
1101
1102 static constexpr bool member_is_reference =
1103 std::is_lvalue_reference_v<member_type>;
1104
1105 static constexpr bool volatile_member =
1106 std::is_volatile_v<deref_effective_member_type>;
1107
1108 static constexpr bool const_member =
1109 std::is_const_v<deref_effective_member_type>;
1110
1111 static constexpr bool rvalue_member =
1112 (! member_is_reference) && rvalue_object;
1113
1114 /*
1115 * copy_member == true: the member value is copied into the wrapper,
1116 * and returned by copy from get().
1117 */
1118 static constexpr bool copy_member =
1119 (CopyValue == MEMBER_COPY_ALWAYS)
1120 || (
1121 (CopyValue != MEMBER_COPY_NEVER)
1122 && (!volatile_member)
1123 && (const_member || rvalue_member)
1124 && std::is_trivially_copyable_v< member_type >
1125 );
1126
1127 /*
1128 * return rvalue_member == true: a reference to the member is stored,
1129 * and an rvalue reference is returned from get().
1130 */
1131 static constexpr bool return_rvalue_member =
1132 (!copy_member) && rvalue_member;
1133
1134 static constexpr bool return_lvalue_member =
1135 (!return_rvalue_member || copy_member);
1136
1137 using member_store_type = std::conditional_t<
1138 copy_member,
1139 const std::remove_volatile_t<deref_effective_member_type>,
1140 effective_member_type
1141 >;
1142
1143 using member_return_type = std::conditional_t<
1144 copy_member,
1145 std::remove_const_t<member_store_type>,
1146 std::conditional_t<
1147 return_rvalue_member,
1148 deref_effective_member_type&&,
1149 deref_effective_member_type&
1150 >
1151 >;
1152
1153 // This may be a copy of the member's value where that is safe to do.
1154 // Otherwise it is a reference to the member.
1155 member_store_type member;
1156 public:
1157 static constexpr bool has_member = true;
1158
1159 constexpr wrapped_object_member( object_type& obj )
1160 : member( Access::ugmisc::get(obj) )
1161 {}
1162
1163 wrapped_object_member(const wrapped_object_member&) = default;
1164 wrapped_object_member(wrapped_object_member&&) = default;
1165
1166 template<class...A>
1167 constexpr member_return_type get(A&&...args) const { return member; }
1168
1169 template<class...A>
1170 constexpr member_return_type operator()(A&&...args) const { return member; }
1171 };
1172
1173 static constexpr empty find_ref() { return {}; }
1174
1175 template<class T1, class...T>
1176 static constexpr auto find_ref(T1&& obj, T&&...objs) {
1177 if constexpr ( Access::ugmisc::template has_member<T1> ) {
1178 return wrapped_object_member<T1>{ obj };
1179 } else {
1180 return find_ref(std::forward<T>(objs)...);
1181 }
1182 }
1183
1184public:
1185 /*
1186 * The public methods are static because member_value itself holds all its
1187 * defining characteristics in its type.
1188 */
1189
1193 template<class...T>
1194 static constexpr auto with(T&&...objs) {
1195 return find_ref(std::forward<T>(objs)...);
1196 }
1197
1198 template<class...T>
1199 constexpr auto operator() (T&&...objs) const { return with<T...>(objs...); }
1200
1214
1224
1234};
1235
1236
1237
1238
1240namespace member_ {
1245template< class Access, class TypeList > class member_type_access_traits {
1246 using all_types = TypeList;
1247 using normal_types =
1248 filter_type_list<member_::get_is_default_type, all_types, invert_test>;
1249 using default_types =
1250 filter_type_list<member_::get_is_default_type, all_types>;
1251
1252 static_assert( default_types::size <= 1 );
1253 static_assert( default_types::size + normal_types::size == all_types::size );
1254
1255 using access = Access;
1256
1257 template<bool Found, class U> struct result {
1258 static constexpr bool found = Found;
1259 using type = U;
1260 };
1261
1262 /*
1263 * This allows us to avoid instantiating type_list_member<0, seq> where seq
1264 * is empty.
1265 */
1266 template< class seq = normal_types >
1267 static constexpr auto find_type_pair() {
1268
1269 if constexpr ( seq::size == 0 ) {
1270 return result<false, void>{};
1271 } else {
1272 using first_type = type_list_member<0, seq>;
1273 if constexpr ( access::ugmisc::template has_member_type<first_type> ) {
1274 return result<
1275 true,
1276 typename access::ugmisc::template type<first_type>
1277 >{};
1278 } else {
1279 return find_type_pair< type_list_remove_prefix<1, seq> >();
1280 }
1281 }
1282 }
1283
1284 using result_t = decltype(find_type_pair());
1285
1286 /*
1287 * This allows us to avoid instantiating type_list_member<default_types>
1288 * when default_types is empty.
1289 */
1290 static constexpr auto find_default_type_pair() {
1291 if constexpr ( default_types::size ) {
1292 return result<true, type_list_member<0, default_types>>{};
1293 } else {
1294 return result<false, void>{};
1295 }
1296 }
1297
1298 using default_t = decltype(find_default_type_pair());
1299
1300
1301public:
1302 static constexpr bool has_member = result_t::found;
1303 static constexpr bool has_default = default_t::found;
1304 static constexpr bool has_type = has_member || has_default;
1305 using type = std::conditional_t<
1306 has_member,
1307 typename result_t::type,
1308 typename default_t::type
1309 >;
1310};
1311
1312
1313
1314
1319template<
1320 class Access,
1321 class TypeList,
1322 bool HasType = member_type_access_traits<Access, TypeList>::has_type
1323 >
1324struct member_type_access {
1325 static_assert( ! HasType, "There is a missing specialisation." );
1326 static constexpr bool has_member = false;
1327 static constexpr bool has_default = false;
1328 static constexpr bool has_type = false;
1329 // Don't declare type!
1330 using safe_type = void;
1331};
1332
1333
1334template<class Access, class TypeList>
1335struct member_type_access<Access, TypeList, true> {
1336 static constexpr bool has_member
1337 = member_type_access_traits<Access, TypeList>::has_member;
1338
1339 static constexpr bool has_default
1340 = member_type_access_traits<Access, TypeList>::has_default;
1341
1342 static constexpr bool has_type = true;
1343
1344 using type
1345 = typename member_type_access_traits<Access, TypeList>::type;
1346
1347 using safe_type = type;
1348};
1349
1350
1351
1352
1353template<class Access, class...T>
1354class static_member_value_base {
1355 template<class U> struct access {
1356 using type = U;
1357 static constexpr bool has_member = Access::ugmisc::template has_static_member<U>;
1358 static constexpr auto& get() { return Access::ugmisc::template static_get<U>(); }
1359 };
1360
1361 struct void_wrapper {
1362 using type = void;
1363 };
1364
1365 template<bool Found, class Type=void_wrapper>
1366 struct result {
1367 using type = Type;
1368 static constexpr bool found = Found;
1369 };
1370
1371 using accessor_types = flatten_apply_each_class<access, T...>;
1372
1373 template<class accessors = accessor_types>
1374 static constexpr auto find_matching_type() {
1375 if constexpr ( accessors::size == 0 ) {
1376 return result<false>{};
1377 } else {
1378 using first_type = type_list_member<0, accessors>;
1379 if constexpr ( first_type::has_member ) {
1380 return result<true, first_type>{};
1381 } else {
1382 return find_matching_type<type_list_remove_prefix<1, accessors>>();
1383 }
1384 }
1385 }
1386
1387 using result_type = decltype(find_matching_type());
1388 using getter_type = typename result_type::type;
1389
1390public:
1391 static constexpr bool has_member = result_type::found;
1392
1393 template<class F, class...A>
1394 static constexpr decltype(auto) get(F&& default_get, A&&...args) {
1395 if constexpr ( has_member ) {
1396 return getter_type::get();
1397 } else {
1398 return default_get(std::forward<A>(args)...);
1399 }
1400 }
1401
1402 static constexpr auto& get() { return getter_type::get(); }
1403
1404 using safe_matched_type = typename getter_type::type;
1405};
1406
1407
1408template<bool WithType, class Access, class...T>
1409struct static_member_value;
1410
1411template<class Access, class...T>
1412struct static_member_value<true, Access, T...>
1413: public static_member_value_base<Access, T...>
1414{
1415 using matched_type =
1416 typename static_member_value_base<Access, T...>::safe_matched_type;
1417};
1418
1419template<class Access, class...T>
1420struct static_member_value<false, Access, T...>
1421: public static_member_value_base<Access, T...>
1422{ };
1423
1424
1425template<class Access, class...T>
1426using static_member_value_t =
1427 static_member_value<
1428 static_member_value_base<Access, T...>::has_member,
1429 Access, T...
1430 >;
1431
1432
1433} /* member_ (::ugmisc::member_) */
1435
1436
1437
1438#ifndef UGMISC_DOCS
1439template< class Access, class...T > class member_type_access
1440: public member_::member_type_access<Access, flatten_t<T...>>
1441{
1442};
1443#else
1444
1480template< class Access, class...T > struct member_type_access {
1485 static constexpr bool has_member = ???;
1486
1492 static constexpr bool has_default = ???;
1493
1498 static constexpr bool has_type = has_member || has_default;
1499
1511 using type = ???;
1512
1517 using safe_type = ???;
1518};
1519#endif
1520
1521
1522
1523
1561template<class Access, class...T>
1562struct static_member_value : public member_::static_member_value_t<Access, T...>
1563{
1564#ifdef UGMISC_DOCS
1569 static constexpr bool has_member = ???;
1570
1581 template<class F, class...A>
1582 static constexpr decltype(auto) get(F&& default_get, A&&...args);
1583
1589 static constexpr auto& get() { return getter_type::get(); }
1590
1598
1604 using matched_type = ???;
1605#endif
1606};
1607
1608
1609
1610
1611}
1612#endif /* UGMISC_MEMBER_HPP */
member_value_copy
Definition member.hpp:953
@ MEMBER_COPY_NEVER
Always take a reference to the original object.
Definition member.hpp:955
@ MEMBER_COPY_AUTO
Default. Copy value if safe and trivial to do so.
Definition member.hpp:954
@ MEMBER_COPY_ALWAYS
Always copy the value.
Definition member.hpp:956
static constexpr wrapped_objects< void, T... > with(T &&...objs)
Definition member.hpp:939
static constexpr default_function< F > fallback(F &&f)
Definition member.hpp:933
static constexpr bool has_member
Definition member.hpp:1485
static constexpr bool has_default
Definition member.hpp:1492
static constexpr bool has_type
Definition member.hpp:1498
static constexpr auto with(T &&...objs)
Definition member.hpp:1194
static constexpr member_value< Access, MEMBER_COPY_AUTO > copy_auto
Definition member.hpp:1213
static constexpr member_value< Access, MEMBER_COPY_ALWAYS > copy_always
Definition member.hpp:1223
static constexpr member_value< Access, MEMBER_COPY_NEVER > copy_never
Definition member.hpp:1233
constexpr decltype(auto) operator()(Args &&...args) const
Definition member.hpp:657
static constexpr bool is_matched(Args &&...)
Definition member.hpp:611
static matched_type< Args... > declval_matched(Args &&...) noexcept
static constexpr bool is_matched_v
Definition member.hpp:604
static constexpr decltype(auto) call(Args &&...args)
Definition member.hpp:642
typename caller< void, types_list, Args... >::type matched_type
Definition member.hpp:620
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:669
static constexpr decltype(auto) get(F &&default_get, A &&...args)
static constexpr auto & get()
Definition member.hpp:1589
static constexpr bool has_member
Definition member.hpp:1569
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