Bläddra i källkod

Refactoring: introduce header cmext/algorithm with append functions

Marc Chevrier 5 år sedan
förälder
incheckning
a38d04c076

+ 1 - 0
Tests/CMakeLib/CMakeLists.txt

@@ -26,6 +26,7 @@ set(CMakeLib_TESTS
   testUVRAII.cxx
   testUVStreambuf.cxx
   testCMExtMemory.cxx
+  testCMExtAlgorithm.cxx
   )
 
 add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx)

+ 117 - 0
Tests/CMakeLib/testCMExtAlgorithm.cxx

@@ -0,0 +1,117 @@
+#include <iostream>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <cmext/algorithm>
+
+namespace {
+
+int failed = 0;
+
+void testAppend()
+{
+  std::cout << "testAppend()" << std::endl;
+
+  // ----------------------------------------------------
+  // cm::append(Vector, Iterator, Iterator)
+  {
+    std::vector<int> v1{ 1, 2, 3 };
+    std::vector<int> v1_ref{ 1, 2, 3, 4, 5, 6 };
+    std::vector<int> v2{ 4, 5, 6 };
+    std::vector<int> v2_ref{ 4, 5, 6 };
+
+    cm::append(v1, v2.begin(), v2.end());
+
+    if (v1 != v1_ref || v2 != v2_ref) {
+      ++failed;
+    }
+  }
+
+  // ----------------------------------------------------
+  // cm::append(Vector, Range)
+  {
+    std::vector<int> v1{ 1, 2, 3 };
+    std::vector<int> v1_ref{ 1, 2, 3, 4, 5, 6 };
+    std::vector<int> v2{ 4, 5, 6 };
+    std::vector<int> v2_ref{ 4, 5, 6 };
+
+    cm::append(v1, v2);
+
+    if (v1 != v1_ref || v2 != v2_ref) {
+      ++failed;
+    }
+  }
+
+  // ----------------------------------------------------
+  // cm::append(Vector<*>, Vector<unique_ptr>)
+  {
+    std::vector<int*> v1{ new int(1), new int(2), new int(3) };
+    std::vector<int*> v1_ref = v1;
+    std::vector<std::unique_ptr<int>> v2;
+
+    v2.emplace_back(new int(4));
+    v2.emplace_back(new int(5));
+    v2.emplace_back(new int(6));
+
+    cm::append(v1, v2);
+
+    if (v1.size() == 6 && v2.size() == 3) {
+      for (int i = 0; i < 3; i++) {
+        if (v1[i] != v1_ref[i]) {
+          ++failed;
+          break;
+        }
+      }
+      for (int i = 0; i < 3; i++) {
+        if (v1[i + 3] != v2[i].get()) {
+          ++failed;
+          break;
+        }
+      }
+    } else {
+      ++failed;
+    }
+
+    // free memory to please memory sanitizer
+    delete v1[0];
+    delete v1[1];
+    delete v1[2];
+  }
+
+  // ----------------------------------------------------
+  // cm::append(Vector<unique_ptr>, Vector<unique_ptr>)
+  {
+    std::vector<std::unique_ptr<int>> v1;
+    std::vector<std::unique_ptr<int>> v2;
+
+    v1.emplace_back(new int(1));
+    v1.emplace_back(new int(2));
+    v1.emplace_back(new int(3));
+
+    v2.emplace_back(new int(4));
+    v2.emplace_back(new int(5));
+    v2.emplace_back(new int(6));
+
+    cm::append(v1, std::move(v2));
+
+    if (v1.size() == 6 && v2.empty()) {
+      for (int i = 0; i < 6; i++) {
+        if (*v1[i] != i + 1) {
+          ++failed;
+          break;
+        }
+      }
+    } else {
+      ++failed;
+    }
+  }
+}
+}
+
+int testCMExtAlgorithm(int /*unused*/, char* /*unused*/ [])
+{
+  testAppend();
+
+  return failed;
+}

+ 7 - 0
Utilities/std/cm/type_traits

@@ -26,12 +26,19 @@ using enable_if_t = typename std::enable_if<B, T>::type;
 
 #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703)
 
+// Helper classes
+using std::bool_constant;
+
 // Miscellaneous transformations
 using std::invoke_result;
 using std::invoke_result_t;
 
 #else
 
+// Helper classes
+template <bool B>
+using bool_constant = std::integral_constant<bool, B>;
+
 // Miscellaneous transformations
 template <typename F, typename... ArgTypes>
 using invoke_result = std::result_of<F(ArgTypes...)>;

+ 52 - 0
Utilities/std/cmext/algorithm

@@ -0,0 +1,52 @@
+// -*-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.  */
+#ifndef cmext_algorithm
+#define cmext_algorithm
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <cm/type_traits>
+#include <cmext/iterator>
+
+namespace cm {
+
+template <typename T>
+void append(std::vector<std::unique_ptr<T>>& v,
+            std::vector<std::unique_ptr<T>>&& r)
+{
+  std::transform(r.begin(), r.end(), std::back_inserter(v),
+                 [](std::unique_ptr<T>& item) { return std::move(item); });
+  r.clear();
+}
+
+template <typename T>
+void append(std::vector<T*>& v, std::vector<std::unique_ptr<T>> const& r)
+{
+  std::transform(r.begin(), r.end(), std::back_inserter(v),
+                 [](const std::unique_ptr<T>& item) { return item.get(); });
+}
+
+template <typename T, typename InputIt,
+          cm::enable_if_t<cm::is_input_iterator<InputIt>::value, int> = 0>
+void append(std::vector<T>& v, InputIt first, InputIt last)
+{
+  v.insert(v.end(), first, last);
+}
+
+template <typename T, typename Range,
+          cm::enable_if_t<cm::is_input_range<Range>::value, int> = 0>
+void append(std::vector<T>& v, Range const& r)
+{
+  v.insert(v.end(), r.begin(), r.end());
+}
+
+} // namespace cm
+
+#endif

+ 49 - 0
Utilities/std/cmext/iterator

@@ -0,0 +1,49 @@
+// -*-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.  */
+#ifndef cmext_iterator
+#define cmext_iterator
+
+#include <iterator>
+
+#include <cm/type_traits>
+
+namespace cm {
+
+// checks if a type is an iterator type
+template <typename I>
+using is_iterator =
+  std::is_integral<typename std::iterator_traits<I>::difference_type>;
+
+// checks if a type is an input iterator type
+template <typename I>
+using is_input_iterator =
+  std::is_base_of<std::input_iterator_tag,
+                  typename std::iterator_traits<I>::iterator_category>;
+
+// checks if a type is a range type: must have a difference_type type
+template <typename Range>
+using is_range = cm::bool_constant<
+  cm::is_iterator<decltype(std::declval<const Range>().begin())>::value &&
+  cm::is_iterator<decltype(std::declval<const Range>().end())>::value>;
+
+// checks if a type is an input range type: must have methods begin() and end()
+// returning an input iterator
+template <typename Range>
+using is_input_range =
+#if defined(_MSC_VER) && _MSC_VER < 1920
+  // MS C++ is not able to evaluate complex type introspection,
+  // so use a simplified version
+  cm::is_input_iterator<typename Range::const_iterator>;
+#else
+  cm::bool_constant<
+    cm::is_input_iterator<decltype(
+      std::declval<const Range>().begin())>::value &&
+    cm::is_input_iterator<decltype(std::declval<const Range>().end())>::value>;
+#endif
+
+} // namespace cm
+
+#endif