OpenKalman
language-features.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and other recursive filters.
3  *
4  * Copyright (c) 2021-2025 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
16 #ifndef OPENKALMAN_LANGUAGE_FEATURES_HPP
17 #define OPENKALMAN_LANGUAGE_FEATURES_HPP
18 
19 #ifdef __clang__
20 # define OPENKALMAN_CPP_FEATURE_CONCEPTS true
21 # define OPENKALMAN_CPP_FEATURE_CONCEPTS_2 (__clang_major__ >= 15) // optimal value may be as low as > 10 (ver. 10.0.0)
22 #elif defined(__GNUC__)
23 # define OPENKALMAN_CPP_FEATURE_CONCEPTS (__GNUC__ >= 20) // optimal value may be as low as > 10 (ver. 10.1.0)
24 # define OPENKALMAN_CPP_FEATURE_CONCEPTS_2 (__GNUC__ >= 12) // optimal value may be as low as > 10 (ver. 10.1.0)
25 #else
26 # define OPENKALMAN_CPP_FEATURE_CONCEPTS true
27 # define OPENKALMAN_CPP_FEATURE_CONCEPTS_2 true
28 #endif
29 
30 
31 #ifdef __cpp_lib_math_constants
32 #include <numbers>
33 namespace OpenKalman::numbers { using namespace std::numbers; }
34 #else
35 // These re-create the c++20 mathematical constants.
36 namespace OpenKalman::numbers
37 {
38 #ifdef __cpp_lib_concepts
39 #include <concepts>
40  template<std::floating_point T> inline constexpr T e_v = 2.718281828459045235360287471352662498L;
41  template<std::floating_point T> inline constexpr T log2e_v = 1.442695040888963407359924681001892137L;
42  template<std::floating_point T> inline constexpr T log10e_v = 0.434294481903251827651128918916605082L;
43  template<std::floating_point T> inline constexpr T pi_v = 3.141592653589793238462643383279502884L;
44  template<std::floating_point T> inline constexpr T inv_pi_v = 0.318309886183790671537767526745028724L;
45  template<std::floating_point T> inline constexpr T inv_sqrtpi_v = 0.564189583547756286948079451560772586L;
46  template<std::floating_point T> inline constexpr T ln2_v = 0.693147180559945309417232121458176568L;
47  template<std::floating_point T> inline constexpr T ln10_v = 2.302585092994045684017991454684364208L;
48  template<std::floating_point T> inline constexpr T sqrt2_v = 1.414213562373095048801688724209698079L;
49  template<std::floating_point T> inline constexpr T sqrt3_v = 1.732050807568877293527446341505872367L;
50  template<std::floating_point T> inline constexpr T inv_sqrt3_v = 0.577350269189625764509148780501957456L;
51  template<std::floating_point T> inline constexpr T egamma_v = 0.577215664901532860606512090082402431L;
52  template<std::floating_point T> inline constexpr T phi_v = 1.618033988749894848204586834365638118L;
53 #else
54 #include <type_traits>
55  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T e_v = 2.718281828459045235360287471352662498L;
56  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T log2e_v = 1.442695040888963407359924681001892137L;
57  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T log10e_v = 0.434294481903251827651128918916605082L;
58  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T pi_v = 3.141592653589793238462643383279502884L;
59  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T inv_pi_v = 0.318309886183790671537767526745028724L;
60  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T inv_sqrtpi_v = 0.564189583547756286948079451560772586L;
61  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T ln2_v = 0.693147180559945309417232121458176568L;
62  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T ln10_v = 2.302585092994045684017991454684364208L;
63  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T sqrt2_v = 1.414213562373095048801688724209698079L;
64  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T sqrt3_v = 1.732050807568877293527446341505872367L;
65  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T inv_sqrt3_v = 0.577350269189625764509148780501957456L;
66  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T egamma_v = 0.577215664901532860606512090082402431L;
67  template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> inline constexpr T phi_v = 1.618033988749894848204586834365638118L;
68 #endif
69 
70  inline constexpr double e = e_v<double>;
71  inline constexpr double log2e = log2e_v<double>;
72  inline constexpr double log10e = log10e_v<double>;
73  inline constexpr double pi = pi_v<double>;
74  inline constexpr double inv_pi = inv_pi_v<double>;
75  inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
76  inline constexpr double ln2 = ln2_v<double>;
77  inline constexpr double ln10 = ln10_v<double>;
78  inline constexpr double sqrt2 = sqrt2_v<double>;
79  inline constexpr double sqrt3 = sqrt3_v<double>;
80  inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
81  inline constexpr double egamma = egamma_v<double>;
82  inline constexpr double phi = phi_v<double>;
83 }
84 #endif
85 
86 
87 // std::size_t literal similar and equivalent to "uz" literal defined in c++23 standard.
88 constexpr std::size_t operator ""_uz(unsigned long long x) { return x; };
89 
90 
91 #ifndef __cpp_lib_integer_comparison_functions
92 namespace OpenKalman
93 {
94  template<typename T, typename U>
95  constexpr bool cmp_equal(T t, U u) noexcept
96  {
97  if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
98  return t == u;
99  else if constexpr (std::is_signed_v<T>)
100  return t >= 0 && std::make_unsigned_t<T>(t) == u;
101  else
102  return u >= 0 && std::make_unsigned_t<U>(u) == t;
103  }
104 
105  template<typename T, typename U>
106  constexpr bool cmp_not_equal(T t, U u) noexcept
107  {
108  return !cmp_equal(t, u);
109  }
110 
111  template<typename T, typename U>
112  constexpr bool cmp_less(T t, U u) noexcept
113  {
114  if constexpr (std::is_signed_v<T> == std::is_signed_v<U>) return t < u;
115  else if constexpr (std::is_signed_v<T>) return t < 0 or std::make_unsigned_t<T>(t) < u;
116  else return u >= 0 and t < std::make_unsigned_t<U>(u);
117  }
118 
119  template<typename T, typename U>
120  constexpr bool cmp_greater(T t, U u) noexcept
121  {
122  return cmp_less(u, t);
123  }
124 
125  template<typename T, typename U>
126  constexpr bool cmp_less_equal(T t, U u) noexcept
127  {
128  return not cmp_less(u, t);
129  }
130 
131  template<typename T, typename U>
132  constexpr bool cmp_greater_equal(T t, U u) noexcept
133  {
134  return not cmp_less(t, u);
135  }
136 }
137 #endif
138 
139 
140 #ifndef __cpp_impl_three_way_comparison
141 namespace OpenKalman
142 {
143  namespace detail
144  {
145  enum struct Ord : signed char { equivalent = 0, less = -1, greater = 1, unordered = 2 };
146  }
147 
148 
150  {
151  struct unspecified { constexpr unspecified(unspecified*) noexcept {} };
152 
153  detail::Ord my_value;
154 
155  explicit constexpr partial_ordering(detail::Ord value) noexcept : my_value(value) {}
156 
157  public:
158 
159  static const partial_ordering less;
160  static const partial_ordering equivalent;
161  static const partial_ordering greater;
162  static const partial_ordering unordered;
163 
164  template<typename I, std::enable_if_t<std::is_constructible_v<std::ptrdiff_t, I>, int> = 0>
165  explicit constexpr partial_ordering(I i) : my_value(static_cast<detail::Ord>(i)) {}
166 
167  [[nodiscard]] friend constexpr bool
168  operator==(partial_ordering v, unspecified) noexcept { return v.my_value == detail::Ord::equivalent; }
169 
170  [[nodiscard]] friend constexpr bool
171  operator==(unspecified, partial_ordering v) noexcept { return v.my_value == detail::Ord::equivalent; }
172 
173  [[nodiscard]] friend constexpr bool
174  operator==(partial_ordering v, partial_ordering w) noexcept { return v.my_value == w.my_value; };
175 
176  [[nodiscard]] friend constexpr bool
177  operator<(partial_ordering v, unspecified) noexcept { return v.my_value == detail::Ord::less; }
178 
179  [[nodiscard]] friend constexpr bool
180  operator<(unspecified, partial_ordering v) noexcept { return v.my_value == detail::Ord::greater; }
181 
182  [[nodiscard]] friend constexpr bool
183  operator>(partial_ordering v, unspecified) noexcept { return v.my_value == detail::Ord::greater; }
184 
185  [[nodiscard]] friend constexpr bool
186  operator>(unspecified, partial_ordering v) noexcept { return v.my_value == detail::Ord::less; }
187 
188  [[nodiscard]] friend constexpr bool
189  operator<=(partial_ordering v, unspecified) noexcept { return v.my_value <= detail::Ord::equivalent; }
190 
191  [[nodiscard]] friend constexpr bool
192  operator<=(unspecified, partial_ordering v) noexcept { return v.my_value == detail::Ord::greater or v.my_value == detail::Ord::equivalent; }
193 
194  [[nodiscard]] friend constexpr bool
195  operator>=(partial_ordering v, unspecified) noexcept { return v.my_value == detail::Ord::greater or v.my_value == detail::Ord::equivalent; }
196 
197  [[nodiscard]] friend constexpr bool
198  operator>=(unspecified, partial_ordering v) noexcept { return v.my_value <= detail::Ord::equivalent; }
199  };
200 
201 
202  // valid values' definitions
203  inline constexpr partial_ordering partial_ordering::less(detail::Ord::less);
204 
205  inline constexpr partial_ordering partial_ordering::equivalent(detail::Ord::equivalent);
206 
207  inline constexpr partial_ordering partial_ordering::greater(detail::Ord::greater);
208 
209  inline constexpr partial_ordering partial_ordering::unordered(detail::Ord::unordered);
210 
211 
213  {
214  template<typename T, typename U>
215  constexpr partial_ordering
216  operator() [[nodiscard]] (T&& t, U&& u) const
217  {
218  if (t == u) return partial_ordering::equivalent;
219  if (t < u) return partial_ordering::less;
220  if (t > u) return partial_ordering::greater;
221  return partial_ordering::unordered;
222  }
223 
224  using is_transparent = void;
225  };
226 
227 }
228 #endif
229 
230 
231 #if not defined(__cpp_lib_remove_cvref) or not defined(__cpp_lib_ranges)
232 namespace OpenKalman
233 {
234  template<typename T>
235  struct remove_cvref { using type = std::remove_cv_t<std::remove_reference_t<T>>; };
236 
237  template<typename T>
238  using remove_cvref_t = typename remove_cvref<T>::type;
239 }
240 #endif
241 
242 
243 namespace OpenKalman::internal
244 {
245  namespace detail
246  {
247  struct decay_copy_impl final
248  {
249  template<typename T>
250  constexpr std::decay_t<T> operator()(T&& t) const noexcept { return std::forward<T>(t); }
251  };
252  }
253 
254  inline constexpr detail::decay_copy_impl decay_copy;
255 }
256 
257 
258 #ifndef __cpp_lib_bounded_array_traits
259 namespace OpenKalman
260 {
261  template<typename T>
262  struct is_bounded_array : std::false_type {};
263 
264  template<typename T, std::size_t N>
265  struct is_bounded_array<T[N]> : std::true_type {};
266 
267  template<typename T>
268  constexpr bool is_bounded_array_v = is_bounded_array<T>::value;
269 }
270 #endif
271 
272 
273 namespace OpenKalman::internal
274 {
275  namespace detail
276  {
277  template<typename T, typename = void>
278  struct is_integer_like_impl : std::false_type {};
279 
280  template<>
281  struct is_integer_like_impl<bool> : std::false_type {};
282 
283  template<>
284  struct is_integer_like_impl<const bool> : std::false_type {};
285 
286  template<>
287  struct is_integer_like_impl<volatile bool> : std::false_type {};
288 
289  template<>
290  struct is_integer_like_impl<const volatile bool> : std::false_type {};
291 
292  template<typename T>
293  struct is_integer_like_impl<T, std::enable_if_t<std::is_integral_v<T>>> : std::true_type {};
294  }
295 
296  template<typename T>
297  inline constexpr bool is_integer_like = detail::is_integer_like_impl<T>::value;
298 
299  template<typename T>
300  inline constexpr bool is_signed_integer_like = is_integer_like<T> and std::is_signed_v<T>;
301 
302  template<typename T>
303  inline constexpr bool is_unsigned_integer_like = is_integer_like<T> and std::is_unsigned_v<T>;
304 
305 }
306 
307 
308 #if __cplusplus < 202002L
309 namespace OpenKalman
310 {
315  namespace detail
316  {
317  template<typename T> constexpr T& reference_wrapper_FUN(T& t) noexcept { return t; }
318  template<typename T> void reference_wrapper_FUN(T&&) = delete;
319  }
320 
321 
322  template<typename T>
324  {
325 #ifdef __cpp_lib_remove_cvref
326  using std::remove_cvref_t;
327 #endif
328 
329  public:
330 
331  using type = T;
332 
333  template<typename U, typename = std::void_t<decltype(detail::reference_wrapper_FUN<T>(std::declval<U>()))>,
334  std::enable_if_t<not std::is_same_v<reference_wrapper, remove_cvref_t<U>>, int> = 0>
335  constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::reference_wrapper_FUN<T>(std::forward<U>(u))))
336  : ptr(std::addressof(detail::reference_wrapper_FUN<T>(std::forward<U>(u)))) {}
337 
338 
339  reference_wrapper(const reference_wrapper&) noexcept = default;
340 
341 
342  reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
343 
344 
345  constexpr operator T& () const noexcept { return *ptr; }
346  constexpr T& get() const noexcept { return *ptr; }
347 
348 
349  template<typename... ArgTypes>
350  constexpr std::invoke_result_t<T&, ArgTypes...>
351  operator() (ArgTypes&&... args ) const noexcept(std::is_nothrow_invocable_v<T&, ArgTypes...>)
352  {
353  return get()(std::forward<ArgTypes>(args)...);
354  }
355 
356  private:
357 
358  T* ptr;
359 
360  };
361 
362  // deduction guides
363  template<typename T>
365 
366 
367  template<typename T>
368  constexpr std::reference_wrapper<T>
369  ref(T& t) noexcept { return {t}; };
370 
371  template<typename T>
372  constexpr std::reference_wrapper<T>
373  ref(std::reference_wrapper<T> t) noexcept { return std::move(t); };
374 
375  template<typename T>
376  void ref(const T&&) = delete;
377 
378  template<typename T>
379  constexpr std::reference_wrapper<const T>
380  cref(const T& t) noexcept { return {t}; };
381 
382  template<typename T>
383  constexpr std::reference_wrapper<const T>
384  cref(std::reference_wrapper<T> t) noexcept { return std::move(t); };
385 
386  template<typename T>
387  void cref(const T&&) = delete;
388 }
389 #endif
390 
391 
392 #ifndef __cpp_lib_concepts
393 namespace OpenKalman
394 {
395  // ---
396  // Basic concepts
397  // ---
398 
399  struct identity
400  {
401  template<typename T>
402  [[nodiscard]] constexpr T&& operator()(T&& t) const noexcept { return std::forward<T>(t); }
403 
404  struct is_transparent; // undefined
405  };
406 
407 
408  template<typename T>
409  struct type_identity { using type = T; };
410 
411  template<typename T>
412  using type_identity_t = typename type_identity<T>::type;
413 
414 
415  template<typename T, typename...Args>
416  inline constexpr bool
417  destructible = std::is_nothrow_destructible_v<T>;
418 
419 
420  template<typename T, typename...Args>
421  inline constexpr bool
422  constructible_from = destructible<T> and std::is_constructible_v<T, Args...>;
423 
424 
425  namespace detail
426  {
427  template<typename From, typename To, typename = void>
428  struct convertible_to_impl : std::false_type {};
429 
430  template<typename From, typename To>
431  struct convertible_to_impl<From, To, std::void_t<decltype(static_cast<To>(std::declval<From>()))>> : std::true_type {};
432  }
433 
434  template<typename From, typename To>
435  inline constexpr bool
436  convertible_to = std::is_convertible_v<From, To> and detail::convertible_to_impl<From, To>::value;
437 
438 
439  template<typename T>
440  inline constexpr bool
441  move_constructible = constructible_from<T, T> and convertible_to<T, T>;
442 
443 
444  template<typename T>
445  inline constexpr bool copy_constructible =
446  move_constructible<T> and
447  constructible_from<T, T&> and convertible_to<T&, T> and
448  constructible_from<T, const T&> && convertible_to<const T&, T> and
449  constructible_from<T, const T> && convertible_to<const T, T>;
450 
451 
452  template<typename T>
453  inline constexpr bool
454  movable =
455  std::is_object_v<T> and
456  std::is_move_constructible_v<T> and
457  std::is_assignable_v<T&, T> /*and
458  std::swappable<T>*/;
459 
460 
461  template<typename T>
462  inline constexpr bool
463  copyable =
464  std::is_copy_constructible_v<T> and
465  movable<T> and
466  std::is_assignable_v<T&, T&> and
467  std::is_assignable_v<T&, const T&> and
468  std::is_assignable_v<T&, const T>;
469 
470 
471  template<typename T>
472  inline constexpr bool
473  semiregular = copyable<T> and std::is_default_constructible_v<T>;
474 
475 }
476 #endif
477 
478 
479 // ---
480 // invoke, invoke_r
481 // ---
482 
483 #if __cplusplus < 202002L
484 namespace OpenKalman
485 {
486  namespace detail
487  {
488  template<typename> static constexpr bool is_reference_wrapper_v = false;
489  template<typename U> static constexpr bool is_reference_wrapper_v<std::reference_wrapper<U>> = true;
490 
491  template<typename T> using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
492 
493  template<typename C, typename P, typename O, typename...Args>
494  static constexpr decltype(auto)
495  invoke_memptr(P C::* member, O&& object, Args&&... args)
496  {
497  if constexpr (std::is_function_v<P>)
498  {
499  if constexpr (std::is_same_v<C, remove_cvref_t<O>> or std::is_base_of_v<C, remove_cvref_t<O>>)
500  return (std::forward<O>(object) .* member)(std::forward<Args>(args)...);
501  else if constexpr (is_reference_wrapper_v<remove_cvref_t<O>>)
502  return (object.get() .* member)(std::forward<Args>(args)...);
503  else
504  return ((*std::forward<O>(object)) .* member)(std::forward<Args>(args)...);
505  }
506  else
507  {
508  static_assert(std::is_object_v<P> && sizeof...(args) == 0);
509  if constexpr (std::is_same_v<C, remove_cvref_t<O>> or std::is_base_of_v<C, remove_cvref_t<O>>)
510  return std::forward<O>(object) .* member;
511  else if constexpr (is_reference_wrapper_v<remove_cvref_t<O>>)
512  return object.get() .* member;
513  else
514  return (*std::forward<O>(object)) .* member;
515  }
516  }
517  }
518 
519 
524  template<typename F, typename...Args>
525  static constexpr decltype(auto)
526  invoke(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_v<F, Args...>)
527  {
528  if constexpr (std::is_member_pointer_v<remove_cvref_t<F>>)
529  return invoke_memptr(f, std::forward<Args>(args)...);
530  else
531  return std::forward<F>(f)(std::forward<Args>(args)...);
532  }
533 }
534 #endif
535 
536 
537 #if __cplusplus < 202302L
538 namespace OpenKalman
539 {
544 #ifdef __cpp_concepts
545  template<typename R, typename F, typename...Args> requires std::is_invocable_r_v<R, F, Args...>
546 #else
547  template<typename R, typename F, typename...Args, std::enable_if_t<std::is_invocable_r_v<R, F, Args...>, int> = 0>
548 #endif
549  constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>)
550  {
551 #if __cplusplus < 202002L
552  namespace s = OpenKalman;
553 #else
554  namespace s = std;
555 #endif
556  if constexpr (std::is_void_v<R>)
557  s::invoke(std::forward<F>(f), std::forward<Args>(args)...);
558  else
559  return s::invoke(std::forward<F>(f), std::forward<Args>(args)...);
560  }
561 }
562 #endif
563 
564 
565 namespace OpenKalman::internal
566 {
567  namespace std_get_detail
568  {
569  using std::get;
570 
571 
572 #ifndef __cpp_concepts
573  template<std::size_t i, typename T, typename = void>
574  struct member_get_is_defined : std::false_type {};
575 
576  template<std::size_t i, typename T>
577  struct member_get_is_defined<i, T, std::void_t<decltype(std::declval<T>().template get<i>())>> : std::true_type {};
578 
579 
580  template<std::size_t i, typename T, typename = void>
581  struct function_get_is_defined : std::false_type {};
582 
583  template<std::size_t i, typename T>
584  struct function_get_is_defined<i, T, std::void_t<decltype(get<i>(std::declval<T>()))>> : std::true_type {};
585 #endif
586 
587 
588  template<std::size_t i>
589  struct get_impl
590  {
591 #ifdef __cpp_concepts
592  template<typename T> requires
593  requires { std::declval<T&&>().template get<i>(); } or
594  requires { get<i>(std::declval<T&&>()); }
595 #else
597 #endif
598  constexpr decltype(auto)
599  operator() [[nodiscard]] (T&& t) const
600  {
601 #ifdef __cpp_concepts
602  if constexpr (requires { std::forward<T>(t).template get<i>(); })
603 #else
605 #endif
606  return std::forward<T>(t).template get<i>();
607  else
608  return get<i>(std::forward<T>(t));
609  }
610  };
611  }
612 
613 
618  template<std::size_t i>
619  inline constexpr std_get_detail::get_impl<i>
620  generalized_std_get;
621 
622 }
623 
624 
625 #endif //OPENKALMAN_LANGUAGE_FEATURES_HPP
Definition: language-features.hpp:399
Definition: language-features.hpp:262
A generalization of std::greater in which the arguments may be of different types.
Definition: greater.hpp:29
decltype(auto) constexpr get(Arg &&arg, I i)
A generalization of std::get.
Definition: get.hpp:62
Definition: language-features.hpp:36
A generalization of std::less in which the arguments may be of different types.
Definition: less.hpp:29
Definition: tuple_reverse.hpp:103
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
Definition: language-features.hpp:212
Definition: language-features.hpp:149
The root namespace for OpenKalman.
Definition: basics.hpp:34
Definition: language-features.hpp:589
Definition: language-features.hpp:278
Definition: language-features.hpp:247
Definition: language-features.hpp:409
Definition: language-features.hpp:428
Definition: basics.hpp:48
Definition: language-features.hpp:323