Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Concepts.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Common_Concept_h_
5#define _Stroika_Foundation_Common_Concept_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <chrono>
10#include <concepts>
11#include <functional> // needed for std::equal_to
12#include <iterator> // needed for std::begin/std::end calls
13#include <memory>
14#include <optional>
15#include <variant>
16
17#include "Stroika/Foundation/Common/Common.h"
18#include "Stroika/Foundation/Common/ConceptsBase.h"
19
20/*
21 * \file
22 * Miscellaneous type traits and concepts for metaprogramming
23 *
24 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
25 *
26 *
27 USEFUL EXAMPLE:
28 template <IStdPathLike2UNICODEString TOSTRINGABLE>
29 explicit String (TOSTRINGABLE&& s)
30 requires (
31 not IBasicUNICODEStdString<remove_cvref_t<TOSTRINGABLE>> and
32 not requires (TOSTRINGABLE t) {
33 {
34 []<IUNICODECanUnambiguouslyConvertFrom T1> (const T1*) {}(t)
35 };
36 } and
37 not requires (TOSTRINGABLE t) {
38 {
39 []<IUNICODECanUnambiguouslyConvertFrom T1> (const span<const T1>&) {}(t)
40 };
41 } and
42 not requires (TOSTRINGABLE t) {
43 {
44 []<IStdBasicStringCompatibleCharacter T1> (const basic_string_view<T1>&) {}(t)
45 };
46 })
47#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
48 : String{mkSTR_ (forward<TOSTRINGABLE> (s))} {}
49#else
50 ;
51#endif
52
53 */
54
56
57 namespace Private_ {
58 struct void_type {
59 using type = void;
60 };
61 }
62
63 /**
64 * \brief concept true if integral or floatpoint type 'T'. Not sure why not provided by std c++
65 *
66 * Also note - NOT marked true for arithmetic-like types, like big-num package (perhaps provide another concept for this).
67 */
68 template <typename T>
69 concept IBuiltinArithmetic = is_arithmetic_v<T>;
70
71 /**
72 * \brief Extract the number of arguments, return type, and each individual argument type from a lambda or simple function object.
73 *
74 * \par Example Usage
75 * \code
76 * auto lambda = [](int i) { return long(i*10); };
77 *
78 * using traits = FunctionTraits<decltype(lambda)>;
79 *
80 * static_assert (traits::kArity == 1);
81 * static_assert (same_as<long, traits::result_type>);
82 * static_assert (same_as<int, traits::arg<0>::type>);
83 * \endcode
84 *
85 * CREDITS:
86 * From https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda
87 * https://stackoverflow.com/users/224671/kennytm
88 *
89 * For generic types, directly use the result of the signature of its 'operator()'
90 * Specialize for pointers to member function
91 *
92 * \note this doesn't work for function objects that have templated operator() - such as String::EqualsComparer, since there is no type to extract.
93 */
94 template <typename T>
95 struct FunctionTraits : public FunctionTraits<decltype (&T::operator())> {};
96 template <typename CLASS_TYPE, typename RETURN_TYPE, typename... ARGS>
97 struct FunctionTraits<RETURN_TYPE (CLASS_TYPE::*) (ARGS...) const> {
98 /**
99 * \brief Number of arguments
100 */
101 static inline constexpr size_t kArity = sizeof...(ARGS);
102
103 /**
104 * Function return type.
105 */
106 using result_type = RETURN_TYPE;
107
108 /**
109 * type of the ith 'arg';
110 *
111 * \note UNCLEAR if/how this might work if the function is overloaded...
112 *
113 * \see arg_t, ArgOrVoid, ArgOrVoid_t
114 */
115 template <size_t i>
116 struct arg {
117 using type = typename std::tuple_element<i, std::tuple<ARGS...>>::type;
118 // the i-th argument is equivalent to the i-th tuple element of a tuple
119 // composed of those arguments.
120 };
121
122 /**
123 */
124 template <size_t i>
125 using arg_t = typename arg<i>::type;
126
127 /**
128 * \brief like 'arg' - except that if index > max legal, instead of failing to compile, will return void. Helpful
129 * sometimes in contexts where c++ templates run more code than you might want.
130 */
131 template <size_t i>
132 struct ArgOrVoid {
133 using type = typename conditional_t<(i < sizeof...(ARGS)), tuple_element<i, tuple<ARGS...>>, Private_::void_type>::type;
134 };
135
136 /**
137 */
138 template <size_t i>
139 using ArgOrVoid_t = typename ArgOrVoid<i>::type;
140 };
141
142 /**
143 * \brief like std::invocable concept, except also requires the invocation doesn't raise exceptions
144 */
145 template <typename F, typename... Args>
146 concept INoThrowInvocable = invocable<F, Args...> and requires (F f, Args... args) {
147 { noexcept (f (args...)) };
148 };
149
150 /**
151 * \par Example Usage
152 * \code
153 * static_assert (invocable_r<decltype ([] (int) { return ""; }), const char*, int>);
154 * static_assert (invocable_r<decltype ([] (char*, char*) {}), void, char*, char*>);
155 * \endcode
156 *
157 * \note used STL-style name since so closely related to invocable - which is part of the standard library.
158 */
159 template <typename F, typename R, typename... Args>
160 concept invocable_r = invocable<F, Args...> && convertible_to<invoke_result_t<F, Args...>, R>;
161 static_assert (invocable_r<decltype ([] (int) { return ""; }), const char*, int>);
162 static_assert (invocable_r<decltype ([] (char*, char*) {}), void, char*, char*>);
163
164 // From https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
165 template <typename T>
166 concept IDuration =
167 requires { []<typename Rep, typename Period> (type_identity<chrono::duration<Rep, Period>>) {}(type_identity<T> ()); };
168 static_assert (not IDuration<float>);
169
170 // From https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
171 template <typename T>
172 concept ITimePoint =
173 requires { []<typename CLOCK, typename DURATION> (type_identity<chrono::time_point<CLOCK, DURATION>>) {}(type_identity<T> ()); };
174 static_assert (not ITimePoint<float>);
175
176 /**
177 * \brief concept - trivial shorthand for variadic same_as A or same_as B, or ...
178 *
179 * \par Example Usage
180 * \code
181 * template <typename T>
182 * concept IBasicUNICODECodePoint = same_as<remove_cv_t<T>, char8_t> or same_as<remove_cv_t<T>, char16_t> or same_as<remove_cv_t<T>, char32_t>;
183 *
184 * template <typename T>
185 * concept IBasicUNICODECodePoint = Common::IAnyOf<remove_cv_t<T>, char8_t, char16_t, char32_t>;
186 * \endcode
187 */
188 template <typename T, typename... U>
189 concept IAnyOf = (same_as<T, U> or ...);
190
191 /**
192 * \brief concept version of std::is_trivially_copyable_v
193 */
194 template <typename T>
195 concept trivially_copyable = is_trivially_copyable_v<T>;
196
197 /**
198 * A template which ignores its template arguments, and always returns true_type;
199 * NOT crazy - helpful is template metaprogramming.
200 */
201 template <typename...>
202 using True = true_type;
203
204 namespace Private_ {
205 template <class _Ty>
206 concept _Boolean_testable_impl = convertible_to<_Ty, bool>;
207 }
208
209 /**
210 * \brief handy re-usable concept, with the obvious meaning, and strangely omitted from std-c++ (though used in exposition).
211 */
212 template <class _Ty>
213 concept Boolean_testable = Private_::_Boolean_testable_impl<_Ty> && requires (_Ty&& __t) {
214 { !static_cast<_Ty&&> (__t) } -> Private_::_Boolean_testable_impl;
215 };
216
217 /**
218 * \brief equality_comparable_with, but less strict - just checks if it can be equality compared!
219 *
220 * INSPIRATION: https://godbolt.org/z/qevGWKan4
221 * static_assert (equality_comparable_with<nullopt_t, optional<int>>); // note this fails
222 */
223 template <class _Ty1, class _Ty2>
224 concept Weak_Equality_Comparable_With = requires (const remove_reference_t<_Ty1>& __x, const remove_reference_t<_Ty2>& __y) {
225 { __x == __y } -> Boolean_testable;
226 { __x != __y } -> Boolean_testable;
227 };
228 static_assert (not equality_comparable_with<nullopt_t, optional<int>>);
230
231 /**
232 * \brief like convertible_to, but also handling cases where T has an explict CTOR taking From
233 *
234 * \see https://stackoverflow.com/questions/76547398/stdconvertible-to-failing-to-recognize-explicitly-convertible-types
235 */
236 template <class FROM, class TO>
237 concept explicitly_convertible_to = requires { static_cast<TO> (std::declval<FROM> ()); };
238
239 /**
240 */
241 template <typename OT>
242 concept IOptional = same_as<remove_cvref_t<OT>, std::optional<typename OT::value_type>>;
243 static_assert (IOptional<std::optional<int>>);
244 static_assert (not IOptional<int>);
245
246 namespace Private_ {
247#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
248 template <typename T1, typename T2 = void>
249 struct is_shared_ptr_ : std::false_type {};
250 template <typename T1>
251 struct is_shared_ptr_<shared_ptr<T1>> : std::true_type {};
252 template <typename T1, typename T2 = void>
253 struct is_pair_ : std::false_type {};
254 template <typename T1, typename T2>
255 struct is_pair_<pair<T1, T2>> : std::true_type {};
256 template <typename... ARGS>
257 struct is_variant_ : std::false_type {};
258 template <typename... ARGS>
259 struct is_variant_<variant<ARGS...>> : std::true_type {};
260#endif
261 }
262
263 /**
264 * \brief return true iff argument type T, is std::pair<a,b> for some a/b types
265 */
266 template <typename T>
267 concept IPair =
268#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
269 Private_::is_pair_<T>::value
270#else
271 requires (T t) {
272 {
273 []<typename T1, typename T2> (pair<T1, T2>) {}(t)
274 };
275 }
276#endif
277 ;
278
279 /**
280 * \brief return true iff argument type T, is std::shared_ptr<A> for some A types
281 */
282 template <typename T>
283 concept ISharedPtr =
284#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
285 Private_::is_shared_ptr_<T>::value
286#else
287
288 requires (T t) {
289 {
290 []<typename T1> (shared_ptr<T1>) {}(t)
291 };
292 }
293#endif
294 ;
295 static_assert (ISharedPtr<shared_ptr<int>>);
296 static_assert (not ISharedPtr<int>);
297
298 namespace Private_ {
299 template <typename T, std::size_t N>
300 concept has_tuple_element = requires (T t) {
301 typename std::tuple_element_t<N, std::remove_const_t<T>>;
302 { get<N> (t) } -> std::convertible_to<const std::tuple_element_t<N, T>&>;
303 };
304 }
305
306 /**
307 * \brief Concept ITuple<T> check if T is a tuple.
308 *
309 * based on https://stackoverflow.com/questions/68443804/c20-concept-to-check-tuple-like-types
310 */
311 template <typename T>
312 concept ITuple = !std::is_reference_v<T> && requires (T t) {
313 typename std::tuple_size<T>::type;
314 requires std::derived_from<std::tuple_size<T>, std::integral_constant<std::size_t, std::tuple_size_v<T>>>;
315 } && []<std::size_t... N> (std::index_sequence<N...>) {
316 return (Private_::has_tuple_element<T, N> && ...);
317 }(std::make_index_sequence<std::tuple_size_v<T>> ());
318
319 /**
320 * \brief - detect if T is a std::variant<> type.
321 */
322 template <typename T>
323 concept IVariant =
324#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
325 Private_::is_variant_<T>::value
326#else
327 requires (T t) {
328 {
329 []<typename... TYPES> (variant<TYPES...>) {}(t)
330 };
331 }
332#endif
333 ;
334 static_assert (not IVariant<int>);
335 static_assert (IVariant<variant<int>>);
336
337 /**
338 * Concepts let you construct a 'template' of one arg from one with two args, but class, and variable templates don't allow
339 * this; but this magic trick of double indirection does allow it. And cannot use concepts as template arguments to another template
340 * sadly, so need this trick...
341 *
342 * The 'test' here just invokes convertible_to<TEST_ARGUMENT, T>
343 */
344 template <typename T>
346 template <typename TEST_ARGUMENT>
347 using Test = conditional_t<convertible_to<TEST_ARGUMENT, T>, true_type, false_type>;
348 };
349
350 /**
351 * Concepts let you construct a 'template' of one arg from one with two args, but class, and variable templates don't allow
352 * this; but this magic trick of double indirection does allow it. And cannot use concepts as template arguments to another template
353 * sadly, so need this trick...
354 *
355 * The 'test' here just invokes constructible_from<TEST_ARGUMENT, T>
356 */
357 template <typename T>
359 template <typename TEST_ARGUMENT>
360 using Test = conditional_t<constructible_from<TEST_ARGUMENT, T>, true_type, false_type>;
361 };
362
363 /**
364 * \brief Concept checks if the given type T has a const size() method which can be called to return a size_t.
365 *
366 * \par Example Usage
367 * \code
368 * if constexpr (IHasSizeMethod<T>) {
369 * T a{};
370 * return a.size ();
371 * }
372 * \endcode
373 */
374 template <typename T>
375 concept IHasSizeMethod = requires (const T& t) {
376 { t.size () } -> std::convertible_to<size_t>;
377 };
378
379 namespace Private_ {
380 template <typename T>
381 concept HasEq_ = requires (T t) {
382 { t == t } -> std::convertible_to<bool>;
383 };
384 template <typename T>
385 constexpr inline bool HasEq_v_ = HasEq_<T>;
386 template <typename T, typename U>
387 constexpr inline bool HasEq_v_<std::pair<T, U>> = HasEq_v_<T> and HasEq_v_<U>;
388 template <typename... Ts>
389 constexpr inline bool HasEq_v_<std::tuple<Ts...>> = (HasEq_v_<Ts> and ...);
390 template <typename T>
391 constexpr bool HasUsableEqualToOptimization ()
392 {
393 // static_assert (Common::IOperatorEq<remove_cvref_t<T>> and ! equality_comparable<T>);
394 // static_assert (not Common::IOperatorEq<T> and equality_comparable<T>);
395 // static_assert (Common::IOperatorEq<remove_cvref_t<T>> == equality_comparable<T>);
396 // @todo figure out why Private_::HasEq_v_ needed and cannot use equality_comparable
397 if constexpr (Private_::HasEq_v_<T>) {
398 struct EqualToEmptyTester_ : equal_to<T> {
399 int a;
400 };
401 // leverage empty base class optimization to see if equal_to contains any real data
402 return sizeof (EqualToEmptyTester_) == sizeof (int);
403 }
404 return false;
405 }
406 }
407
408 /**
409 * Check if equal_to<T> is both well defined, and contains no data. The reason it matters that it contains no data, is because
410 * then one instance is as good as another, and it need not be passed anywhere, opening an optimization opportunity.
411 */
412 template <typename T>
413 concept IEqualToOptimizable = equality_comparable<T> and Private_::HasUsableEqualToOptimization<T> ();
414
415 /**
416 * \brief Concept checks if the given type T has a value_type (type) member
417 *
418 * \par Example Usage
419 * \code
420 * if constexpr (IHasValueType<T>) {
421 * typename T::value_type x;
422 * }
423 * \endcode
424 *
425 * \note this replaces Stroika v2.1 constexpr inline bool has_value_type_v template variable
426 */
427 template <typename T>
428 concept IHasValueType = requires (T t) { typename T::value_type; };
429
430 namespace Private_ {
431 template <typename T, typename = void>
432 struct ExtractValueType {
433 using type = void;
434 };
435 template <IHasValueType T>
436 struct ExtractValueType<T> {
437 using type = typename T::value_type;
438 };
439 template <typename T>
440 struct ExtractValueType<const T*, void> {
441 using type = T;
442 };
443 template <typename T>
444 struct ExtractValueType<T*, void> {
445 using type = T;
446 };
447 }
448
449 /**
450 * \brief Extract the type of elements in a container, or returned by an iterator (value_type) or void it there is no value_type
451 *
452 * \note when known if argument is container or iterator, use std::iter_value_t, or std::ranges::range_value_t
453 *
454 * If the given T has a field value_type, return it; returns void if T has no value_type
455 *
456 * NOTE - similar to std::ranges::range_value_t or std::iter_value_t except works with other types.
457 */
458 template <typename T>
459 using ExtractValueType_t = typename Private_::ExtractValueType<remove_cvref_t<T>>::type;
460
461 /**
462 * from https://stackoverflow.com/questions/32785105/implementing-a-switch-type-trait-with-stdconditional-t-chain-calls
463 * \par Example Usage
464 * \code
465 * using Type = Select_t<Case<false, void>,
466 * Case<false, int>,
467 * Case<true, std::string>>;
468 * \endcode
469 */
470 template <bool B, typename T>
471 struct Case {
472 static constexpr bool value = B;
473 using type = T;
474 };
475 template <typename HEAD, typename... TAIL>
476 struct Select : std::conditional_t<HEAD::value, HEAD, Select<TAIL...>> {};
477 template <typename T>
478 struct Select<T> {
479 using type = T;
480 };
481 template <bool B, typename T>
482 struct Select<Case<B, T>> {
483 // last one had better be true!
484 static_assert (B, "!");
485 using type = T;
486 };
487 template <typename HEAD, typename... TAIL>
488 using Select_t = typename Select<HEAD, TAIL...>::type;
489
490 /////////////////////////////////////////////////////////////
491 ////////////////////// DEPREACTED BELOW /////////////////////
492 /////////////////////////////////////////////////////////////
493
494 DISABLE_COMPILER_MSC_WARNING_START (4996);
495 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
496 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
497 template <typename T>
498 [[deprecated ("Since Stroika v3.0d1, use IEqualToOptimizable concept")]] constexpr bool HasUsableEqualToOptimization ()
499 {
500 return IEqualToOptimizable<T>;
501 }
502
503 template <typename T>
504 [[deprecated ("Since Stroika v3.0d1, use require expression")]] constexpr inline bool has_plus_v = requires (T t) {
505 { t + t };
506 };
507 namespace Private_ {
508 template <typename From, typename To>
509 struct is_explicitly_convertible {
510 template <typename T>
511 static void f (T);
512
513 template <typename F, typename T>
514 static constexpr auto test (int) -> decltype (f (static_cast<T> (declval<F> ())), true)
515 {
516 return true;
517 }
518
519 template <typename F, typename T>
520 static constexpr auto test (...) -> bool
521 {
522 return false;
523 }
524
525 static bool const value = test<From, To> (0);
526 };
527 }
528 template <typename From, typename To>
529 [[deprecated ("Since Stroika v3.0d1 - not sure any point - probalyuse convertible or constructible concepts")]] constexpr inline bool is_explicitly_convertible_v =
530 Private_::is_explicitly_convertible<From, To>::value;
531
532 template <typename T>
533 [[deprecated ("Since Stroika v3.0d1, use IHasSizeMethod")]] constexpr inline bool has_size_v = IHasSizeMethod<T>;
534
535 namespace Private_ {
536 template <typename T>
537 using has_beginend_t = decltype (static_cast<bool> (begin (declval<T&> ()) != end (declval<T&> ())));
538 }
539 template <typename T>
540 [[deprecated ("Since Stroika v3.0d1, use std::ranges::range (probably - roughly same)")]] constexpr inline bool has_beginend_v =
541 is_detected_v<Private_::has_beginend_t, T>;
542
543 namespace Private_ {
544 template <typename T>
545 using has_minus_t = decltype (std::declval<T> () - std::declval<T> ());
546 }
547 template <typename T>
548 [[deprecated ("Since Stroika v3.0d1, use something else, either requires statment, or random_access_iterator for "
549 "example")]] constexpr inline bool has_minus_v = is_detected_v<Private_::has_minus_t, T>;
550 template <typename T, typename U>
551 [[deprecated ("Since Stroika v3.0d1, use something else, either requires statment, or random_access_iterator for "
552 "example")]] constexpr inline bool has_minus_v<std::pair<T, U>> = has_minus_v<T> and has_minus_v<U>;
553 template <typename... Ts>
554 [[deprecated ("Since Stroika v3.0d1, use something else, either requires statment, or random_access_iterator for "
555 "example")]] constexpr inline bool has_minus_v<std::tuple<Ts...>> = (has_minus_v<Ts> and ...);
556 namespace Private_ {
557 template <typename T>
558 using has_neq_t = decltype (static_cast<bool> (std::declval<T> () != std::declval<T> ()));
559 }
560 template <typename T>
561 [[deprecated ("Since Stroika v3.0d1, use equality_comparable (cuz in C++20 basically same) concept")]] constexpr inline bool has_neq_v =
562 is_detected_v<Private_::has_neq_t, T>;
563 template <typename T, typename U>
564 [[deprecated ("Since Stroika v3.0d1, use equality_comparable (cuz in C++20 basically same) concept")]] constexpr inline bool has_neq_v<std::pair<T, U>> =
565 has_neq_v<T> and has_neq_v<U>;
566 template <typename... Ts>
567 [[deprecated ("Since Stroika v3.0d1, use equality_comparable (cuz in C++20 basically same) concept")]] constexpr inline bool has_neq_v<std::tuple<Ts...>> =
568 (has_neq_v<Ts> and ...);
569
570 template <typename T>
571 [[deprecated ("Since Stroika v3.0d1, use equality_comparable concept")]] constexpr inline bool has_eq_v = equality_comparable<T>;
572
573 template <typename T>
574 [[deprecated ("Since Stroika v3.0d1, use totally_ordered concept")]] constexpr inline bool has_lt_v = totally_ordered<T>;
575
576 template <typename T>
577 [[deprecated ("Since Stroika v3.0d1, use IHasValueType concept")]] constexpr inline bool has_value_type_v = IHasValueType<T>;
578 template <typename T>
579 [[deprecated ("Since Stroika v3.0d1, use https://en.cppreference.com/w/cpp/concepts/equality_comparable")]] constexpr bool EqualityComparable ()
580 {
581 return equality_comparable<T>;
582 }
583
584 template <typename T>
585 [[deprecated ("Since Stroika v3.0d1, use https://en.cppreference.com/w/cpp/concepts/totally_ordered - NOT SAME THING AT ALL, BUT "
586 "CLOSEST IN STANDARD")]] constexpr bool
587 LessThanComparable ()
588 {
589 return has_lt_v<T>;
590 }
591
592 namespace Private_ {
593 // From https://stackoverflow.com/questions/15393938/find-out-if-a-c-object-is-callable
594 template <typename T>
595 struct is_callable_impl_ {
596 private:
597 typedef char (&yes)[1];
598 typedef char (&no)[2];
599
600 struct Fallback {
601 void operator() ();
602 };
603 struct Derived : T, Fallback {};
604
605 template <typename U, U>
606 struct Check;
607
608 template <typename>
609 static yes test (...);
610
611 template <typename C>
612 static no test (Check<void (Fallback::*) (), &C::operator()>*);
613
614 public:
615 static const bool value = sizeof (test<Derived> (0)) == sizeof (yes);
616 };
617 template <typename T>
618 using is_callable = conditional_t<is_class_v<T>, is_callable_impl_<T>, false_type>;
619 }
620 template <typename T>
621 [[deprecated ("Since Stroika v3.0d1, use https://en.cppreference.com/w/cpp/concepts/invocable")]] constexpr bool is_callable_v =
622 Private_::is_callable<T>::value;
623 template <typename ITERABLE>
624 [[deprecated ("Since Stroika v3.0d1, use ranges::range")]] constexpr bool IsIterable_v =
625 has_beginend_v<ITERABLE> and not same_as<ExtractValueType_t<ITERABLE>, void>;
626
627 namespace Private_ {
628
629 // Would be nice to simplify, but my current version of is_detected_v only takes one template parameter, and the std::experimental version not included in VS2k19
630 template <typename ITERABLE_OF_T, typename T>
631 struct IsIterableOfT_Impl2_ {
632 template <typename X, typename USE_ITERABLE = ITERABLE_OF_T,
633 bool ITER_RESULT_CONVERTIBLE_TO_T = is_convertible_v<decltype (*begin (declval<USE_ITERABLE&> ())), T>>
634 static auto check (const X& x)
635 -> conditional_t<is_detected_v<has_beginend_t, ITERABLE_OF_T> and ITER_RESULT_CONVERTIBLE_TO_T, substitution_succeeded<T>, substitution_failure>;
636 static substitution_failure check (...);
637 using type = decltype (check (declval<T> ()));
638 };
639 template <typename ITERABLE_OF_T, typename T>
640 using IsIterableOfT_t = integral_constant<bool, not same_as<typename IsIterableOfT_Impl2_<ITERABLE_OF_T, T>::type, substitution_failure>>;
641 }
642 template <typename ITERABLE_OF_T, typename T>
643 [[deprecated ("Since Stroika v3.0d1 use Traversal::IIterableOfTo concept")]] constexpr bool IsIterableOfT_v =
644 Private_::IsIterableOfT_t<ITERABLE_OF_T, T>::value;
645
646 namespace Private_ {
647 template <typename T, typename = void>
648 struct is_iterator {
649 static constexpr bool value = false;
650 };
651 template <typename T>
652 struct is_iterator<T, enable_if_t<!same_as<typename iterator_traits<T>::value_type, void>>> {
653 static constexpr bool value = true;
654 };
655 }
656 template <typename T>
657 [[deprecated ("Since Stroika v3.0d1, use input_iterator, forward_iterator, or some other sort of std iterator concept")]] constexpr bool IsIterator_v =
658 Private_::is_iterator<remove_cvref_t<T>>::value;
659
660 template <typename FUNCTOR_ARG, typename FUNCTOR>
661 [[deprecated ("Since Stroika v3.0d1, use std::predicate<F,ARG,ARG>")]] constexpr bool IsTPredicate ()
662 {
663 using T = remove_cvref_t<FUNCTOR_ARG>;
664 if constexpr (is_invocable_v<FUNCTOR, T>) {
665 return std::is_convertible_v<std::invoke_result_t<FUNCTOR, T>, bool>;
666 }
667 return false;
668 }
669 template <typename T>
670 [[deprecated ("Since Stroika v3.0d1, use three_way_comparable")]] constexpr inline bool has_spaceship_v = three_way_comparable<T>;
671 DISABLE_COMPILER_MSC_WARNING_END (4996);
672 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
673 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
674}
675
676/*
677 ********************************************************************************
678 ***************************** Implementation Details ***************************
679 ********************************************************************************
680 */
681#include "Concepts.inl"
682
683#endif /*_Stroika_Foundation_Common_Concept_h_ */
handy re-usable concept, with the obvious meaning, and strangely omitted from std-c++ (though used in...
Definition Concepts.h:213
concept - trivial shorthand for variadic same_as A or same_as B, or ...
Definition Concepts.h:189
concept true if integral or floatpoint type 'T'. Not sure why not provided by std c++
Definition Concepts.h:69
Concept checks if the given type T has a const size() method which can be called to return a size_t.
Definition Concepts.h:375
Concept checks if the given type T has a value_type (type) member.
Definition Concepts.h:428
like std::invocable concept, except also requires the invocation doesn't raise exceptions
Definition Concepts.h:146
return true iff argument type T, is std::pair<a,b> for some a/b types
Definition Concepts.h:267
return true iff argument type T, is std::shared_ptr<A> for some A types
Definition Concepts.h:283
Concept ITuple<T> check if T is a tuple.
Definition Concepts.h:312
detect if T is a std::variant<> type.
Definition Concepts.h:323
equality_comparable_with, but less strict - just checks if it can be equality compared!
Definition Concepts.h:224
like convertible_to, but also handling cases where T has an explict CTOR taking From
Definition Concepts.h:237
concept version of std::is_trivially_copyable_v
Definition Concepts.h:195
typename Private_::ExtractValueType< remove_cvref_t< T > >::type ExtractValueType_t
Extract the type of elements in a container, or returned by an iterator (value_type) or void it there...
Definition Concepts.h:459
Extract the number of arguments, return type, and each individual argument type from a lambda or simp...
Definition Concepts.h:95