Browse Source

stl containers: enhance compatibility with C++14, C++17 and C++20

* Ensure various functions working with containers are available through
all headers as specified by the standard.
* Add C++20 std::ssize() function.
Marc Chevrier 3 years ago
parent
commit
95a1b2c7b8

+ 95 - 9
Help/dev/source.rst

@@ -35,6 +35,18 @@ Available features are:
 
 * From ``C++14``:
 
+  * ``<cm/array>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
+  * ``<cm/deque>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
+  * ``<cm/forward_list>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
   * ``<cm/iomanip>``:
     ``cm::quoted``
 
@@ -42,68 +54,142 @@ Available features are:
     ``cm::make_reverse_iterator``, ``cm::cbegin``, ``cm::cend``,
     ``cm::rbegin``, ``cm::rend``, ``cm::crbegin``, ``cm::crend``
 
+  * ``<cm/list>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
+  * ``<cm/map>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
   * ``<cm/memory>``:
     ``cm::make_unique``
 
+  * ``<cm/set>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
+  * ``<cm/string>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
+  * ``<cm/string_view>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
   * ``<cm/shared_mutex>``:
     ``cm::shared_lock``
 
   * ``<cm/type_traits>``:
     ``cm::enable_if_t``
 
+  * ``<cm/unordered_map>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
+  * ``<cm/unordered_set>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
+  * ``<cm/vector>``:
+    ``cm::cbegin``, ``cm::cend``, ``cm::rbegin``, ``cm::rend``,
+    ``cm::crbegin``, ``cm::crend``
+
 * From ``C++17``:
 
   * ``<cm/algorithm>``:
     ``cm::clamp``
 
+  * ``<cm/array>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
+  * ``<cm/deque>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
   * ``cm/filesystem>``:
     ``cm::filesystem::path``
 
+  * ``<cm/forward_list>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
   * ``<cm/iterator>``:
     ``cm::size``, ``cm::empty``, ``cm::data``
 
+  * ``<cm/list>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
+  * ``<cm/map>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
   * ``<cm/optional>``:
     ``cm::nullopt_t``, ``cm::nullopt``, ``cm::optional``,
     ``cm::make_optional``, ``cm::bad_optional_access``
 
+  * ``<cm/set>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
   * ``<cm/shared_mutex>``:
     ``cm::shared_mutex``
 
+  * ``<cm/string>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
   * ``<cm/string_view>``:
-    ``cm::string_view``
+    ``cm::string_view``, ``cm::size``, ``cm::empty``, ``cm::data``
 
   * ``<cm/type_traits>``:
     ``cm::bool_constant``, ``cm::invoke_result_t``, ``cm::invoke_result``,
     ``cm::void_t``
 
+  * ``<cm/unordered_map>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
+  * ``<cm/unordered_set>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
   * ``<cm/utility>``:
     ``cm::in_place_t``, ``cm::in_place``
 
+  * ``<cm/vector>``:
+    ``cm::size``, ``cm::empty``, ``cm::data``
+
 * From ``C++20``:
 
+  * ``<cm/array>``:
+    ``cm::ssize``
+
   * ``<cm/deque>``:
-    ``cm::erase``, ``cm::erase_if``
+    ``cm::erase``, ``cm::erase_if``, ``cm::ssize``
+
+  * ``<cm/forward_list>``:
+    ``cm::ssize``
+
+  * ``<cm/iterator>``:
+    ``cm::ssize``
 
   * ``<cm/list>``:
-    ``cm::erase``, ``cm::erase_if``
+    ``cm::erase``, ``cm::erase_if``, ``cm::ssize``
 
   * ``<cm/map>`` :
-    ``cm::erase_if``
+    ``cm::erase_if``, ``cm::ssize``
 
   * ``<cm/set>`` :
-    ``cm::erase_if``
+    ``cm::erase_if``, ``cm::ssize``
+
+  * ``<cm/string_view>``:
+    ``cm::ssize``
 
   * ``<cm/string>``:
-    ``cm::erase``, ``cm::erase_if``
+    ``cm::erase``, ``cm::erase_if``, ``cm::ssize``
 
   * ``<cm/unordered_map>``:
-    ``cm::erase_if``
+    ``cm::erase_if``, ``cm::ssize``
 
   * ``<cm/unordered_set>``:
-    ``cm::erase_if``
+    ``cm::erase_if``, ``cm::ssize``
 
   * ``<cm/vector>``:
-    ``cm::erase``, ``cm::erase_if``
+    ``cm::erase``, ``cm::erase_if``, ``cm::ssize``
 
 Additionally, some useful non-standard extensions to the C++ standard library
 are available in headers under the directory ``cmext/`` in namespace ``cm``.

+ 10 - 0
Utilities/std/cm/array

@@ -0,0 +1,10 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <array> // IWYU pragma: export
+
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export

+ 302 - 0
Utilities/std/cm/bits/container_helpers.hxx

@@ -0,0 +1,302 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#pragma once
+
+#include <iterator> // IWYU pragma: keep
+
+#if __cplusplus < 201402L || defined(_MSVC_LANG) && _MSVC_LANG < 201402L
+#  include <initializer_list>
+#endif
+#if __cplusplus < 202002L || defined(_MSVC_LANG) && _MSVC_LANG < 202002L
+#  include <cstddef>
+#  include <type_traits>
+#endif
+
+namespace cm {
+
+using std::begin;
+using std::end;
+
+#if __cplusplus < 201402L || defined(_MSVC_LANG) && _MSVC_LANG < 201402L
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto cbegin(const C& c)
+#  else
+inline constexpr auto cbegin(const C& c) noexcept(noexcept(std::begin(c)))
+#  endif
+  -> decltype(std::begin(c))
+{
+  return std::begin(c);
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto cend(const C& c)
+#  else
+inline constexpr auto cend(const C& c) noexcept(noexcept(std::end(c)))
+#  endif
+  -> decltype(std::end(c))
+{
+  return std::end(c);
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto rbegin(C& c)
+#  else
+inline constexpr auto rbegin(C& c)
+#  endif
+  -> decltype(c.rbegin())
+{
+  return c.rbegin();
+}
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto rbegin(const C& c)
+#  else
+inline constexpr auto rbegin(const C& c)
+#  endif
+  -> decltype(c.rbegin())
+{
+  return c.rbegin();
+}
+template <typename T, std::size_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline std::reverse_iterator<T*> rbegin(T (&array)[N])
+#  else
+inline constexpr std::reverse_iterator<T*> rbegin(T (&array)[N]) noexcept
+#  endif
+{
+  return std::reverse_iterator<T*>(array + N);
+}
+template <typename T>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline std::reverse_iterator<const T*> rbegin(std::initializer_list<T> il)
+#  else
+inline constexpr std::reverse_iterator<const T*> rbegin(
+  std::initializer_list<T> il) noexcept
+#  endif
+{
+  return std::reverse_iterator<const T*>(il.end());
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto rend(C& c)
+#  else
+inline constexpr auto rend(C& c)
+#  endif
+  -> decltype(c.rend())
+
+{
+  return c.rend();
+}
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto rend(const C& c)
+#  else
+inline constexpr auto rend(const C& c)
+#  endif
+  -> decltype(c.rend())
+{
+  return c.rend();
+}
+template <typename T, std::size_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline std::reverse_iterator<T*> rend(T (&array)[N])
+#  else
+inline constexpr std::reverse_iterator<T*> rend(T (&array)[N]) noexcept
+#  endif
+{
+  return std::reverse_iterator<T*>(array);
+}
+template <typename T>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline std::reverse_iterator<const T*> rend(std::initializer_list<T> il)
+#  else
+inline constexpr std::reverse_iterator<const T*> rend(
+  std::initializer_list<T> il) noexcept
+#  endif
+{
+  return std::reverse_iterator<const T*>(il.begin());
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto crbegin(const C& c)
+#  else
+inline constexpr auto crbegin(const C& c)
+#  endif
+  -> decltype(cm::rbegin(c))
+{
+  return cm::rbegin(c);
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto crend(const C& c)
+#  else
+inline constexpr auto crend(const C& c)
+#  endif
+  -> decltype(cm::rend(c))
+{
+  return cm::rend(c);
+}
+
+#else
+
+using std::cbegin;
+using std::cend;
+
+using std::rbegin;
+using std::rend;
+
+using std::crbegin;
+using std::crend;
+
+#endif
+
+#if __cplusplus < 201703L || defined(_MSVC_LANG) && _MSVC_LANG < 201703L
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto size(const C& c)
+#  else
+inline constexpr auto size(const C& c) noexcept(noexcept(c.size()))
+#  endif
+  -> decltype(c.size())
+{
+  return c.size();
+}
+
+template <typename T, std::size_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline std::size_t size(const T (&)[N])
+#  else
+inline constexpr std::size_t size(const T (&)[N]) noexcept
+#  endif
+{
+  return N;
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto empty(const C& c)
+#  else
+inline constexpr auto empty(const C& c) noexcept(noexcept(c.empty()))
+#  endif
+  -> decltype(c.empty())
+{
+  return c.empty();
+}
+
+template <typename T, std::size_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline bool empty(const T (&)[N])
+#  else
+inline constexpr bool empty(const T (&)[N]) noexcept
+#  endif
+{
+  return false;
+}
+
+template <typename E>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline bool empty(std::initializer_list<E> il)
+#  else
+inline constexpr bool empty(std::initializer_list<E> il) noexcept
+#  endif
+{
+  return il.size() == 0;
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto data(C& c) -> decltype(c.data())
+#  else
+inline constexpr auto data(C& c) noexcept(noexcept(c.data()))
+#  endif
+  -> decltype(c.data())
+{
+  return c.data();
+}
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto data(const C& c)
+#  else
+inline constexpr auto data(const C& c) noexcept(noexcept(c.data()))
+#  endif
+  -> decltype(c.data())
+{
+  return c.data();
+}
+
+template <typename T, std::size_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline T* data(T (&array)[N])
+#  else
+inline constexpr T* data(T (&array)[N]) noexcept
+#  endif
+{
+  return array;
+}
+
+template <typename E>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline const E* data(std::initializer_list<E> il)
+#  else
+inline constexpr const E* data(std::initializer_list<E> il) noexcept
+#  endif
+{
+  return il.begin();
+}
+
+#else
+
+using std::size;
+using std::empty;
+using std::data;
+
+#endif
+
+#if __cplusplus < 202002L || defined(_MSVC_LANG) && _MSVC_LANG < 202002L
+
+template <typename C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline auto ssize(const C& c)
+#  else
+inline constexpr auto ssize(const C& c)
+#  endif
+  -> typename std::common_type<
+    std::ptrdiff_t, typename std::make_signed<decltype(c.size())>::type>::type
+{
+  using signed_type = typename std::make_signed<decltype(c.size())>::type;
+  using result_type =
+    typename std::common_type<std::ptrdiff_t, signed_type>::type;
+
+  return static_cast<result_type>(c.size());
+}
+
+template <typename T, std::ptrdiff_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+inline std::ptrdiff_t ssize(const T (&)[N])
+#  else
+inline constexpr std::ptrdiff_t ssize(const T (&)[N]) noexcept
+#  endif
+{
+  return N;
+}
+
+#else
+
+using std::ssize;
+
+#endif
+
+} // namespace cm

+ 2 - 0
Utilities/std/cm/deque

@@ -8,6 +8,8 @@
 #include <algorithm>
 #include <deque> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
+
 namespace cm {
 
 // should be updated when C++20 is finalized

+ 10 - 0
Utilities/std/cm/forward_list

@@ -0,0 +1,10 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <forward_list> // IWYU pragma: export
+
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export

+ 2 - 189
Utilities/std/cm/iterator

@@ -7,18 +7,13 @@
 
 #include <iterator> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
+
 namespace cm {
 
 #if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
 using std::make_reverse_iterator;
 
-using std::cbegin;
-using std::cend;
-
-using std::rbegin;
-using std::rend;
-using std::crbegin;
-using std::crend;
 #else
 template <class Iter>
 std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
@@ -26,188 +21,6 @@ std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
   return std::reverse_iterator<Iter>(it);
 }
 
-// std::c{begin,end} backport from C++14
-template <class C>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-auto cbegin(C const& c)
-#  else
-constexpr auto cbegin(C const& c) noexcept(noexcept(std::begin(c)))
-#  endif
-  -> decltype(std::begin(c))
-{
-  return std::begin(c);
-}
-
-template <class C>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-auto cend(C const& c)
-#  else
-constexpr auto cend(C const& c) noexcept(noexcept(std::end(c)))
-#  endif
-  -> decltype(std::end(c))
-{
-  return std::end(c);
-}
-
-// std::r{begin,end} backport from C++14
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  rbegin(C& c) -> decltype(c.rbegin())
-{
-  return c.rbegin();
-}
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  rbegin(C const& c) -> decltype(c.rbegin())
-{
-  return c.rbegin();
-}
-template <typename T, size_t N>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  std::reverse_iterator<T*>
-    rbegin(T (&arr)[N])
-{
-  return std::reverse_iterator<T*>(arr + N);
-}
-
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  rend(C& c) -> decltype(c.rend())
-{
-  return c.rend();
-}
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  rend(C const& c) -> decltype(c.rend())
-{
-  return c.rend();
-}
-template <typename T, size_t N>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  std::reverse_iterator<T*>
-    rend(T (&arr)[N])
-{
-  return std::reverse_iterator<T*>(arr);
-}
-
-// std::cr{begin,end} backport from C++14
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  crbegin(C const& c) -> decltype(cm::rbegin(c))
-{
-  return cm::rbegin(c);
-}
-
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  crend(C const& c) -> decltype(cm::rend(c))
-{
-  return cm::rend(c);
-}
-#endif
-
-#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
-using std::size;
-
-using std::empty;
-using std::data;
-#else
-
-// std::size backport from C++17.
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  size(C const& c) -> decltype(c.size())
-{
-  return c.size();
-}
-
-template <typename T, size_t N>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  std::size_t
-  size(const T (&)[N]) throw()
-{
-  return N;
-}
-
-// std::empty backport from C++17.
-template <class C>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-auto empty(C const& c)
-#  else
-constexpr auto empty(C const& c) noexcept(noexcept(c.empty()))
-#  endif
-  -> decltype(c.empty())
-{
-  return c.empty();
-}
-template <typename T, size_t N>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-bool empty(const T (&)[N])
-#  else
-constexpr bool empty(const T (&)[N]) noexcept
-#  endif
-{
-  return false;
-}
-
-// std::data backport from C++17.
-template <class C>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-auto data(C const& c)
-#  else
-constexpr auto data(C const& c) noexcept(noexcept(c.data()))
-#  endif
-  -> decltype(c.data())
-{
-  return c.data();
-}
-template <class C>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-auto data(C& c)
-#  else
-constexpr auto data(C& c) noexcept(noexcept(c.data()))
-#  endif
-  -> decltype(c.data())
-{
-  return c.data();
-}
-template <typename T, size_t N>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-T* data(T (&)[N])
-#  else
-constexpr T* data(T (&arr)[N]) noexcept
-#  endif
-{
-  return arr;
-}
-
 #endif
 
 } // namespace cm

+ 2 - 0
Utilities/std/cm/list

@@ -7,6 +7,8 @@
 
 #include <list> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
+
 namespace cm {
 
 // should be updated when C++20 is finalized

+ 1 - 0
Utilities/std/cm/map

@@ -7,6 +7,7 @@
 
 #include <map> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
 #include <cm/bits/erase_if.hxx>
 
 namespace cm {

+ 1 - 0
Utilities/std/cm/set

@@ -7,6 +7,7 @@
 
 #include <set> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
 #include <cm/bits/erase_if.hxx>
 
 namespace cm {

+ 2 - 0
Utilities/std/cm/string

@@ -8,6 +8,8 @@
 #include <algorithm>
 #include <string> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
+
 namespace cm {
 
 // should be updated when C++20 is finalized

+ 2 - 0
Utilities/std/cm/string_view

@@ -9,6 +9,8 @@
 #  define CMake_HAVE_CXX_STRING_VIEW
 #endif
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
+
 #ifdef CMake_HAVE_CXX_STRING_VIEW
 #  include <string_view> // IWYU pragma: export
 namespace cm {

+ 1 - 0
Utilities/std/cm/unordered_map

@@ -7,6 +7,7 @@
 
 #include <unordered_map> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
 #include <cm/bits/erase_if.hxx>
 
 namespace cm {

+ 1 - 0
Utilities/std/cm/unordered_set

@@ -7,6 +7,7 @@
 
 #include <unordered_set> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
 #include <cm/bits/erase_if.hxx>
 
 namespace cm {

+ 2 - 0
Utilities/std/cm/vector

@@ -8,6 +8,8 @@
 #include <algorithm>
 #include <vector> // IWYU pragma: export
 
+#include <cm/bits/container_helpers.hxx> // IWYU pragma: export
+
 namespace cm {
 
 // should be updated when C++20 is finalized