Stroika Library 3.0d16
 
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 // From https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
151 template <typename T>
152 concept IDuration =
153 requires { []<typename Rep, typename Period> (type_identity<chrono::duration<Rep, Period>>) {}(type_identity<T> ()); };
154 static_assert (not IDuration<float>);
155
156 // From https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
157 template <typename T>
158 concept ITimePoint =
159 requires { []<typename CLOCK, typename DURATION> (type_identity<chrono::time_point<CLOCK, DURATION>>) {}(type_identity<T> ()); };
160 static_assert (not ITimePoint<float>);
161
162 /**
163 * \brief concept - trivial shorthand for variadic same_as A or same_as B, or ...
164 *
165 * \par Example Usage
166 * \code
167 * template <typename T>
168 * 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>;
169 *
170 * template <typename T>
171 * concept IBasicUNICODECodePoint = Common::IAnyOf<remove_cv_t<T>, char8_t, char16_t, char32_t>;
172 * \endcode
173 */
174 template <typename T, typename... U>
175 concept IAnyOf = (same_as<T, U> or ...);
176
177 /**
178 * \brief concept version of std::is_trivially_copyable_v
179 */
180 template <typename T>
181 concept trivially_copyable = is_trivially_copyable_v<T>;
182
183 /**
184 * A template which ignores its template arguments, and always returns true_type;
185 * NOT crazy - helpful is template metaprogramming.
186 */
187 template <typename...>
188 using True = true_type;
189
190 namespace Private_ {
191 template <class _Ty>
192 concept _Boolean_testable_impl = convertible_to<_Ty, bool>;
193 }
194
195 /**
196 * \brief handy re-usable concept, with the obvious meaning, and strangely omitted from std-c++ (though used in exposition).
197 */
198 template <class _Ty>
199 concept Boolean_testable = Private_::_Boolean_testable_impl<_Ty> && requires (_Ty&& __t) {
200 { !static_cast<_Ty&&> (__t) } -> Private_::_Boolean_testable_impl;
201 };
202
203 /**
204 * \brief equality_comparable_with, but less strict - just checks if it can be equality compared!
205 *
206 * INSPIRATION: https://godbolt.org/z/qevGWKan4
207 * static_assert (equality_comparable_with<nullopt_t, optional<int>>); // note this fails
208 */
209 template <class _Ty1, class _Ty2>
210 concept Weak_Equality_Comparable_With = requires (const remove_reference_t<_Ty1>& __x, const remove_reference_t<_Ty2>& __y) {
211 { __x == __y } -> Boolean_testable;
212 { __x != __y } -> Boolean_testable;
213 };
214 static_assert (not equality_comparable_with<nullopt_t, optional<int>>);
216
217 /**
218 * \brief like convertible_to, but also handling cases where T has an explict CTOR taking From
219 *
220 * \see https://stackoverflow.com/questions/76547398/stdconvertible-to-failing-to-recognize-explicitly-convertible-types
221 */
222 template <class FROM, class TO>
223 concept explicitly_convertible_to = requires { static_cast<TO> (std::declval<FROM> ()); };
224
225 /**
226 */
227 template <typename OT>
228 concept IOptional = same_as<remove_cvref_t<OT>, std::optional<typename OT::value_type>>;
229 static_assert (IOptional<std::optional<int>>);
230 static_assert (not IOptional<int>);
231
232 namespace Private_ {
233#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
234 template <typename T1, typename T2 = void>
235 struct is_shared_ptr_ : std::false_type {};
236 template <typename T1>
237 struct is_shared_ptr_<shared_ptr<T1>> : std::true_type {};
238 template <typename T1, typename T2 = void>
239 struct is_pair_ : std::false_type {};
240 template <typename T1, typename T2>
241 struct is_pair_<pair<T1, T2>> : std::true_type {};
242 template <typename... ARGS>
243 struct is_variant_ : std::false_type {};
244 template <typename... ARGS>
245 struct is_variant_<variant<ARGS...>> : std::true_type {};
246#endif
247 }
248
249 /**
250 * \brief return true iff argument type T, is std::pair<a,b> for some a/b types
251 */
252 template <typename T>
253 concept IPair =
254#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
255 Private_::is_pair_<T>::value
256#else
257 requires (T t) {
258 {
259 []<typename T1, typename T2> (pair<T1, T2>) {}(t)
260 };
261 }
262#endif
263 ;
264
265 /**
266 * \brief return true iff argument type T, is std::shared_ptr<A> for some A types
267 */
268 template <typename T>
269 concept ISharedPtr =
270#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
271 Private_::is_shared_ptr_<T>::value
272#else
273
274 requires (T t) {
275 {
276 []<typename T1> (shared_ptr<T1>) {}(t)
277 };
278 }
279#endif
280 ;
281 static_assert (ISharedPtr<shared_ptr<int>>);
282 static_assert (not ISharedPtr<int>);
283
284 namespace Private_ {
285 template <typename T, std::size_t N>
286 concept has_tuple_element = requires (T t) {
287 typename std::tuple_element_t<N, std::remove_const_t<T>>;
288 { get<N> (t) } -> std::convertible_to<const std::tuple_element_t<N, T>&>;
289 };
290 }
291
292 /**
293 * \brief Concept ITuple<T> check if T is a tuple.
294 *
295 * based on https://stackoverflow.com/questions/68443804/c20-concept-to-check-tuple-like-types
296 */
297 template <typename T>
298 concept ITuple = !std::is_reference_v<T> && requires (T t) {
299 typename std::tuple_size<T>::type;
300 requires std::derived_from<std::tuple_size<T>, std::integral_constant<std::size_t, std::tuple_size_v<T>>>;
301 } && []<std::size_t... N> (std::index_sequence<N...>) {
302 return (Private_::has_tuple_element<T, N> && ...);
303 }(std::make_index_sequence<std::tuple_size_v<T>> ());
304
305 /**
306 * \brief - detect if T is a std::variant<> type.
307 */
308 template <typename T>
309 concept IVariant =
310#if qCompilerAndStdLib_template_concept_matcher_requires_Buggy
311 Private_::is_variant_<T>::value
312#else
313 requires (T t) {
314 {
315 []<typename... TYPES> (variant<TYPES...>) {}(t)
316 };
317 }
318#endif
319 ;
320 static_assert (not IVariant<int>);
321 static_assert (IVariant<variant<int>>);
322
323 /**
324 * Concepts let you construct a 'template' of one arg from one with two args, but class, and variable templates don't allow
325 * this; but this magic trick of double indirection does allow it. And cannot use concepts as template arguments to another template
326 * sadly, so need this trick...
327 *
328 * The 'test' here just invokes convertible_to<TEST_ARGUMENT, T>
329 */
330 template <typename T>
332 template <typename TEST_ARGUMENT>
333 using Test = conditional_t<convertible_to<TEST_ARGUMENT, T>, true_type, false_type>;
334 };
335
336 /**
337 * Concepts let you construct a 'template' of one arg from one with two args, but class, and variable templates don't allow
338 * this; but this magic trick of double indirection does allow it. And cannot use concepts as template arguments to another template
339 * sadly, so need this trick...
340 *
341 * The 'test' here just invokes constructible_from<TEST_ARGUMENT, T>
342 */
343 template <typename T>
345 template <typename TEST_ARGUMENT>
346 using Test = conditional_t<constructible_from<TEST_ARGUMENT, T>, true_type, false_type>;
347 };
348
349 /**
350 * \brief Concept checks if the given type T has a const size() method which can be called to return a size_t.
351 *
352 * \par Example Usage
353 * \code
354 * if constexpr (IHasSizeMethod<T>) {
355 * T a{};
356 * return a.size ();
357 * }
358 * \endcode
359 */
360 template <typename T>
361 concept IHasSizeMethod = requires (const T& t) {
362 { t.size () } -> std::convertible_to<size_t>;
363 };
364
365 namespace Private_ {
366 template <typename T>
367 concept HasEq_ = requires (T t) {
368 { t == t } -> std::convertible_to<bool>;
369 };
370 template <typename T>
371 constexpr inline bool HasEq_v_ = HasEq_<T>;
372 template <typename T, typename U>
373 constexpr inline bool HasEq_v_<std::pair<T, U>> = HasEq_v_<T> and HasEq_v_<U>;
374 template <typename... Ts>
375 constexpr inline bool HasEq_v_<std::tuple<Ts...>> = (HasEq_v_<Ts> and ...);
376 template <typename T>
377 constexpr bool HasUsableEqualToOptimization ()
378 {
379 // static_assert (Common::IOperatorEq<remove_cvref_t<T>> and ! equality_comparable<T>);
380 // static_assert (not Common::IOperatorEq<T> and equality_comparable<T>);
381 // static_assert (Common::IOperatorEq<remove_cvref_t<T>> == equality_comparable<T>);
382 // @todo figure out why Private_::HasEq_v_ needed and cannot use equality_comparable
383 if constexpr (Private_::HasEq_v_<T>) {
384 struct EqualToEmptyTester_ : equal_to<T> {
385 int a;
386 };
387 // leverage empty base class optimization to see if equal_to contains any real data
388 return sizeof (EqualToEmptyTester_) == sizeof (int);
389 }
390 return false;
391 }
392 }
393
394 /**
395 * Check if equal_to<T> is both well defined, and contains no data. The reason it matters that it contains no data, is because
396 * then one instance is as good as another, and it need not be passed anywhere, opening an optimization opportunity.
397 */
398 template <typename T>
399 concept IEqualToOptimizable = Private_::HasUsableEqualToOptimization<T> ();
400
401 /**
402 * \brief Concept checks if the given type T has a value_type (type) member
403 *
404 * \par Example Usage
405 * \code
406 * if constexpr (IHasValueType<T>) {
407 * typename T::value_type x;
408 * }
409 * \endcode
410 *
411 * \note this replaces Stroika v2.1 constexpr inline bool has_value_type_v template variable
412 */
413 template <typename T>
414 concept IHasValueType = requires (T t) { typename T::value_type; };
415
416 namespace Private_ {
417 template <typename T, typename = void>
418 struct ExtractValueType {
419 using type = void;
420 };
421 template <IHasValueType T>
422 struct ExtractValueType<T> {
423 using type = typename T::value_type;
424 };
425 template <typename T>
426 struct ExtractValueType<const T*, void> {
427 using type = T;
428 };
429 template <typename T>
430 struct ExtractValueType<T*, void> {
431 using type = T;
432 };
433 }
434
435 /**
436 * \brief Extract the type of elements in a container, or returned by an iterator (value_type) or void it there is no value_type
437 *
438 * \note when known if argument is container or iterator, use std::iter_value_t, or std::ranges::range_value_t
439 *
440 * If the given T has a field value_type, return it; returns void if T has no value_type
441 *
442 * NOTE - similar to std::ranges::range_value_t or std::iter_value_t except works with other types.
443 */
444 template <typename T>
445 using ExtractValueType_t = typename Private_::ExtractValueType<remove_cvref_t<T>>::type;
446
447 /**
448 * from https://stackoverflow.com/questions/32785105/implementing-a-switch-type-trait-with-stdconditional-t-chain-calls
449 * \par Example Usage
450 * \code
451 * using Type = Select_t<Case<false, void>,
452 * Case<false, int>,
453 * Case<true, std::string>>;
454 * \endcode
455 */
456 template <bool B, typename T>
457 struct Case {
458 static constexpr bool value = B;
459 using type = T;
460 };
461 template <typename HEAD, typename... TAIL>
462 struct Select : std::conditional_t<HEAD::value, HEAD, Select<TAIL...>> {};
463 template <typename T>
464 struct Select<T> {
465 using type = T;
466 };
467 template <bool B, typename T>
468 struct Select<Case<B, T>> {
469 // last one had better be true!
470 static_assert (B, "!");
471 using type = T;
472 };
473 template <typename HEAD, typename... TAIL>
474 using Select_t = typename Select<HEAD, TAIL...>::type;
475
476 /////////////////////////////////////////////////////////////
477 ////////////////////// DEPREACTED BELOW /////////////////////
478 /////////////////////////////////////////////////////////////
479
480 DISABLE_COMPILER_MSC_WARNING_START (4996);
481 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
482 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
483 template <typename T>
484 [[deprecated ("Since Stroika v3.0d1, use IEqualToOptimizable concept")]] constexpr bool HasUsableEqualToOptimization ()
485 {
486 return IEqualToOptimizable<T>;
487 }
488
489 template <typename T>
490 [[deprecated ("Since Stroika v3.0d1, use require expression")]] constexpr inline bool has_plus_v = requires (T t) {
491 { t + t };
492 };
493 namespace Private_ {
494 template <typename From, typename To>
495 struct is_explicitly_convertible {
496 template <typename T>
497 static void f (T);
498
499 template <typename F, typename T>
500 static constexpr auto test (int) -> decltype (f (static_cast<T> (declval<F> ())), true)
501 {
502 return true;
503 }
504
505 template <typename F, typename T>
506 static constexpr auto test (...) -> bool
507 {
508 return false;
509 }
510
511 static bool const value = test<From, To> (0);
512 };
513 }
514 template <typename From, typename To>
515 [[deprecated ("Since Stroika v3.0d1 - not sure any point - probalyuse convertible or constructible concepts")]] constexpr inline bool is_explicitly_convertible_v =
516 Private_::is_explicitly_convertible<From, To>::value;
517
518 template <typename T>
519 [[deprecated ("Since Stroika v3.0d1, use IHasSizeMethod")]] constexpr inline bool has_size_v = IHasSizeMethod<T>;
520
521 namespace Private_ {
522 template <typename T>
523 using has_beginend_t = decltype (static_cast<bool> (begin (declval<T&> ()) != end (declval<T&> ())));
524 }
525 template <typename T>
526 [[deprecated ("Since Stroika v3.0d1, use std::ranges::range (probably - roughly same)")]] constexpr inline bool has_beginend_v =
527 is_detected_v<Private_::has_beginend_t, T>;
528
529 namespace Private_ {
530 template <typename T>
531 using has_minus_t = decltype (std::declval<T> () - std::declval<T> ());
532 }
533 template <typename T>
534 [[deprecated ("Since Stroika v3.0d1, use something else, either requires statment, or random_access_iterator for "
535 "example")]] constexpr inline bool has_minus_v = is_detected_v<Private_::has_minus_t, T>;
536 template <typename T, typename U>
537 [[deprecated ("Since Stroika v3.0d1, use something else, either requires statment, or random_access_iterator for "
538 "example")]] constexpr inline bool has_minus_v<std::pair<T, U>> = has_minus_v<T> and has_minus_v<U>;
539 template <typename... Ts>
540 [[deprecated ("Since Stroika v3.0d1, use something else, either requires statment, or random_access_iterator for "
541 "example")]] constexpr inline bool has_minus_v<std::tuple<Ts...>> = (has_minus_v<Ts> and ...);
542 namespace Private_ {
543 template <typename T>
544 using has_neq_t = decltype (static_cast<bool> (std::declval<T> () != std::declval<T> ()));
545 }
546 template <typename T>
547 [[deprecated ("Since Stroika v3.0d1, use equality_comparable (cuz in C++20 basically same) concept")]] constexpr inline bool has_neq_v =
548 is_detected_v<Private_::has_neq_t, T>;
549 template <typename T, typename U>
550 [[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>> =
551 has_neq_v<T> and has_neq_v<U>;
552 template <typename... Ts>
553 [[deprecated ("Since Stroika v3.0d1, use equality_comparable (cuz in C++20 basically same) concept")]] constexpr inline bool has_neq_v<std::tuple<Ts...>> =
554 (has_neq_v<Ts> and ...);
555
556 template <typename T>
557 [[deprecated ("Since Stroika v3.0d1, use equality_comparable concept")]] constexpr inline bool has_eq_v = equality_comparable<T>;
558
559 template <typename T>
560 [[deprecated ("Since Stroika v3.0d1, use totally_ordered concept")]] constexpr inline bool has_lt_v = totally_ordered<T>;
561
562 template <typename T>
563 [[deprecated ("Since Stroika v3.0d1, use IHasValueType concept")]] constexpr inline bool has_value_type_v = IHasValueType<T>;
564 template <typename T>
565 [[deprecated ("Since Stroika v3.0d1, use https://en.cppreference.com/w/cpp/concepts/equality_comparable")]] constexpr bool EqualityComparable ()
566 {
567 return equality_comparable<T>;
568 }
569
570 template <typename T>
571 [[deprecated ("Since Stroika v3.0d1, use https://en.cppreference.com/w/cpp/concepts/totally_ordered - NOT SAME THING AT ALL, BUT "
572 "CLOSEST IN STANDARD")]] constexpr bool
573 LessThanComparable ()
574 {
575 return has_lt_v<T>;
576 }
577
578 namespace Private_ {
579 // From https://stackoverflow.com/questions/15393938/find-out-if-a-c-object-is-callable
580 template <typename T>
581 struct is_callable_impl_ {
582 private:
583 typedef char (&yes)[1];
584 typedef char (&no)[2];
585
586 struct Fallback {
587 void operator() ();
588 };
589 struct Derived : T, Fallback {};
590
591 template <typename U, U>
592 struct Check;
593
594 template <typename>
595 static yes test (...);
596
597 template <typename C>
598 static no test (Check<void (Fallback::*) (), &C::operator()>*);
599
600 public:
601 static const bool value = sizeof (test<Derived> (0)) == sizeof (yes);
602 };
603 template <typename T>
604 using is_callable = conditional_t<is_class_v<T>, is_callable_impl_<T>, false_type>;
605 }
606 template <typename T>
607 [[deprecated ("Since Stroika v3.0d1, use https://en.cppreference.com/w/cpp/concepts/invocable")]] constexpr bool is_callable_v =
608 Private_::is_callable<T>::value;
609 template <typename ITERABLE>
610 [[deprecated ("Since Stroika v3.0d1, use ranges::range")]] constexpr bool IsIterable_v =
611 has_beginend_v<ITERABLE> and not same_as<ExtractValueType_t<ITERABLE>, void>;
612
613 namespace Private_ {
614
615 // 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
616 template <typename ITERABLE_OF_T, typename T>
617 struct IsIterableOfT_Impl2_ {
618 template <typename X, typename USE_ITERABLE = ITERABLE_OF_T,
619 bool ITER_RESULT_CONVERTIBLE_TO_T = is_convertible_v<decltype (*begin (declval<USE_ITERABLE&> ())), T>>
620 static auto check (const X& x)
621 -> conditional_t<is_detected_v<has_beginend_t, ITERABLE_OF_T> and ITER_RESULT_CONVERTIBLE_TO_T, substitution_succeeded<T>, substitution_failure>;
622 static substitution_failure check (...);
623 using type = decltype (check (declval<T> ()));
624 };
625 template <typename ITERABLE_OF_T, typename T>
626 using IsIterableOfT_t = integral_constant<bool, not same_as<typename IsIterableOfT_Impl2_<ITERABLE_OF_T, T>::type, substitution_failure>>;
627 }
628 template <typename ITERABLE_OF_T, typename T>
629 [[deprecated ("Since Stroika v3.0d1 use Traversal::IIterableOfTo concept")]] constexpr bool IsIterableOfT_v =
630 Private_::IsIterableOfT_t<ITERABLE_OF_T, T>::value;
631
632 namespace Private_ {
633 template <typename T, typename = void>
634 struct is_iterator {
635 static constexpr bool value = false;
636 };
637 template <typename T>
638 struct is_iterator<T, enable_if_t<!same_as<typename iterator_traits<T>::value_type, void>>> {
639 static constexpr bool value = true;
640 };
641 }
642 template <typename T>
643 [[deprecated ("Since Stroika v3.0d1, use input_iterator, forward_iterator, or some other sort of std iterator concept")]] constexpr bool IsIterator_v =
644 Private_::is_iterator<remove_cvref_t<T>>::value;
645
646 template <typename FUNCTOR_ARG, typename FUNCTOR>
647 [[deprecated ("Since Stroika v3.0d1, use std::predicate<F,ARG,ARG>")]] constexpr bool IsTPredicate ()
648 {
649 using T = remove_cvref_t<FUNCTOR_ARG>;
650 if constexpr (is_invocable_v<FUNCTOR, T>) {
651 return std::is_convertible_v<std::invoke_result_t<FUNCTOR, T>, bool>;
652 }
653 return false;
654 }
655 template <typename T>
656 [[deprecated ("Since Stroika v3.0d1, use three_way_comparable")]] constexpr inline bool has_spaceship_v = three_way_comparable<T>;
657 DISABLE_COMPILER_MSC_WARNING_END (4996);
658 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
659 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
660}
661
662/*
663 ********************************************************************************
664 ***************************** Implementation Details ***************************
665 ********************************************************************************
666 */
667#include "Concepts.inl"
668
669#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:199
concept - trivial shorthand for variadic same_as A or same_as B, or ...
Definition Concepts.h:175
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:361
Concept checks if the given type T has a value_type (type) member.
Definition Concepts.h:414
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:253
return true iff argument type T, is std::shared_ptr<A> for some A types
Definition Concepts.h:269
Concept ITuple<T> check if T is a tuple.
Definition Concepts.h:298
detect if T is a std::variant<> type.
Definition Concepts.h:309
equality_comparable_with, but less strict - just checks if it can be equality compared!
Definition Concepts.h:210
like convertible_to, but also handling cases where T has an explict CTOR taking From
Definition Concepts.h:223
concept version of std::is_trivially_copyable_v
Definition Concepts.h:181
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:445
Extract the number of arguments, return type, and each individual argument type from a lambda or simp...
Definition Concepts.h:95