diff --git a/.treefmt.toml b/.treefmt.toml new file mode 100644 index 00000000..1dbcf80d --- /dev/null +++ b/.treefmt.toml @@ -0,0 +1,38 @@ +[global] +excludes = [ + "**/Doxyfile", + "**/Makefile", + "*.*-format", + "*.S", + "*.cmm", + "*.css", + "*.dld", + "*.gdb", + "*.gif", + "*.gitignore", + "*.html", + "*.ini", + "*.josh", + "*.json", + "*.md", + "*.png", + "*.puml", + "*.py", + "*.rb", + "*.rst", + "*.s", + "*.sh", + "*.spec", + "*.toml", + "*.txt", + "*.yaml", + "*.yml", + "docker/**", + "scripts/clang-format-wrapper", + "include/etl/generators/**" +] + +[formatter.cpp] +command = "scripts/clang-format-wrapper" +options = [ "-i", "--style=file" ] +includes = [ "*.c", "*.cc", "*.cpp", "*.h", "*.hh", "*.hpp" ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6231d084..2d7a3028 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,7 @@ Thanks for considering a contribution! Here’s what you need to know before ope - If you are fixing a bug, add a unit test that *fails* before the bug fix is implemented. - Do not initiate a pull request until all of the units tests pass. See below for information on project files and test scripts. - Branches should be based on the branch `master`. If `development` has pending updates, I’ll rebase the PR against it before pulling.. +- For formatting help, you can use clang-format, or the convenience wrapper treefmt. See also [docs/source-formatting.md](docs/source-formatting.md) There is a project file for VS2022 for C++14, 17, 20, 23, and bash scripts that run the tests for C++11, 14, 17, 20, 23 under Linux with GCC and Clang. There are syntax-only check bash scripts that cover C++03, 11, 14, 17, 20, 23 under Linux with GCC and Clang. diff --git a/docs/source-formatting.md b/docs/source-formatting.md new file mode 100644 index 00000000..4c353c05 --- /dev/null +++ b/docs/source-formatting.md @@ -0,0 +1,99 @@ +# Source Formatting + +This project uses **clang-format** (version 18) to enforce a consistent coding style +for C and C++ source files. For convenience, **treefmt** is also configured as a +single-command wrapper that discovers and formats every file in the tree. + +--- + +## clang-format + +### Configuration file + +The formatting rules live in [`.clang-format`](../.clang-format) at the repository +root. The style is based on **LLVM**. + +See the `.clang-format` file itself for the complete list. + +### Version requirement + +clang-format **18** is required. +The helper script [`scripts/clang-format-wrapper`](../scripts/clang-format-wrapper) +automatically resolves the correct binary: it first looks for `clang-format-18` on +`PATH`, then falls back to `clang-format` and verifies that its major version is 18. +All other tooling in the repo calls this wrapper instead of `clang-format` directly. + +### Running clang-format manually + +Format every tracked source file in the repository: + +```bash +git ls-files -z \ + '*.c' '*.cc' '*.cpp' \ + '*.h' '*.hh' '*.hpp' \ + ':(exclude)include/etl/generators/*' | xargs -0 scripts/clang-format-wrapper -i --verbose --style=file +``` + +You can also format individual files directly: + +```bash +scripts/clang-format-wrapper -i --style=file path/to/file.cpp +``` + +--- + +## treefmt + +[treefmt](https://treefmt.com) is a language-agnostic source-tree formatter. +It reads a single configuration file and dispatches each file to the appropriate +formatter. In this project, it delegates all C/C++ formatting to the same +`clang-format-wrapper` described above. + +In comparison to calling clang-format directly, it brings a significant speedup. + +### Configuration file + +The configuration lives in [`.treefmt.toml`](../.treefmt.toml) at the repository root. + +### Installing treefmt + +treefmt is a standalone Go binary. Install it with any of: + +```bash +# Using the official install script +curl -fsSL https://raw.githubusercontent.com/numtide/treefmt/main/install.sh | bash + +# Or via Homebrew +brew install treefmt + +# Or via Nix +nix profile install nixpkgs#treefmt2 +``` + +See the [treefmt documentation](https://treefmt.com) for more options. + +### Running treefmt + +From the repository root: + +```bash +# Format everything +treefmt + +# Check formatting without modifying files (useful in CI) +treefmt --fail-on-change +``` + +--- + +## Excluded paths + +`.treefmt.toml` excludes generated files under +`include/etl/generators/`. Do **not** format those files manually via clang-format or treefmt. + +## Pre-commit + +Before submitting a PR / contribution, run `treefmt --fail-on-change` to catch +unformatted code before merge. + +Alternatively, a plain `treefmt` automatically fixes any issues. \ No newline at end of file diff --git a/include/etl/absolute.h b/include/etl/absolute.h index cad11b7c..52cfe8bd 100644 --- a/include/etl/absolute.h +++ b/include/etl/absolute.h @@ -34,16 +34,43 @@ SOFTWARE. #include "platform.h" #include "type_traits.h" #include "integral_limits.h" +#include "error_handler.h" namespace etl { + namespace private_absolute + { + //************************************************************************* + // Non-constexpr function that is never called for valid inputs. + // If reached during constant evaluation, the compiler emits an error + // because it's not constexpr. + // At runtime, triggers the ETL assert handler. + //************************************************************************* + template + inline T signed_min_error() + { + ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("absolute value of minimum signed integer is undefined")); + return T(0); + } + } + //*************************************************************************** // For signed types. //*************************************************************************** template ETL_NODISCARD - ETL_CONSTEXPR - typename etl::enable_if::value, T>::type + ETL_CONSTEXPR + typename etl::enable_if::value && etl::is_integral::value, T>::type + absolute(T value) + { + return (value == etl::integral_limits::min) ? etl::private_absolute::signed_min_error() + : (value < T(0)) ? -value : value; + } + + template + ETL_NODISCARD + ETL_CONSTEXPR + typename etl::enable_if::value && !etl::is_integral::value, T>::type absolute(T value) ETL_NOEXCEPT { return (value < T(0)) ? -value : value; diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 74fbb8ed..cb38f8c3 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -34,7 +34,7 @@ SOFTWARE. #define ETL_ALGORITHM_INCLUDED ///\defgroup algorithm algorithm -/// Including reverse engineered algorithms from C++ 0x11, 0x14, 0x17 +/// Including reverse engineered algorithms from C++11, 14, 17 /// Additional new variants of certain algorithms. ///\ingroup utilities @@ -507,6 +507,7 @@ namespace etl //*************************************************************************** template ETL_NODISCARD + ETL_CONSTEXPR14 bool binary_search(TIterator first, TIterator last, const T& value, Compare compare) { first = etl::lower_bound(first, last, value, compare); @@ -516,6 +517,7 @@ namespace etl template ETL_NODISCARD + ETL_CONSTEXPR14 bool binary_search(TIterator first, TIterator last, const T& value) { typedef etl::less::value_type> compare; @@ -949,7 +951,7 @@ namespace etl { // Push Heap Helper template - void push_heap(TIterator first, TDistance value_index, TDistance top_index, TValue value, TCompare compare) + ETL_CONSTEXPR14 void push_heap(TIterator first, TDistance value_index, TDistance top_index, TValue value, TCompare compare) { TDistance parent = (value_index - 1) / 2; @@ -965,7 +967,7 @@ namespace etl // Adjust Heap Helper template - void adjust_heap(TIterator first, TDistance value_index, TDistance length, TValue value, TCompare compare) + ETL_CONSTEXPR14 void adjust_heap(TIterator first, TDistance value_index, TDistance length, TValue value, TCompare compare) { TDistance top_index = value_index; TDistance child2nd = (2 * value_index) + 2; @@ -993,7 +995,7 @@ namespace etl // Is Heap Helper template - bool is_heap(const TIterator first, const TDistance n, TCompare compare) + ETL_CONSTEXPR14 bool is_heap(const TIterator first, const TDistance n, TCompare compare) { TDistance parent = 0; @@ -1016,6 +1018,7 @@ namespace etl // Pop Heap template + ETL_CONSTEXPR14 void pop_heap(TIterator first, TIterator last, TCompare compare) { typedef typename etl::iterator_traits::value_type value_t; @@ -1029,6 +1032,7 @@ namespace etl // Pop Heap template + ETL_CONSTEXPR14 void pop_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1038,6 +1042,7 @@ namespace etl // Push Heap template + ETL_CONSTEXPR14 void push_heap(TIterator first, TIterator last, TCompare compare) { typedef typename etl::iterator_traits::difference_type difference_t; @@ -1048,6 +1053,7 @@ namespace etl // Push Heap template + ETL_CONSTEXPR14 void push_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1057,6 +1063,7 @@ namespace etl // Make Heap template + ETL_CONSTEXPR14 void make_heap(TIterator first, TIterator last, TCompare compare) { typedef typename etl::iterator_traits::difference_type difference_t; @@ -1084,6 +1091,7 @@ namespace etl // Make Heap template + ETL_CONSTEXPR14 void make_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1094,6 +1102,7 @@ namespace etl // Is Heap template ETL_NODISCARD + ETL_CONSTEXPR14 bool is_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1104,6 +1113,7 @@ namespace etl // Is Heap template ETL_NODISCARD + ETL_CONSTEXPR14 bool is_heap(TIterator first, TIterator last, TCompare compare) { return private_heap::is_heap(first, last - first, compare); @@ -1111,6 +1121,7 @@ namespace etl // Sort Heap template + ETL_CONSTEXPR14 void sort_heap(TIterator first, TIterator last) { while (first != last) @@ -1122,6 +1133,7 @@ namespace etl // Sort Heap template + ETL_CONSTEXPR14 void sort_heap(TIterator first, TIterator last, TCompare compare) { while (first != last) @@ -1131,6 +1143,119 @@ namespace etl } } + //*************************************************************************** + /// partial_sort + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + void partial_sort(TIterator first, TIterator middle, TIterator last, TCompare compare) + { + if (first == middle) + { + return; + } + + typedef typename etl::iterator_traits::value_type value_t; + typedef typename etl::iterator_traits::difference_type difference_t; + + etl::make_heap(first, middle, compare); + + for (TIterator i = middle; i != last; ++i) + { + if (compare(*i, *first)) + { + value_t value = ETL_MOVE(*i); + *i = ETL_MOVE(*first); + + private_heap::adjust_heap(first, difference_t(0), difference_t(middle - first), ETL_MOVE(value), compare); + } + } + + etl::sort_heap(first, middle, compare); + } + + //*************************************************************************** + /// partial_sort + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + void partial_sort(TIterator first, TIterator middle, TIterator last) + { + typedef etl::less::value_type> compare; + + etl::partial_sort(first, middle, last, compare()); + } + + //*************************************************************************** + /// partial_sort_copy + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + TRandomAccessIterator partial_sort_copy(TInputIterator first, + TInputIterator last, + TRandomAccessIterator d_first, + TRandomAccessIterator d_last, + TCompare compare) + { + typedef typename etl::iterator_traits::value_type value_t; + typedef typename etl::iterator_traits::difference_type difference_t; + + TRandomAccessIterator result = d_first; + + // Fill the destination range + while ((first != last) && (result != d_last)) + { + *result = *first; + ++result; + ++first; + } + + if (result == d_first) + { + return result; + } + + // Build a max-heap over the destination range + etl::make_heap(d_first, result, compare); + + // Process remaining input elements + for (TInputIterator i = first; i != last; ++i) + { + if (compare(*i, *d_first)) + { + value_t value = *i; + private_heap::adjust_heap(d_first, difference_t(0), difference_t(result - d_first), ETL_MOVE(value), compare); + } + } + + etl::sort_heap(d_first, result, compare); + + return result; + } + + //*************************************************************************** + /// partial_sort_copy + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + TRandomAccessIterator partial_sort_copy(TInputIterator first, + TInputIterator last, + TRandomAccessIterator d_first, + TRandomAccessIterator d_last) + { + typedef etl::less::value_type> compare; + + return etl::partial_sort_copy(first, last, d_first, d_last, compare()); + } + //*************************************************************************** // Search //*************************************************************************** @@ -1185,7 +1310,6 @@ namespace etl //*************************************************************************** namespace private_algorithm { -#if ETL_USING_CPP11 //********************************* // For random access iterators template @@ -1193,27 +1317,32 @@ namespace etl typename etl::enable_if::value, TIterator>::type rotate_general(TIterator first, TIterator middle, TIterator last) { - if (first == middle || middle == last) + if (first == middle) + { + return last; + } + + if (middle == last) { return first; } typedef typename etl::iterator_traits::value_type value_type; + typedef typename etl::iterator_traits::difference_type difference_type; - int n = last - first; - int m = middle - first; - int gcd_nm = (n == 0 || m == 0) ? n + m : etl::gcd(n, m); - + difference_type n = last - first; + difference_type m = middle - first; + difference_type gcd_nm = (n == 0 || m == 0) ? n + m : etl::gcd(n, m); TIterator result = first + (last - middle); - for (int i = 0; i < gcd_nm; i++) + for (difference_type i = 0; i < gcd_nm; i++) { value_type temp = ETL_MOVE(*(first + i)); - int j = i; + difference_type j = i; while (true) { - int k = j + m; + difference_type k = j + m; if (k >= n) { @@ -1234,56 +1363,6 @@ namespace etl return result; } -#else - //********************************* - // For random access iterators - template - ETL_CONSTEXPR14 - typename etl::enable_if::value, TIterator>::type - rotate_general(TIterator first, TIterator middle, TIterator last) - { - if (first == middle || middle == last) - { - return first; - } - - typedef typename etl::iterator_traits::value_type value_type; - - int n = last - first; - int m = middle - first; - int gcd_nm = (n == 0 || m == 0) ? n + m : etl::gcd(n, m); - - TIterator result = first + (last - middle); - - for (int i = 0; i < gcd_nm; i++) - { - value_type temp = *(first + i); - int j = i; - - while (true) - { - int k = j + m; - - if (k >= n) - { - k = k - n; - } - - if (k == i) - { - break; - } - - *(first + j) = *(first + k); - j = k; - } - - *(first + j) = temp; - } - - return result; - } -#endif //********************************* // For bidirectional iterators @@ -1292,7 +1371,12 @@ namespace etl typename etl::enable_if::value, TIterator>::type rotate_general(TIterator first, TIterator middle, TIterator last) { - if (first == middle || middle == last) + if (first == middle) + { + return last; + } + + if (middle == last) { return first; } @@ -1314,7 +1398,12 @@ namespace etl typename etl::enable_if::value, TIterator>::type rotate_general(TIterator first, TIterator middle, TIterator last) { - if (first == middle || middle == last) + if (first == middle) + { + return last; + } + + if (middle == last) { return first; } @@ -1390,26 +1479,29 @@ namespace etl ETL_CONSTEXPR14 TIterator rotate(TIterator first, TIterator middle, TIterator last) { + if (first == middle) + { + return last; + } + if (middle == last) + { + return first; + } + if (etl::next(first) == middle) { return private_algorithm::rotate_left_by_one(first, last); } +#if ETL_USING_CPP20 if (etl::next(middle) == last) { -#if ETL_USING_CPP20 - if ETL_IF_CONSTEXPR(etl::is_forward_iterator::value) - { - return private_algorithm::rotate_general(first, middle, last); - } - else + if ETL_IF_CONSTEXPR(etl::is_bidirectional_iterator_concept::value) { return private_algorithm::rotate_right_by_one(first, last); } -#else - return private_algorithm::rotate_general(first, middle, last); -#endif } +#endif return private_algorithm::rotate_general(first, middle, last); } @@ -1641,35 +1733,6 @@ namespace etl return compare(b, a) ? ETL_OR_STD::pair(b, a) : ETL_OR_STD::pair(a, b); } - //*************************************************************************** - /// is_sorted_until - ///\ingroup algorithm - /// - //*************************************************************************** - template - ETL_NODISCARD - ETL_CONSTEXPR14 - TIterator is_sorted_until(TIterator begin, - TIterator end) - { - if (begin != end) - { - TIterator next = begin; - - while (++next != end) - { - if (*next < *begin) - { - return next; - } - - ++begin; - } - } - - return end; - } - //*************************************************************************** /// is_sorted_until ///\ingroup algorithm @@ -1700,6 +1763,22 @@ namespace etl return end; } + //*************************************************************************** + /// is_sorted_until + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + TIterator is_sorted_until(TIterator begin, + TIterator end) + { + typedef etl::less::value_type> compare; + + return etl::is_sorted_until(begin, end, compare()); + } + //*************************************************************************** /// is_sorted ///\ingroup algorithm @@ -1768,22 +1847,9 @@ namespace etl TIterator is_unique_sorted_until(TIterator begin, TIterator end) { - if (begin != end) - { - TIterator next = begin; + typedef etl::less::value_type> compare; - while (++next != end) - { - if (!(*begin < *next)) - { - return next; - } - - ++begin; - } - } - - return end; + return etl::is_unique_sorted_until(begin, end, compare()); } //*************************************************************************** @@ -1838,6 +1904,51 @@ namespace etl return end; } + //*************************************************************************** + /// adjacent_find + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + TIterator adjacent_find(TIterator first, TIterator last, TBinaryPredicate predicate) + { + if (first != last) + { + TIterator next = first; + ++next; + + while (next != last) + { + if (predicate(*first, *next)) + { + return first; + } + + ++first; + ++next; + } + } + + return last; + } + + //*************************************************************************** + /// adjacent_find + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + TIterator adjacent_find(TIterator first, TIterator last) + { + typedef etl::equal_to::value_type> predicate; + + return etl::adjacent_find(first, last, predicate()); + } + //*************************************************************************** /// is_permutation ///\ingroup algorithm @@ -1896,9 +2007,9 @@ namespace etl { if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) { - size_t n = etl::count(begin2, end2, *i); + size_t n = etl::count_if(begin2, end2, etl::bind1st(predicate, *i)); - if (n == 0 || size_t(etl::count(i, end1, *i)) != n) + if (n == 0 || size_t(etl::count_if(i, end1, etl::bind1st(predicate, *i))) != n) { return false; } @@ -1922,18 +2033,20 @@ namespace etl TIterator2 begin2, TIterator2 end2) { - if (begin1 != end1) + if (etl::distance(begin1, end1) != etl::distance(begin2, end2)) { - for (TIterator1 i = begin1; i != end1; ++i) - { - if (i == etl::find(begin1, i, *i)) - { - size_t n = etl::count(begin2, end2, *i); + return false; + } - if (n == 0 || size_t(etl::count(i, end1, *i)) != n) - { - return false; - } + for (TIterator1 i = begin1; i != end1; ++i) + { + if (i == etl::find(begin1, i, *i)) + { + size_t n = etl::count(begin2, end2, *i); + + if (n == 0 || size_t(etl::count(i, end1, *i)) != n) + { + return false; } } } @@ -1948,24 +2061,27 @@ namespace etl //*************************************************************************** template ETL_NODISCARD + ETL_CONSTEXPR14 bool is_permutation(TIterator1 begin1, TIterator1 end1, TIterator2 begin2, TIterator2 end2, TBinaryPredicate predicate) { - if (begin1 != end1) + if (etl::distance(begin1, end1) != etl::distance(begin2, end2)) { - for (TIterator1 i = begin1; i != end1; ++i) - { - if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) - { - size_t n = etl::count(begin2, end2, *i); + return false; + } - if (n == 0 || size_t(etl::count(i, end1, *i)) != n) - { - return false; - } + for (TIterator1 i = begin1; i != end1; ++i) + { + if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) + { + size_t n = etl::count_if(begin2, end2, etl::bind1st(predicate, *i)); + + if (n == 0 || size_t(etl::count_if(i, end1, etl::bind1st(predicate, *i))) != n) + { + return false; } } } @@ -2020,14 +2136,22 @@ namespace etl TIterator end, TUnaryPredicate predicate) { - while (begin != end) - { - if (!predicate(*begin)) - { - return begin; - } + typedef typename etl::iterator_traits::difference_type difference_t; - ++begin; + // binary search on a partitioned range + for (difference_t length = etl::distance(begin, end); 0 < length; ) + { + difference_t half = length / 2; + TIterator middle = etl::next(begin, half); + if (predicate(*middle)) + { + begin = etl::next(middle); + length -= (half + 1); + } + else + { + length = half; + } } return begin; @@ -2344,6 +2468,253 @@ namespace etl return first; } + + //*************************************************************************** + /// unique + /// see https://en.cppreference.com/w/cpp/algorithm/unique + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TIterator unique(TIterator first, TIterator last) + { + if (first == last) + { + return last; + } + + TIterator result = first; + + while (++first != last) + { + if (!(*result == *first) && (++result != first)) + { + *result = ETL_MOVE(*first); + } + } + + return ++result; + } + + //*************************************************************************** + /// unique + /// see https://en.cppreference.com/w/cpp/algorithm/unique + /// predicate overload to determine equality. + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TIterator unique(TIterator first, TIterator last, TBinaryPredicate predicate) + { + if (first == last) + { + return last; + } + + TIterator result = first; + + while (++first != last) + { + if (!predicate(*result, *first) && (++result != first)) + { + *result = ETL_MOVE(*first); + } + } + + return ++result; + } + + //*************************************************************************** + /// unique_copy + /// see https://en.cppreference.com/w/cpp/algorithm/unique_copy + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator unique_copy(TInputIterator first, + TInputIterator last, + TOutputIterator d_first) + { + if (first == last) + { + return d_first; + } + + typename etl::iterator_traits::value_type prev = *first; + *d_first = prev; + + while (++first != last) + { + if (!(prev == *first)) + { + prev = *first; + *(++d_first) = prev; + } + } + + return ++d_first; + } + + //*************************************************************************** + /// unique_copy + /// see https://en.cppreference.com/w/cpp/algorithm/unique_copy + /// predicate overload to determine equality. + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator unique_copy(TInputIterator first, + TInputIterator last, + TOutputIterator d_first, + TBinaryPredicate predicate) + { + if (first == last) + { + return d_first; + } + + typename etl::iterator_traits::value_type prev = *first; + *d_first = prev; + + while (++first != last) + { + if (!predicate(prev, *first)) + { + prev = *first; + *(++d_first) = prev; + } + } + + return ++d_first; + } + + //*************************************************************************** + /// merge + /// Merges two sorted ranges into one sorted range. + /// see https://en.cppreference.com/w/cpp/algorithm/merge + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator merge(TInputIterator1 first1, TInputIterator1 last1, + TInputIterator2 first2, TInputIterator2 last2, + TOutputIterator d_first, + TCompare compare) + { + while ((first1 != last1) && (first2 != last2)) + { + if (compare(*first2, *first1)) + { + *d_first = *first2; + ++first2; + } + else + { + *d_first = *first1; + ++first1; + } + ++d_first; + } + + d_first = etl::copy(first1, last1, d_first); + d_first = etl::copy(first2, last2, d_first); + + return d_first; + } + + //*************************************************************************** + /// merge + /// Merges two sorted ranges into one sorted range. + /// Uses operator< for comparison. + /// see https://en.cppreference.com/w/cpp/algorithm/merge + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator merge(TInputIterator1 first1, TInputIterator1 last1, + TInputIterator2 first2, TInputIterator2 last2, + TOutputIterator d_first) + { + typedef etl::less::value_type> compare; + + return etl::merge(first1, last1, first2, last2, d_first, compare()); + } + + //*************************************************************************** + /// inplace_merge + /// Merges two consecutive sorted ranges [first, middle) and [middle, last) + /// into one sorted range [first, last) in-place. + /// Uses an iterative rotate-based algorithm that requires no additional + /// memory, no recursion and no explicit stack, making it safe for deeply + /// embedded targets with constrained stack sizes. + /// Complexity: O(N log N) comparisons, O(N log N) element moves. + /// see https://en.cppreference.com/w/cpp/algorithm/inplace_merge + ///\ingroup algorithm + //*************************************************************************** + template + void inplace_merge(TBidirectionalIterator first, + TBidirectionalIterator middle, + TBidirectionalIterator last, + TCompare compare) + { + typedef typename etl::iterator_traits::difference_type difference_type; + + difference_type len1 = etl::distance(first, middle); + difference_type len2 = etl::distance(middle, last); + + while ((len1 != 0) && (len2 != 0)) + { + // Find where the first element of the right half belongs in the left half. + // All elements in [first, cut1) are <= *middle, so they are already in place. + TBidirectionalIterator cut1 = etl::upper_bound(first, middle, *middle, compare); + difference_type prefix = etl::distance(first, cut1); + len1 -= prefix; + + // If the entire left half is <= *middle, we are done. + if (len1 == 0) + { + return; + } + + // Advance first past the already-placed prefix. + first = cut1; + + // Find where the first element of the (remaining) left half belongs in + // the right half. All elements in [middle, cut2) are < *first, so they + // need to be moved before *first. + TBidirectionalIterator cut2 = etl::lower_bound(middle, last, *first, compare); + difference_type run = etl::distance(middle, cut2); + len2 -= run; + + // Rotate the block [first, middle, cut2) so that [middle, cut2) moves + // before [first, middle). After the rotate the elements from + // [middle, cut2) (length = run) now occupy [first, first + run) and + // are in their final position. + etl::rotate(first, middle, cut2); + + // Advance past the block we just placed. + etl::advance(first, run); + middle = cut2; + } + } + + //*************************************************************************** + /// inplace_merge + /// Merges two consecutive sorted ranges [first, middle) and [middle, last) + /// into one sorted range [first, last) in-place. + /// Uses operator< for comparison. + /// see https://en.cppreference.com/w/cpp/algorithm/inplace_merge + ///\ingroup algorithm + //*************************************************************************** + template + void inplace_merge(TBidirectionalIterator first, + TBidirectionalIterator middle, + TBidirectionalIterator last) + { + typedef etl::less::value_type> compare; + + etl::inplace_merge(first, middle, last, compare()); + } } //***************************************************************************** @@ -3140,6 +3511,11 @@ namespace etl ETL_CONSTEXPR20 void selection_sort(TIterator first, TIterator last, TCompare compare) { + if (first == last) + { + return; + } + TIterator min; const TIterator ilast = private_algorithm::get_before_last(first, last); const TIterator jlast = last; @@ -3160,7 +3536,10 @@ namespace etl } using ETL_OR_STD::swap; // Allow ADL - swap(*i, *min); + if (min != i) + { + swap(*i, *min); + } } } @@ -3184,11 +3563,7 @@ namespace etl ETL_CONSTEXPR14 void heap_sort(TIterator first, TIterator last, TCompare compare) { - if (!etl::is_heap(first, last, compare)) - { - etl::make_heap(first, last, compare); - } - + etl::make_heap(first, last, compare); etl::sort_heap(first, last, compare); } @@ -3200,11 +3575,7 @@ namespace etl ETL_CONSTEXPR14 void heap_sort(TIterator first, TIterator last) { - if (!etl::is_heap(first, last)) - { - etl::make_heap(first, last); - } - + etl::make_heap(first, last); etl::sort_heap(first, last); } @@ -3248,7 +3619,7 @@ namespace etl #endif //*************************************************************************** - /// Returns the maximum value. + /// Returns the minimum value. //*************************************************************************** #if ETL_USING_CPP11 template @@ -3444,14 +3815,7 @@ namespace etl { typedef typename etl::iterator_traits::value_type value_type; - TIterator pivot = last; // Maybe find a better pivot choice? - value_type pivot_value = *pivot; - - // Swap the pivot with the last, if necessary. - if (pivot != last) - { - swap(*pivot, *last); - } + value_type pivot_value = ETL_MOVE(*last); TIterator i = first; @@ -3550,7 +3914,7 @@ namespace etl { typedef etl::less::value_type> compare_t; - nth_element(first, last, compare_t()); + nth_element(first, nth, last, compare_t()); } #endif } diff --git a/include/etl/array.h b/include/etl/array.h index f1624209..da4fcd04 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -46,7 +46,7 @@ SOFTWARE. #include ///\defgroup array array -/// A replacement for std::array if you haven't got C++0x11. +/// A replacement for std::array if you haven't got C++11. ///\ingroup containers namespace etl @@ -81,7 +81,7 @@ namespace etl //*************************************************************************** ///\ingroup array - /// A replacement for std::array if you haven't got C++0x11. + /// A replacement for std::array if you haven't got C++11. //*************************************************************************** template class array @@ -646,7 +646,7 @@ namespace etl //*************************************************************************** ///\ingroup array - /// A replacement for std::array if you haven't got C++0x11. + /// A replacement for std::array if you haven't got C++11. /// Specialisation for zero sized array. //*************************************************************************** template @@ -1105,7 +1105,7 @@ namespace etl #if ETL_USING_CPP17 template array(T...) -> array::type, sizeof...(T)>; -#endif +#endif //************************************************************************* /// Make diff --git a/include/etl/format.h b/include/etl/format.h index 3268f43b..7812f9ab 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -48,7 +48,9 @@ SOFTWARE. #include "variant.h" #include "visitor.h" +#if ETL_USING_FORMAT_FLOATING_POINT #include +#endif #if ETL_USING_CPP11 @@ -138,9 +140,11 @@ namespace etl unsigned int, long long int, unsigned long long int, +#if ETL_USING_FORMAT_FLOATING_POINT float, double, long double, +#endif const char*, etl::string_view, const void* @@ -302,6 +306,7 @@ namespace etl { } +#if ETL_USING_FORMAT_FLOATING_POINT basic_format_arg(const float v) : data(v) { @@ -316,6 +321,7 @@ namespace etl : data(v) { } +#endif basic_format_arg(const etl::string_view v) : data(v) @@ -1039,6 +1045,7 @@ namespace etl format_plain_num(it, value, spec, width); } +#if ETL_USING_FORMAT_FLOATING_POINT template void format_floating_default(OutputIt& it, T value, const format_spec_t& spec) { @@ -1214,6 +1221,7 @@ namespace etl private_format::format_sequence(it, "."); private_format::format_plain_num(it, fractional_int, spec, fractional_decimals); } +#endif class dummy_assign_to { @@ -1336,6 +1344,7 @@ namespace etl size_t count; }; +#if ETL_USING_FORMAT_FLOATING_POINT template void format_floating_g(OutputIt& it, T value, const format_spec_t& spec) { @@ -1409,6 +1418,7 @@ namespace etl } } } +#endif template struct format_visitor @@ -1490,6 +1500,7 @@ namespace etl return it; } +#if ETL_USING_FORMAT_FLOATING_POINT template typename format_context::iterator format_aligned_floating(Float arg, format_context& fmt_ctx) { @@ -1534,6 +1545,7 @@ namespace etl private_format::fill(it, suffix_size, fmt_ctx.format_spec.fill); return it; } +#endif template void format_string_view(OutputIt& it, etl::string_view arg, const format_spec_t& spec) @@ -2072,6 +2084,7 @@ namespace etl } }; +#if ETL_USING_FORMAT_FLOATING_POINT template<> struct formatter { @@ -2119,6 +2132,7 @@ namespace etl return private_format::format_aligned_floating(arg, fmt_ctx); } }; +#endif template<> struct formatter diff --git a/include/etl/fsm.h b/include/etl/fsm.h index 36130a47..a00206e7 100644 --- a/include/etl/fsm.h +++ b/include/etl/fsm.h @@ -62,6 +62,7 @@ SOFTWARE. #include "largest.h" #if ETL_USING_CPP11 #include "tuple.h" + #include "type_list.h" #endif #include @@ -83,14 +84,14 @@ namespace etl // For internal FSM use. typedef typename etl::larger_type::type fsm_internal_id_t; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template class fsm_state; #else template class fsm_state; #endif @@ -195,7 +196,7 @@ namespace etl // Pass this whenever no state change is desired. // The highest unsigned value of fsm_state_id_t. static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits::max; - + // Pass this when this event also needs to be passed to the parent. static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U; @@ -213,15 +214,15 @@ namespace etl ETL_CONSTANT fsm_state_id_t ifsm_state_helper::Self_Transition; // Compile-time: TState::ID must equal its index in the type list (0..N-1) - template struct check_ids : etl::true_type + template struct check_ids : etl::true_type { }; template struct check_ids - : etl::integral_constant::value> + : etl::integral_constant::value> { - }; + }; //*************************************************************************** /// RAII detection mechanism to catch reentrant calls to methods that might @@ -250,11 +251,11 @@ namespace etl { is_locked = false; } - + private: // Reference to the flag signifying a lock on the state machine. bool& is_locked; - + // Copy & move semantics disabled since this is a guard. fsm_reentrancy_guard(fsm_reentrancy_guard const&) ETL_DELETE; fsm_reentrancy_guard& operator= (fsm_reentrancy_guard const&) ETL_DELETE; @@ -272,7 +273,7 @@ namespace etl /// A class to store FSM states. //*************************************************************************** template - class fsm_state_pack + class fsm_state_pack { public: @@ -294,18 +295,18 @@ namespace etl /// Gets a reference to the state. //********************************* template - TState& get() - { - return etl::get(storage); + TState& get() + { + return etl::get(storage); } //********************************* /// Gets a const reference to the state. //********************************* template - const TState& get() const - { - return etl::get(storage); + const TState& get() const + { + return etl::get(storage); } private: @@ -341,14 +342,14 @@ namespace etl using private_fsm::ifsm_state_helper<>::Pass_To_Parent; using private_fsm::ifsm_state_helper<>::Self_Transition; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template friend class fsm_state; #else template friend class etl::fsm_state; #endif @@ -560,7 +561,7 @@ namespace etl { etl::fsm_state_id_t next_state_id = p_state->process_event(message); - process_state_change(next_state_id); + process_state_change(next_state_id); } else { @@ -691,7 +692,7 @@ namespace etl p_state->on_exit_state(); next_state_id = p_state->on_enter_state(); } - + if (have_changed_state(next_state_id)) { ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id()); @@ -722,17 +723,249 @@ namespace etl }; //************************************************************************************************* - // For C++17 and above. + // For C++11 and above. //************************************************************************************************* -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above //*************************************************************************** // The definition for all types. //*************************************************************************** template class fsm_state : public ifsm_state { + private: + + using message_id_sequence = etl::index_sequence; + public: + using message_types = etl::type_list; + using sorted_message_types = etl::type_list_sort_t; + + static_assert(etl::type_list_is_unique::value, "All TMessageTypes must be unique"); + static_assert(etl::type_list_all_of::value, "All TMessageTypes must satisfy the condition etl::is_message_type"); + static_assert(etl::index_sequence_is_unique::value, "All message IDs must be unique"); + + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + protected: + + ~fsm_state() + { + } + + TContext& get_fsm_context() const + { + return static_cast(ifsm_state::get_fsm_context()); + } + + private: + + static constexpr size_t Number_Of_Messages = sizeof...(TMessageTypes); + static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t::ID; + + static_assert(Number_Of_Messages > 0, "Zero messages"); + + //********************************************** + // Checks that the message ids are contiguous. + //********************************************** + template = Number_Of_Messages)> + struct contiguous_impl; + + template + struct contiguous_impl : etl::true_type + { + }; + + template + struct contiguous_impl + : etl::bool_constant<(etl::type_list_type_at_index_t::ID + 1U == + etl::type_list_type_at_index_t::ID) && + contiguous_impl::value> + { + + + }; + + // The message ids are contiguous if there are 0 or 1 message types, or if each message id is one greater than the previous message id. + static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value; + + using handler_ptr = etl::fsm_state_id_t (*)(TDerived&, const etl::imessage&); ///< Pointer to a handler function that takes a reference to the derived class and a reference to the message. + using message_dispatch_table_t = etl::array; ///< The dispatch table type. An array of handler pointers, one for each message type. + using message_id_table_t = etl::array; ///< The message id table type. An array of message ids, one for each message type. + + //******************************************** + etl::fsm_state_id_t process_event(const etl::imessage& message) + { + const etl::message_id_t id = message.get_message_id(); + + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) + { + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + const etl::fsm_state_id_t new_state_id = dispatch(message, index); + + if (new_state_id != Pass_To_Parent) + { + return new_state_id; + } + } + } + +#include "etl/private/diagnostic_array_bounds_push.h" + // If we get here, then we don't have a handler for this message type, so pass it to the parent if we have one, otherwise call on_event_unknown. + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + // Call for a single message type + //********************************************** + template + static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg) + { + return derived.on_event(static_cast(msg)); + } + + //********************************************** + // Get the handler for a single message type at the index in the sorted type_list. + // This will be called for each message type to generate the dispatch table. + //********************************************** + template + static constexpr handler_ptr get_message_handler() + { + return &call_on_event>; + } + + //********************************************** + // Generate the dispatch table at compile time. + // This will create an array of handler pointers, one for each message type. + //********************************************** + template + static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence) + { + return message_dispatch_table_t{ { get_message_handler()... } }; + } + + //********************************************** + // Get the message id for a single message type at an index in the sorted type_list. + // This will be called for each message type to generate the message id table. + //********************************************** + template + static constexpr etl::message_id_t get_message_id_from_index() + { + return etl::type_list_type_at_index_t::ID; + } + + //********************************************** + // Generate the message id table at compile time. + // This will create an array of message ids, one for each message type. + //********************************************** + template + static constexpr message_id_table_t make_message_id_table(etl::index_sequence) + { + return message_id_table_t{ { get_message_id_from_index()... } }; + } + + //********************************************** + // Get the dispatch index for a message id. + // This will be used at runtime to find the handler for a message id. + // If the message ids are contiguous, we can calculate the index directly. If they are not contiguous, we need to do a binary search. + // This will return Number_Of_Messages if the message id is not found. + //********************************************** + static size_t get_dispatch_index_from_message_id(etl::message_id_t id) + { + if ETL_IF_CONSTEXPR(Message_Ids_Are_Contiguous) + { + // The IDs are contiguous, so we can calculate the index directly. + return static_cast(id - Message_Id_Start); + } + else + { + // The IDs are not contiguous, so we need to do a binary search. + size_t left = 0; + size_t right = Number_Of_Messages; + + while (left < right) + { + size_t mid = (left + right) / 2; + + if (message_id_table[mid] == id) + { + return mid; + } + else if (message_id_table[mid] < id) + { + left = mid + 1; + } + else + { + right = mid; + } + } + } + + return Number_Of_Messages; // Not found + } + + //********************************************** + // Dispatch the message to the appropriate handler based on the index in the dispatch table. + //********************************************** + etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index) + { + return message_dispatch_table[index](static_cast(*this), msg); + } + + //********************************************** + // The dispatch table is generated at compile time. The dispatch table contains pointers to the on_receive handlers for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_dispatch_table_t message_dispatch_table = + etl::fsm_state::make_message_dispatch_table(etl::make_index_sequence::Number_Of_Messages>{}); + + //********************************************** + // The message id table is generated at compile time. The message id table contains the corresponding message ids for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_id_table_t message_id_table = + etl::fsm_state::make_message_id_table(etl::make_index_sequence::Number_Of_Messages>{}); + }; + +#if ETL_USING_CPP11 && !ETL_USING_CPP17 + template + constexpr const typename etl::fsm_state::message_dispatch_table_t + etl::fsm_state::message_dispatch_table; + + template + constexpr const typename etl::fsm_state::message_id_table_t + etl::fsm_state::message_id_table; +#endif + + /// Definition of STATE_ID + template + ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; + + //*************************************************************************** + // The definition for no messages. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + private: + + using message_id_sequence = etl::index_sequence<>; + + public: + + using message_types = etl::type_list<>; + using sorted_message_types = etl::type_list<>; + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; fsm_state() @@ -753,59 +986,24 @@ namespace etl private: - //******************************************** - struct result_t - { - bool was_handled; - etl::fsm_state_id_t state_id; - }; - //******************************************** etl::fsm_state_id_t process_event(const etl::imessage& message) { - etl::fsm_state_id_t new_state_id; - - const bool was_handled = (process_event_type(message, new_state_id) || ...); - - if (!was_handled || (new_state_id == Pass_To_Parent)) - { - new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); - } - - return new_state_id; - } - - //******************************************** - template - bool process_event_type(const etl::imessage& msg, etl::fsm_state_id_t& new_state_id) - { - if (TMessage::ID == msg.get_message_id()) - { - new_state_id = static_cast(this)->on_event(static_cast(msg)); - return true; - } - else - { - return false; - } + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); } }; - /// Definition of STATE_ID - template - ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; - #else //************************************************************************************************* -// For C++14 and below. +// For C++03 and below. //************************************************************************************************* //*************************************************************************** // The definition for all 16 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -864,10 +1062,10 @@ namespace etl //*************************************************************************** // Specialisation for 15 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -925,10 +1123,10 @@ namespace etl //*************************************************************************** // Specialisation for 14 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -985,10 +1183,10 @@ namespace etl //*************************************************************************** // Specialisation for 13 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1044,9 +1242,9 @@ namespace etl //*************************************************************************** // Specialisation for 12 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1101,9 +1299,9 @@ namespace etl //*************************************************************************** // Specialisation for 11 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1157,9 +1355,9 @@ namespace etl //*************************************************************************** // Specialisation for 10 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1212,9 +1410,9 @@ namespace etl //*************************************************************************** // Specialisation for 9 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1266,8 +1464,8 @@ namespace etl //*************************************************************************** // Specialisation for 8 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1318,8 +1516,8 @@ namespace etl //*************************************************************************** // Specialisation for 7 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1369,8 +1567,8 @@ namespace etl //*************************************************************************** // Specialisation for 6 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1419,8 +1617,8 @@ namespace etl //*************************************************************************** // Specialisation for 5 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1468,7 +1666,7 @@ namespace etl //*************************************************************************** // Specialisation for 4 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1515,7 +1713,7 @@ namespace etl //*************************************************************************** // Specialisation for 3 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1561,7 +1759,7 @@ namespace etl //*************************************************************************** // Specialisation for 2 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1606,7 +1804,7 @@ namespace etl //*************************************************************************** // Specialisation for 1 message type. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1680,10 +1878,10 @@ namespace etl } }; - template ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; #endif diff --git a/include/etl/functional.h b/include/etl/functional.h index 0f0172ec..e313325e 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -43,7 +43,7 @@ SOFTWARE. namespace etl { //*************************************************************************** - /// A definition of reference_wrapper for those that don't have C++ 0x11 support. + /// A definition of reference_wrapper for those that don't have C++11 support. ///\ingroup reference //*************************************************************************** template @@ -224,9 +224,9 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(rhs) < static_cast(lhs))) { - return !(static_cast(lhs) < static_cast(rhs)); + return !(static_cast(rhs) < static_cast(lhs)); } }; #endif @@ -250,7 +250,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(rhs) < static_cast(lhs)) { return static_cast(rhs) < static_cast(lhs); } @@ -276,9 +276,9 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(lhs) < static_cast(rhs))) { - return static_cast(rhs) < static_cast(lhs); + return !(static_cast(lhs) < static_cast(rhs)); } }; #endif @@ -303,7 +303,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) == static_cast(rhs)) { return static_cast(lhs) == static_cast(rhs); } @@ -329,7 +329,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(lhs) == static_cast(rhs))) { return !(static_cast(lhs) == static_cast(rhs)); } diff --git a/include/etl/generators/fsm_generator.h b/include/etl/generators/fsm_generator.h index 0887af6c..98d4c6b0 100644 --- a/include/etl/generators/fsm_generator.h +++ b/include/etl/generators/fsm_generator.h @@ -74,6 +74,7 @@ cog.outl("//******************************************************************** #include "largest.h" #if ETL_USING_CPP11 #include "tuple.h" + #include "type_list.h" #endif #include @@ -95,20 +96,20 @@ namespace etl // For internal FSM use. typedef typename etl::larger_type::type fsm_internal_id_t; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template class fsm_state; #else /*[[[cog import cog cog.outl("template ") + cog.out(" ") + cog.outl(" typename>") cog.outl("class fsm_state;") ]]]*/ /*[[[end]]]*/ @@ -214,7 +215,7 @@ namespace etl // Pass this whenever no state change is desired. // The highest unsigned value of fsm_state_id_t. static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits::max; - + // Pass this when this event also needs to be passed to the parent. static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U; @@ -232,15 +233,15 @@ namespace etl ETL_CONSTANT fsm_state_id_t ifsm_state_helper::Self_Transition; // Compile-time: TState::ID must equal its index in the type list (0..N-1) - template struct check_ids : etl::true_type + template struct check_ids : etl::true_type { }; template struct check_ids - : etl::integral_constant::value> + : etl::integral_constant::value> { - }; + }; //*************************************************************************** /// RAII detection mechanism to catch reentrant calls to methods that might @@ -269,11 +270,11 @@ namespace etl { is_locked = false; } - + private: // Reference to the flag signifying a lock on the state machine. bool& is_locked; - + // Copy & move semantics disabled since this is a guard. fsm_reentrancy_guard(fsm_reentrancy_guard const&) ETL_DELETE; fsm_reentrancy_guard& operator= (fsm_reentrancy_guard const&) ETL_DELETE; @@ -291,7 +292,7 @@ namespace etl /// A class to store FSM states. //*************************************************************************** template - class fsm_state_pack + class fsm_state_pack { public: @@ -313,18 +314,18 @@ namespace etl /// Gets a reference to the state. //********************************* template - TState& get() - { - return etl::get(storage); + TState& get() + { + return etl::get(storage); } //********************************* /// Gets a const reference to the state. //********************************* template - const TState& get() const - { - return etl::get(storage); + const TState& get() const + { + return etl::get(storage); } private: @@ -360,20 +361,20 @@ namespace etl using private_fsm::ifsm_state_helper<>::Pass_To_Parent; using private_fsm::ifsm_state_helper<>::Self_Transition; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template friend class fsm_state; #else /*[[[cog import cog cog.outl(" template ") + cog.out(" ") + cog.outl(" typename>") ]]]*/ /*[[[end]]]*/ friend class etl::fsm_state; @@ -586,7 +587,7 @@ namespace etl { etl::fsm_state_id_t next_state_id = p_state->process_event(message); - process_state_change(next_state_id); + process_state_change(next_state_id); } else { @@ -717,7 +718,7 @@ namespace etl p_state->on_exit_state(); next_state_id = p_state->on_enter_state(); } - + if (have_changed_state(next_state_id)) { ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id()); @@ -748,17 +749,249 @@ namespace etl }; //************************************************************************************************* - // For C++17 and above. + // For C++11 and above. //************************************************************************************************* -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above //*************************************************************************** // The definition for all types. //*************************************************************************** template class fsm_state : public ifsm_state { + private: + + using message_id_sequence = etl::index_sequence; + public: + using message_types = etl::type_list; + using sorted_message_types = etl::type_list_sort_t; + + static_assert(etl::type_list_is_unique::value, "All TMessageTypes must be unique"); + static_assert(etl::type_list_all_of::value, "All TMessageTypes must satisfy the condition etl::is_message_type"); + static_assert(etl::index_sequence_is_unique::value, "All message IDs must be unique"); + + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + protected: + + ~fsm_state() + { + } + + TContext& get_fsm_context() const + { + return static_cast(ifsm_state::get_fsm_context()); + } + + private: + + static constexpr size_t Number_Of_Messages = sizeof...(TMessageTypes); + static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t::ID; + + static_assert(Number_Of_Messages > 0, "Zero messages"); + + //********************************************** + // Checks that the message ids are contiguous. + //********************************************** + template = Number_Of_Messages)> + struct contiguous_impl; + + template + struct contiguous_impl : etl::true_type + { + }; + + template + struct contiguous_impl + : etl::bool_constant<(etl::type_list_type_at_index_t::ID + 1U == + etl::type_list_type_at_index_t::ID) && + contiguous_impl::value> + { + + + }; + + // The message ids are contiguous if there are 0 or 1 message types, or if each message id is one greater than the previous message id. + static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value; + + using handler_ptr = etl::fsm_state_id_t (*)(TDerived&, const etl::imessage&); ///< Pointer to a handler function that takes a reference to the derived class and a reference to the message. + using message_dispatch_table_t = etl::array; ///< The dispatch table type. An array of handler pointers, one for each message type. + using message_id_table_t = etl::array; ///< The message id table type. An array of message ids, one for each message type. + + //******************************************** + etl::fsm_state_id_t process_event(const etl::imessage& message) + { + const etl::message_id_t id = message.get_message_id(); + + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) + { + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + const etl::fsm_state_id_t new_state_id = dispatch(message, index); + + if (new_state_id != Pass_To_Parent) + { + return new_state_id; + } + } + } + +#include "etl/private/diagnostic_array_bounds_push.h" + // If we get here, then we don't have a handler for this message type, so pass it to the parent if we have one, otherwise call on_event_unknown. + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + // Call for a single message type + //********************************************** + template + static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg) + { + return derived.on_event(static_cast(msg)); + } + + //********************************************** + // Get the handler for a single message type at the index in the sorted type_list. + // This will be called for each message type to generate the dispatch table. + //********************************************** + template + static constexpr handler_ptr get_message_handler() + { + return &call_on_event>; + } + + //********************************************** + // Generate the dispatch table at compile time. + // This will create an array of handler pointers, one for each message type. + //********************************************** + template + static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence) + { + return message_dispatch_table_t{ { get_message_handler()... } }; + } + + //********************************************** + // Get the message id for a single message type at an index in the sorted type_list. + // This will be called for each message type to generate the message id table. + //********************************************** + template + static constexpr etl::message_id_t get_message_id_from_index() + { + return etl::type_list_type_at_index_t::ID; + } + + //********************************************** + // Generate the message id table at compile time. + // This will create an array of message ids, one for each message type. + //********************************************** + template + static constexpr message_id_table_t make_message_id_table(etl::index_sequence) + { + return message_id_table_t{ { get_message_id_from_index()... } }; + } + + //********************************************** + // Get the dispatch index for a message id. + // This will be used at runtime to find the handler for a message id. + // If the message ids are contiguous, we can calculate the index directly. If they are not contiguous, we need to do a binary search. + // This will return Number_Of_Messages if the message id is not found. + //********************************************** + static size_t get_dispatch_index_from_message_id(etl::message_id_t id) + { + if ETL_IF_CONSTEXPR(Message_Ids_Are_Contiguous) + { + // The IDs are contiguous, so we can calculate the index directly. + return static_cast(id - Message_Id_Start); + } + else + { + // The IDs are not contiguous, so we need to do a binary search. + size_t left = 0; + size_t right = Number_Of_Messages; + + while (left < right) + { + size_t mid = (left + right) / 2; + + if (message_id_table[mid] == id) + { + return mid; + } + else if (message_id_table[mid] < id) + { + left = mid + 1; + } + else + { + right = mid; + } + } + } + + return Number_Of_Messages; // Not found + } + + //********************************************** + // Dispatch the message to the appropriate handler based on the index in the dispatch table. + //********************************************** + etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index) + { + return message_dispatch_table[index](static_cast(*this), msg); + } + + //********************************************** + // The dispatch table is generated at compile time. The dispatch table contains pointers to the on_receive handlers for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_dispatch_table_t message_dispatch_table = + etl::fsm_state::make_message_dispatch_table(etl::make_index_sequence::Number_Of_Messages>{}); + + //********************************************** + // The message id table is generated at compile time. The message id table contains the corresponding message ids for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_id_table_t message_id_table = + etl::fsm_state::make_message_id_table(etl::make_index_sequence::Number_Of_Messages>{}); + }; + +#if ETL_USING_CPP11 && !ETL_USING_CPP17 + template + constexpr const typename etl::fsm_state::message_dispatch_table_t + etl::fsm_state::message_dispatch_table; + + template + constexpr const typename etl::fsm_state::message_id_table_t + etl::fsm_state::message_id_table; +#endif + + /// Definition of STATE_ID + template + ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; + + //*************************************************************************** + // The definition for no messages. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + private: + + using message_id_sequence = etl::index_sequence<>; + + public: + + using message_types = etl::type_list<>; + using sorted_message_types = etl::type_list<>; + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; fsm_state() @@ -779,51 +1012,16 @@ namespace etl private: - //******************************************** - struct result_t - { - bool was_handled; - etl::fsm_state_id_t state_id; - }; - //******************************************** etl::fsm_state_id_t process_event(const etl::imessage& message) { - etl::fsm_state_id_t new_state_id; - - const bool was_handled = (process_event_type(message, new_state_id) || ...); - - if (!was_handled || (new_state_id == Pass_To_Parent)) - { - new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); - } - - return new_state_id; - } - - //******************************************** - template - bool process_event_type(const etl::imessage& msg, etl::fsm_state_id_t& new_state_id) - { - if (TMessage::ID == msg.get_message_id()) - { - new_state_id = static_cast(this)->on_event(static_cast(msg)); - return true; - } - else - { - return false; - } + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); } }; - /// Definition of STATE_ID - template - ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; - #else //************************************************************************************************* -// For C++14 and below. +// For C++03 and below. //************************************************************************************************* /*[[[cog import cog @@ -833,14 +1031,14 @@ namespace etl cog.outl("//***************************************************************************") cog.outl("// The definition for all %s message types." % Handlers) cog.outl("//***************************************************************************") - cog.outl("template " % Handlers) + cog.out(" ") + cog.outl(" typename T%s = void>" % Handlers) cog.outl("class fsm_state : public ifsm_state") cog.outl("{") cog.outl("public:") @@ -896,26 +1094,26 @@ namespace etl else: cog.outl("// Specialisation for %d message types." % n) cog.outl("//***************************************************************************") - cog.outl("template " % n) - cog.out("class fsm_state" % n) + cog.out("class fsm_state : public ifsm_state") + cog.outl(" void> : public ifsm_state") cog.outl("{") cog.outl("public:") cog.outl("") @@ -966,13 +1164,13 @@ namespace etl cog.outl("// Specialisation for 0 message types.") cog.outl("//***************************************************************************") cog.outl("template ") - cog.out("class fsm_state : public ifsm_state") + cog.outl(" void> : public ifsm_state") cog.outl("{") cog.outl("public:") cog.outl("") @@ -1002,18 +1200,18 @@ namespace etl cog.outl("};") cog.outl("") - cog.outl("template " % Handlers) - cog.out("ETL_CONSTANT etl::fsm_state_id_t fsm_state" % Handlers) + cog.out("ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID;" % Handlers) + cog.out(" T%s," % n) + cog.outl(" T%s>::STATE_ID;" % Handlers) ]]]*/ /*[[[end]]]*/ #endif diff --git a/include/etl/generators/message_router_generator.h b/include/etl/generators/message_router_generator.h index eeec698f..312848b6 100644 --- a/include/etl/generators/message_router_generator.h +++ b/include/etl/generators/message_router_generator.h @@ -525,33 +525,31 @@ namespace etl //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - etl::message_id_t id = msg.get_message_id(); - size_t index = Number_Of_Messages; + const etl::message_id_t id = msg.get_message_id(); // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + dispatch(msg, index); + return; + } } - // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. - if (index < Number_Of_Messages) + // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. + if (has_successor()) { - dispatch(msg, index); + get_successor().receive(msg); } else { - // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. - if (has_successor()) - { - get_successor().receive(msg); - } - else - { #include "etl/private/diagnostic_array_bounds_push.h" - static_cast(this)->on_receive_unknown(msg); + static_cast(this)->on_receive_unknown(msg); #include "etl/private/diagnostic_pop.h" - } } } @@ -601,29 +599,17 @@ namespace etl //*********************************************** bool accepts(etl::message_id_t id) const ETL_OVERRIDE { - size_t index = Number_Of_Messages; - - // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + if (index < Number_Of_Messages) + { + return true; + } } - if (index < Number_Of_Messages) - { - return true; - } - else - { - if (has_successor()) - { - return get_successor().accepts(id); - } - else - { - return false; - } - } + return has_successor() ? get_successor().accepts(id) : false; } //******************************************** diff --git a/include/etl/generators/type_traits_generator.h b/include/etl/generators/type_traits_generator.h index 1588544f..373b5d0e 100644 --- a/include/etl/generators/type_traits_generator.h +++ b/include/etl/generators/type_traits_generator.h @@ -765,11 +765,11 @@ namespace etl ///\ingroup type_traits /// Implemented by checking if type is convertible to an integer through static_cast - namespace private_type_traits + namespace private_type_traits { // Base case template - struct is_convertible_to_int + struct is_convertible_to_int : false_type { }; @@ -778,7 +778,7 @@ namespace etl // 2nd template argument of base case defaults to int to ensure that this partial specialization is always tried first template struct is_convertible_to_int(declval()))> - : true_type + : true_type { }; } @@ -788,7 +788,7 @@ namespace etl : integral_constant::value && !is_class::value && !is_arithmetic::value && - !is_reference::value> + !is_reference::value> { }; @@ -872,6 +872,42 @@ namespace etl public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1420,6 +1456,42 @@ typedef integral_constant true_type; public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1556,17 +1628,17 @@ typedef integral_constant true_type; cog.outl("/// Template to determine if a type is one of a specified list.") cog.outl("///\\ingroup types") cog.outl("template " % IsOneOf) + cog.out(" ") + cog.outl(" typename T%s = void>" % IsOneOf) cog.outl("struct is_one_of") cog.outl("{") - cog.outl(" static const bool value = ") + cog.outl(" static const bool value =") for n in range(1, int(IsOneOf)): cog.outl(" etl::is_same::value ||" % n) cog.outl(" etl::is_same::value;" % IsOneOf) @@ -1647,7 +1719,7 @@ typedef integral_constant true_type; //*************************************************************************** /// Get the Nth base of a recursively inherited type. /// Requires that the class has defined 'base_type'. - //*************************************************************************** + //*************************************************************************** // Recursive definition of the type. template struct nth_base @@ -2192,7 +2264,7 @@ typedef integral_constant true_type; #if ETL_USING_CPP11 //*************************************************************************** /// is_constructible - namespace private_type_traits + namespace private_type_traits { template struct is_constructible_ : etl::false_type {}; @@ -2371,7 +2443,7 @@ typedef integral_constant true_type; }; template - struct common_type_2_impl + struct common_type_2_impl : decay_conditional_result { }; @@ -2716,7 +2788,7 @@ typedef integral_constant true_type; struct is_member_pointer_helper : etl::true_type {}; } - template + template struct is_member_pointer : private_type_traits::is_member_pointer_helper::type> {}; #if ETL_USING_CPP17 @@ -2809,10 +2881,10 @@ typedef integral_constant true_type; //*************************************************************************** namespace private_type_traits { - template + template struct is_member_object_pointer_helper : public etl::false_type {}; - template + template struct is_member_object_pointer_helper : public etl::negation> {}; } @@ -3034,6 +3106,60 @@ typedef integral_constant true_type; }; #endif + +#if ETL_USING_CPP11 + template + struct has_size : etl::false_type {}; + + template + struct has_size().size())> > + : etl::true_type {}; +#else + template + struct has_size + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_size(char (*)[sizeof(&U::size)]); + + template + static no test_size(...); + + public: + + static const bool value = (sizeof(test_size(0)) == sizeof(yes)); + }; +#endif + +#if ETL_USING_CPP11 + template + struct has_data : etl::false_type {}; + + template + struct has_data().data())> > + : etl::true_type {}; +#else + template + struct has_data + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_data(char (*)[sizeof(&U::data)]); + + template + static no test_data(...); + + public: + + static const bool value = (sizeof(test_data(0)) == sizeof(yes)); + }; +#endif } // Helper macros diff --git a/include/etl/iterator.h b/include/etl/iterator.h index 80535132..d3b47d20 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -67,7 +67,7 @@ namespace etl typedef typename TIterator::pointer pointer; typedef typename TIterator::reference reference; }; - + // For pointers. template struct iterator_traits @@ -606,7 +606,7 @@ namespace etl ETL_CONSTEXPR14 back_insert_iterator& operator =(const typename TContainer::value_type& value) { container->push_back(value); - + return (*this); } @@ -617,7 +617,7 @@ namespace etl ETL_CONSTEXPR14 back_insert_iterator& operator =(typename TContainer::value_type&& value) { container->push_back(etl::move(value)); - + return (*this); } #endif // ETL_USING_CPP11 @@ -655,8 +655,8 @@ namespace etl /// Creates a back_insert_iterator from a container. //*************************************************************************** template - ETL_NODISCARD - ETL_CONSTEXPR14 + ETL_NODISCARD + ETL_CONSTEXPR14 etl::back_insert_iterator back_inserter(TContainer& container) { return etl::back_insert_iterator(container); @@ -1211,6 +1211,48 @@ namespace etl char(&array_size(T(&array)[Array_Size]))[Array_Size]; #define ETL_ARRAY_SIZE(a) sizeof(etl::array_size(a)) + +#if ETL_NOT_USING_STL || ETL_CPP17_NOT_SUPPORTED + //************************************************************************** + /// Returns a pointer to the block of memory containing the elements of the range. + ///\ingroup container + //************************************************************************** + template + ETL_CONSTEXPR typename TContainer::pointer data(TContainer& container) + { + return container.data(); + } + + //************************************************************************** + /// Returns a const_pointer to the block of memory containing the elements of the range. + ///\ingroup container + //************************************************************************** + template + ETL_CONSTEXPR typename TContainer::const_pointer data(const TContainer& container) + { + return container.data(); + } + + ///************************************************************************** + /// Returns a pointer to the block of memory containing the elements of the range. + ///\ingroup container + ///************************************************************************** + template + ETL_CONSTEXPR TValue* data(TValue(&a)[Array_Size]) + { + return a; + } + + ///************************************************************************** + /// Returns a const pointer to the block of memory containing the elements of the range. + ///\ingroup container + ///************************************************************************** + template + ETL_CONSTEXPR const TValue* data(const TValue(&a)[Array_Size]) + { + return a; + } +#endif } #endif diff --git a/include/etl/message_router.h b/include/etl/message_router.h index e9b79d30..7e510466 100644 --- a/include/etl/message_router.h +++ b/include/etl/message_router.h @@ -513,33 +513,31 @@ namespace etl //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - etl::message_id_t id = msg.get_message_id(); - size_t index = Number_Of_Messages; + const etl::message_id_t id = msg.get_message_id(); // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + dispatch(msg, index); + return; + } } - // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. - if (index < Number_Of_Messages) + // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. + if (has_successor()) { - dispatch(msg, index); + get_successor().receive(msg); } else { - // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. - if (has_successor()) - { - get_successor().receive(msg); - } - else - { #include "etl/private/diagnostic_array_bounds_push.h" - static_cast(this)->on_receive_unknown(msg); + static_cast(this)->on_receive_unknown(msg); #include "etl/private/diagnostic_pop.h" - } } } @@ -589,29 +587,17 @@ namespace etl //*********************************************** bool accepts(etl::message_id_t id) const ETL_OVERRIDE { - size_t index = Number_Of_Messages; - - // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + if (index < Number_Of_Messages) + { + return true; + } } - if (index < Number_Of_Messages) - { - return true; - } - else - { - if (has_successor()) - { - return get_successor().accepts(id); - } - else - { - return false; - } - } + return has_successor() ? get_successor().accepts(id) : false; } //******************************************** diff --git a/include/etl/numeric.h b/include/etl/numeric.h index dbb43696..3283b338 100644 --- a/include/etl/numeric.h +++ b/include/etl/numeric.h @@ -47,7 +47,7 @@ namespace etl { //*************************************************************************** /// iota - /// Reverse engineered version of std::iota for non C++ 0x11 compilers. + /// Reverse engineered version of std::iota for non C++11 compilers. /// Fills a range of elements with sequentially increasing values starting with value. ///\param first An iterator to the first position to fill. ///\param last An iterator to the last + 1 position. diff --git a/include/etl/platform.h b/include/etl/platform.h index 6db7c305..08532546 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -154,6 +154,16 @@ SOFTWARE. #define ETL_NOT_USING_WIDE_CHARACTERS 0 #endif +//************************************* +// Helper macro for ETL_FORMAT_NO_FLOATING_POINT. +#if defined(ETL_FORMAT_NO_FLOATING_POINT) + #define ETL_USING_FORMAT_FLOATING_POINT 0 + #define ETL_NOT_USING_FORMAT_FLOATING_POINT 1 +#else + #define ETL_USING_FORMAT_FLOATING_POINT 1 + #define ETL_NOT_USING_FORMAT_FLOATING_POINT 0 +#endif + //************************************* // Figure out things about the compiler, if haven't already done so in etl_profile.h #include "profiles/determine_compiler_version.h" @@ -372,11 +382,13 @@ SOFTWARE. #if ETL_USING_EXCEPTIONS #define ETL_NOEXCEPT noexcept #define ETL_NOEXCEPT_EXPR(...) noexcept(__VA_ARGS__) + #define ETL_NOEXCEPT_IF(b) noexcept((b)) #define ETL_NOEXCEPT_FROM(x) noexcept(noexcept(x)) #else #define ETL_NOEXCEPT #define ETL_NOEXCEPT_EXPR(...) - #define ETL_NOEXCEPT_FROM(x) + #define ETL_NOEXCEPT_IF(b) + #define ETL_NOEXCEPT_FROM(x) #endif #else #define ETL_CONSTEXPR @@ -389,7 +401,8 @@ SOFTWARE. #define ETL_NORETURN #define ETL_NOEXCEPT #define ETL_NOEXCEPT_EXPR(...) - #define ETL_NOEXCEPT_FROM(x) + #define ETL_NOEXCEPT_IF(b) + #define ETL_NOEXCEPT_FROM(x) #define ETL_MOVE(x) x #define ETL_ENUM_CLASS(name) enum name #define ETL_ENUM_CLASS_TYPE(name, type) enum name @@ -402,7 +415,7 @@ SOFTWARE. #if ETL_USING_CPP14 #define ETL_CONSTEXPR14 constexpr - #if !defined(ETL_IN_UNIT_TEST) + #if !defined(ETL_IN_UNIT_TEST) #define ETL_DEPRECATED [[deprecated]] #define ETL_DEPRECATED_REASON(reason) [[deprecated(reason)]] #else @@ -448,7 +461,13 @@ SOFTWARE. #define ETL_UNLIKELY #define ETL_CONSTEXPR20 #define ETL_CONSTEVAL - #define ETL_CONSTINIT + #if ETL_USING_CLANG_COMPILER && ETL_COMPILER_FULL_VERSION >= 40000 + #define ETL_CONSTINIT __attribute__((require_constant_initialization)) + #elif ETL_USING_GCC_COMPILER && ETL_COMPILER_FULL_VERSION >= 100000 + #define ETL_CONSTINIT __constinit + #else + #define ETL_CONSTINIT + #endif #define ETL_NO_UNIQUE_ADDRESS #define ETL_EXPLICIT_EXPR(...) explicit #endif @@ -601,7 +620,7 @@ SOFTWARE. #elif defined(ETL_COMPILER_MICROSOFT) #define ETL_PACKED_CLASS(class_type) __pragma(pack(push, 1)) class class_type #define ETL_PACKED_STRUCT(struct_type) __pragma(pack(push, 1)) struct struct_type - #define ETL_PACKED + #define ETL_PACKED #define ETL_END_PACKED __pragma(pack(pop)) #define ETL_HAS_PACKED 1 #else @@ -651,7 +670,8 @@ namespace etl static ETL_CONSTANT bool using_exceptions = (ETL_USING_EXCEPTIONS == 1); static ETL_CONSTANT bool using_libc_wchar_h = (ETL_USING_LIBC_WCHAR_H == 1); static ETL_CONSTANT bool using_std_exception = (ETL_USING_STD_EXCEPTION == 1); - + static ETL_CONSTANT bool using_format_floating_point = (ETL_USING_FORMAT_FLOATING_POINT == 1); + // Has... static ETL_CONSTANT bool has_initializer_list = (ETL_HAS_INITIALIZER_LIST == 1); static ETL_CONSTANT bool has_8bit_types = (ETL_USING_8BIT_TYPES == 1); diff --git a/include/etl/queue.h b/include/etl/queue.h index 4c4fcd9f..6baa3e49 100644 --- a/include/etl/queue.h +++ b/include/etl/queue.h @@ -454,7 +454,7 @@ namespace etl } //************************************************************************* - /// Removes the oldest value from the back of the queue. + /// Removes the oldest value from the front of the queue. /// Does nothing if the queue is already empty. /// If asserts or exceptions are enabled, throws an etl::queue_empty if the queue is empty. //************************************************************************* diff --git a/include/etl/span.h b/include/etl/span.h index c60a9125..c053d37b 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -47,6 +47,12 @@ SOFTWARE. #include "array.h" #include "byte.h" #include "static_assert.h" +#include "container.h" +#include "private/tuple_size.h" + +#if ETL_USING_STL && ETL_USING_CPP20 + #include +#endif #include "private/dynamic_extent.h" @@ -72,7 +78,28 @@ namespace etl //*************************************************************************** // Tag to indicate a class is a span. //*************************************************************************** - class span_tag {}; + + // Forward declaration for trait + template + class span; + + namespace private_span + { + template + struct is_span_helper : etl::false_type {}; + + template + struct is_span_helper > : etl::true_type {}; + } + + template + struct is_span + : private_span::is_span_helper::type> {}; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_span_v = is_span::value; +#endif //*************************************************************************** ///\ingroup span @@ -130,6 +157,12 @@ namespace etl } }; + //*************************************************************************** + ///\ingroup span + /// Tag to indicate a class is a span. + /// Deprecated, use is_span trait instead. + class span_tag {}; + //*************************************************************************** /// Span - Fixed Extent //*************************************************************************** @@ -141,13 +174,13 @@ namespace etl typedef T element_type; typedef typename etl::remove_cv::type value_type; typedef size_t size_type; - typedef T& reference; - typedef const T& const_reference; - typedef T* pointer; - typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; - typedef T* iterator; - typedef const T* const_iterator; + typedef T* iterator; + typedef const T* const_iterator; typedef ETL_OR_STD::reverse_iterator reverse_iterator; typedef ETL_OR_STD::reverse_iterator const_reverse_iterator; @@ -157,72 +190,92 @@ namespace etl static ETL_CONSTANT size_t extent = Extent; //************************************************************************* - /// Construct from iterators + size + /// Default constructor + /// Enabled only for zero extent, creates an empty span. //************************************************************************* - template - ETL_CONSTEXPR explicit span(const TIterator begin_, const TSize /*size_*/) ETL_NOEXCEPT +#if ETL_USING_CPP11 + template ::type> + ETL_CONSTEXPR span() ETL_NOEXCEPT + : pbegin(ETL_NULLPTR) + { + } +#else + ETL_CONSTEXPR span() ETL_NOEXCEPT + : pbegin(ETL_NULLPTR) + { + ETL_STATIC_ASSERT(Extent == 0, "Default constructor only available for zero extent"); + } +#endif + + //************************************************************************* + /// Construct from iterator + size + //************************************************************************* + template + explicit ETL_CONSTEXPR14 span(const TIterator begin_, const size_t size_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) : pbegin(etl::to_address(begin_)) { + ETL_ASSERT(size_ == Extent, ETL_ERROR(span_size_mismatch)); + (void)size_; } //************************************************************************* /// Construct from iterators //************************************************************************* - template - ETL_CONSTEXPR explicit span(const TIterator begin_, const TIterator /*end_*/) ETL_NOEXCEPT + template + ETL_CONSTEXPR14 span(const TIteratorBegin begin_, const TIteratorEnd end_, + typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) : pbegin(etl::to_address(begin_)) { + ETL_ASSERT(etl::distance(begin_, end_) == Extent, ETL_ERROR(span_size_mismatch)); + (void)end_; } //************************************************************************* /// Construct from C array //************************************************************************* -#if ETL_USING_CPP11 - template::type> - ETL_CONSTEXPR span(element_type(&begin_)[Array_Size]) ETL_NOEXCEPT - : pbegin(begin_) - { - } -#else - //************************************************************************* - /// Construct from C array - //************************************************************************* template - ETL_CONSTEXPR span(element_type(&begin_)[Array_Size], typename etl::enable_if<(Array_Size == Extent), void>::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR span(typename etl::type_identity::type(&begin_)[Array_Size], typename etl::enable_if<(Array_Size == Extent), void>::type* = 0) ETL_NOEXCEPT : pbegin(begin_) { } -#endif #if ETL_USING_CPP11 //************************************************************************* /// Construct from a container or other type that supports /// data() and size() member functions. //************************************************************************* - template >::value && - !etl::is_std_array>::value && - !etl::is_etl_array>::value && - !etl::is_pointer>::value && - !etl::is_array>::value && - etl::is_same, etl::remove_cv_t::value_type>>::value, void>::type> - ETL_CONSTEXPR span(TContainer&& a) ETL_NOEXCEPT - : pbegin(a.data()) - { - } + template + ETL_CONSTEXPR14 span(TContainer&& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + etl::is_lvalue_reference::value && + has_size::value && + has_data::value && + etl::is_convertible::type&>().data()), pointer>::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + : pbegin(a.data()) + { + ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); + } #else //************************************************************************* /// Construct from a container or other type that supports /// data() and size() member functions. //************************************************************************* template - span(TContainer& a, typename etl::enable_if::type>::value && + span(TContainer& a, typename etl::enable_if::value && !etl::is_std_array::type>::value && !etl::is_etl_array::type>::value && !etl::is_pointer::type>::value && !etl::is_array::value && - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) : pbegin(a.data()) { + ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); } //************************************************************************* @@ -230,17 +283,69 @@ namespace etl /// data() and size() member functions. //************************************************************************* template - span(const TContainer& a, typename etl::enable_if::type>::value && + span(const TContainer& a, typename etl::enable_if::value && !etl::is_std_array::type>::value && !etl::is_etl_array::type>::value && !etl::is_pointer::type>::value && - !etl::is_array::value&& - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) : pbegin(a.data()) { + ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); } #endif + //************************************************************************* + /// Constructor from etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(etl::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + + //************************************************************************* + /// Constructor from const etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(const etl::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + +#if ETL_USING_CPP11 + template + span(etl::array&&) = delete; +#endif + +#if ETL_USING_STL && ETL_USING_CPP11 + //************************************************************************* + /// Constructor from std array. + //************************************************************************* + template + ETL_CONSTEXPR span(std::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + + //************************************************************************* + /// Constructor from const std array. + //************************************************************************* + template + ETL_CONSTEXPR span(const std::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + + template + span(std::array&&) = delete; +#endif + //************************************************************************* /// Copy constructor //************************************************************************* @@ -254,7 +359,7 @@ namespace etl /// From fixed extent span. //************************************************************************* template - ETL_CONSTEXPR span(const etl::span& other, typename etl::enable_if::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR span(const etl::span& other, typename etl::enable_if<(Size == Extent) && (Size != etl::dynamic_extent), void>::type* = 0) ETL_NOEXCEPT : pbegin(other.data()) { } @@ -264,50 +369,37 @@ namespace etl /// From dynamic extent span. //************************************************************************* template - ETL_CONSTEXPR14 span(const etl::span& other, typename etl::enable_if::type* = 0) + ETL_CONSTEXPR14 explicit span(const etl::span& other, typename etl::enable_if<(Size == etl::dynamic_extent), void>::type* = 0) : pbegin(other.data()) { ETL_ASSERT(other.size() == Extent, ETL_ERROR(span_size_mismatch)); } -#if ETL_USING_STL && ETL_USING_CPP11 +#if ETL_USING_STL && ETL_USING_CPP20 //************************************************************************* - /// Constructor from std array. + /// Copy constructor + /// From fixed extent std::span. //************************************************************************* template - ETL_CONSTEXPR span(std::array& other, typename etl::enable_if::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR span(const std::span& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, int>::type* = 0) ETL_NOEXCEPT : pbegin(other.data()) { } //************************************************************************* - /// Constructor from const std array. + /// Copy constructor + /// From dynamic extent std::span. //************************************************************************* template - ETL_CONSTEXPR span(const std::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR14 span(const std::span& other, typename etl::enable_if<(Size == etl::dynamic_extent && + etl::is_convertible::value), int>::type* = 0) ETL_NOEXCEPT : pbegin(other.data()) { + ETL_ASSERT(other.size() == Extent, ETL_ERROR(span_size_mismatch)); } #endif - //************************************************************************* - /// Constructor from etl array. - //************************************************************************* - template - ETL_CONSTEXPR span(etl::array& other, typename etl::enable_if::type* = 0) ETL_NOEXCEPT - : pbegin(other.data()) - { - } - - //************************************************************************* - /// Constructor from const etl array. - //************************************************************************* - template - ETL_CONSTEXPR span(const etl::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT - : pbegin(other.data()) - { - } - //************************************************************************* /// Returns a reference to the first element. //************************************************************************* @@ -455,7 +547,7 @@ namespace etl { pbegin = other.pbegin; return *this; - } + } //************************************************************************* /// Returns a reference to the value at index 'i'. @@ -506,13 +598,13 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the first count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); #else ETL_ASSERT_CHECK_EXTRA(count <= size(), ETL_ERROR(span_out_of_range)); - + return etl::span(pbegin, pbegin + count); #endif } @@ -532,11 +624,11 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA - return count <= size() ? - etl::span((pbegin + Extent) - count, (pbegin + Extent)) : + return count <= size() ? + etl::span((pbegin + Extent) - count, (pbegin + Extent)) : throw(ETL_ERROR(span_out_of_range)); #else ETL_ASSERT_CHECK_EXTRA(count <= size(), ETL_ERROR(span_out_of_range)); @@ -589,7 +681,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view from 'offset' over the next 'count' elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (offset <= size()) && (count != etl::dynamic_extent ? count <= (size() - offset) : true) ? @@ -636,7 +728,7 @@ namespace etl /// Span - Dynamic Extent //*************************************************************************** template - class span : public span_tag + class span : public span_tag { public: @@ -668,10 +760,10 @@ namespace etl } //************************************************************************* - /// Construct from pointer + size + /// Construct from iterator + size //************************************************************************* - template - ETL_CONSTEXPR span(const TIterator begin_, const TSize size_) ETL_NOEXCEPT + template + ETL_CONSTEXPR span(const TIterator begin_, size_t size_) ETL_NOEXCEPT : pbegin(etl::to_address(begin_)) , pend(etl::to_address(begin_) + size_) { @@ -680,8 +772,9 @@ namespace etl //************************************************************************* /// Construct from iterators //************************************************************************* - template - ETL_CONSTEXPR span(const TIterator begin_, const TIterator end_) ETL_NOEXCEPT + template + ETL_CONSTEXPR span(const TIteratorBegin begin_, const TIteratorEnd end_, + typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(etl::to_address(begin_)) , pend(etl::to_address(begin_) + etl::distance(begin_, end_)) { @@ -702,11 +795,17 @@ namespace etl /// Construct from a container or other type that supports /// data() and size() member functions. //************************************************************************* - template >::value && - !etl::is_pointer>::value && - !etl::is_array>::value && - etl::is_same, etl::remove_cv_t::value_type>>::value, void>::type> - ETL_CONSTEXPR span(TContainer&& a) ETL_NOEXCEPT + template + ETL_CONSTEXPR span(TContainer&& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + etl::is_lvalue_reference::value && + has_size::value && + has_data::value && + etl::is_convertible::type&>().data()), pointer>::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(a.data()) , pend(a.data() + a.size()) { @@ -717,10 +816,14 @@ namespace etl /// data() and size() member functions. //************************************************************************* template - ETL_CONSTEXPR span(TContainer& a, typename etl::enable_if::type>::value && - !etl::is_pointer::type>::value && - !etl::is_array::value && - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + span(TContainer& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(a.data()) , pend(a.data() + a.size()) { @@ -731,16 +834,92 @@ namespace etl /// data() and size() member functions. //************************************************************************* template - ETL_CONSTEXPR span(const TContainer& a, typename etl::enable_if::type>::value && - !etl::is_pointer::type>::value && - !etl::is_array::value && - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + span(const TContainer& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(a.data()) , pend(a.data() + a.size()) { } #endif +#if ETL_USING_STL && ETL_USING_CPP20 + //************************************************************************* + /// Constructor from std span. + //************************************************************************* + template + ETL_CONSTEXPR span(std::span& other, typename etl::enable_if::value, int>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + , pend(other.data() + other.size()) + { + } + + //************************************************************************* + /// Constructor from const std span. + //************************************************************************* + template + ETL_CONSTEXPR span(const std::span& other, typename etl::enable_if::value, int>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + , pend(other.data() + other.size()) + { + } +#endif + + //************************************************************************* + /// Constructor from etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(etl::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + + //************************************************************************* + /// Constructor from const etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(const etl::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + +#if ETL_USING_CPP11 + template + span(etl::array&&) = delete; +#endif + +#if ETL_USING_STL && ETL_USING_CPP11 + //************************************************************************* + /// Constructor from std array. + //************************************************************************* + template + ETL_CONSTEXPR span(std::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + + //************************************************************************* + /// Constructor from const std array. + //************************************************************************* + template + ETL_CONSTEXPR span(const std::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + + template + span(std::array&&) = delete; +#endif + //************************************************************************* /// Copy constructor //************************************************************************* @@ -763,7 +942,7 @@ namespace etl //************************************************************************* /// Returns a reference to the first element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *pbegin : throw(ETL_ERROR(span_out_of_range)); @@ -777,7 +956,7 @@ namespace etl //************************************************************************* /// Returns a reference to the last element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *(pend - 1) : throw(ETL_ERROR(span_out_of_range)); @@ -956,7 +1135,7 @@ namespace etl /// Obtains a span that is a view over the first COUNT elements of this span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pbegin, pbegin + COUNT) : throw(ETL_ERROR(span_out_of_range)); @@ -970,7 +1149,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the first count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -985,7 +1164,7 @@ namespace etl /// Obtains a span that is a view over the last COUNT elements of this span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pend - COUNT, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -999,7 +1178,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pend - count, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1016,7 +1195,7 @@ namespace etl //************************************************************************* template ETL_NODISCARD ETL_CONSTEXPR - etl::span subspan() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + etl::span subspan() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (OFFSET <= size()) && (COUNT != etl::dynamic_extent ? COUNT <= (size() - OFFSET) : true) ? @@ -1055,7 +1234,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view from 'offset' over the next 'count' elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR14 etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { ETL_ASSERT_CHECK_EXTRA(offset <= size(), ETL_ERROR(span_out_of_range)); ETL_ASSERT_CHECK_EXTRA(count != etl::dynamic_extent ? count <= (size() - offset) : true, ETL_ERROR(span_out_of_range)); @@ -1161,7 +1340,7 @@ namespace etl /// Compare two spans for equality. //************************************************************************* template - ETL_NODISCARD + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::type, typename etl::remove_cv::type>::value, bool>::type operator ==(const etl::span& lhs, const etl::span& rhs) ETL_NOEXCEPT @@ -1255,7 +1434,7 @@ namespace etl template span(etl::ivector&) -> span; - + template span(const etl::ivector&) -> span; @@ -1269,7 +1448,7 @@ namespace etl span(const std::array&) ->span; #endif -#endif +#endif //************************************************************************* /// Hash function. @@ -1290,7 +1469,7 @@ namespace etl /// Obtains a view to the byte representation of the elements of the span s. //************************************************************************* template - span + span as_bytes(span s) ETL_NOEXCEPT { return span(reinterpret_cast(s.data()), s.size_bytes()); @@ -1300,7 +1479,7 @@ namespace etl /// Obtains a view to the byte representation of the elements of the span s. //************************************************************************* template - span + span as_writable_bytes(span s) ETL_NOEXCEPT { ETL_STATIC_ASSERT(!etl::is_const::value, "span must be of non-const type"); diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 524b74eb..521aaf48 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -753,11 +753,11 @@ namespace etl ///\ingroup type_traits /// Implemented by checking if type is convertible to an integer through static_cast - namespace private_type_traits + namespace private_type_traits { // Base case template - struct is_convertible_to_int + struct is_convertible_to_int : false_type { }; @@ -766,7 +766,7 @@ namespace etl // 2nd template argument of base case defaults to int to ensure that this partial specialization is always tried first template struct is_convertible_to_int(declval()))> - : true_type + : true_type { }; } @@ -776,7 +776,7 @@ namespace etl : integral_constant::value && !is_class::value && !is_arithmetic::value && - !is_reference::value> + !is_reference::value> { }; @@ -860,6 +860,42 @@ namespace etl public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1408,6 +1444,42 @@ typedef integral_constant true_type; public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1542,13 +1614,13 @@ typedef integral_constant true_type; /// Template to determine if a type is one of a specified list. ///\ingroup types template struct is_one_of { - static const bool value = + static const bool value = etl::is_same::value || etl::is_same::value || etl::is_same::value || @@ -1640,7 +1712,7 @@ typedef integral_constant true_type; //*************************************************************************** /// Get the Nth base of a recursively inherited type. /// Requires that the class has defined 'base_type'. - //*************************************************************************** + //*************************************************************************** // Recursive definition of the type. template struct nth_base @@ -2185,7 +2257,7 @@ typedef integral_constant true_type; #if ETL_USING_CPP11 //*************************************************************************** /// is_constructible - namespace private_type_traits + namespace private_type_traits { template struct is_constructible_ : etl::false_type {}; @@ -2364,7 +2436,7 @@ typedef integral_constant true_type; }; template - struct common_type_2_impl + struct common_type_2_impl : decay_conditional_result { }; @@ -2709,7 +2781,7 @@ typedef integral_constant true_type; struct is_member_pointer_helper : etl::true_type {}; } - template + template struct is_member_pointer : private_type_traits::is_member_pointer_helper::type> {}; #if ETL_USING_CPP17 @@ -2802,10 +2874,10 @@ typedef integral_constant true_type; //*************************************************************************** namespace private_type_traits { - template + template struct is_member_object_pointer_helper : public etl::false_type {}; - template + template struct is_member_object_pointer_helper : public etl::negation> {}; } @@ -3027,6 +3099,60 @@ typedef integral_constant true_type; }; #endif + +#if ETL_USING_CPP11 + template + struct has_size : etl::false_type {}; + + template + struct has_size().size())> > + : etl::true_type {}; +#else + template + struct has_size + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_size(char (*)[sizeof(&U::size)]); + + template + static no test_size(...); + + public: + + static const bool value = (sizeof(test_size(0)) == sizeof(yes)); + }; +#endif + +#if ETL_USING_CPP11 + template + struct has_data : etl::false_type {}; + + template + struct has_data().data())> > + : etl::true_type {}; +#else + template + struct has_data + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_data(char (*)[sizeof(&U::data)]); + + template + static no test_data(...); + + public: + + static const bool value = (sizeof(test_data(0)) == sizeof(yes)); + }; +#endif } // Helper macros diff --git a/scripts/clang-format-wrapper b/scripts/clang-format-wrapper new file mode 100755 index 00000000..b501b663 --- /dev/null +++ b/scripts/clang-format-wrapper @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +from shutil import which +import re +import subprocess +import sys + + +def get_correct_clang_format(): + path = which("clang-format-18") + if path: + return path + + if which("clang-format") is None: + raise Exception("no clang-format found") + + result = subprocess.run( + ["clang-format", "--version"], capture_output=True, text=True, check=True + ) + match = re.search(r"\b(\d+\.\d+\.\d+)\b", result.stdout) + if not match: + raise Exception( + f"could not determine clang-format version from: {result.stdout.strip()}" + ) + version = match.group(1) + if version.split(".")[0] != "18": + raise Exception(f"clang-format version 18 required. Found {version}") + + return "clang-format" + + +def main(): + clang_format = get_correct_clang_format() + try: + completed = subprocess.run([clang_format] + sys.argv[1:]) + except FileNotFoundError: + print(f"error: clang-format not found at '{clang_format}'", file=sys.stderr) + sys.exit(1) + except PermissionError: + print(f"error: permission denied when running '{clang_format}'", file=sys.stderr) + sys.exit(1) + except OSError as exc: + print(f"error: failed to run '{clang_format}': {exc}", file=sys.stderr) + sys.exit(1) + sys.exit(completed.returncode) + + +if __name__ == "__main__": + main() diff --git a/scripts/update_version.py b/scripts/update_version.py index 9d9fbc65..705a07a8 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -129,6 +129,29 @@ def update_library_properties(filename): f.write(line) f.write('\n') +#------------------------------------------------------------------------------ +def update_zephyr_module(): + print('') + print('Updating zephyr/module.yml') + + zephyr_module = os.path.join(etl_dir, 'zephyr', 'module.yml') + + with open(zephyr_module, 'r') as f: + text = f.read().splitlines() + + search_purl = 'pkg:github/ETLCPP/etl@' + + for i in range(len(text)): + if search_purl in text[i]: + idx = text[i].find(search_purl) + text[i] = text[i][:idx] + search_purl + full_version + print(text[i]) + + with open(zephyr_module, 'w') as f: + for line in text: + f.write(line) + f.write('\n') + #------------------------------------------------------------------------------ def update_versions(): print('') @@ -149,6 +172,8 @@ def update_versions(): update_library_properties(os.path.join(etl_dir, 'library.properties')) + update_zephyr_module() + #------------------------------------------------------------------------------ if __name__ == "__main__": update_versions() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2fdfc71c..2bec7847 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,6 +86,7 @@ add_executable(etl_tests test_closure.cpp test_closure_constexpr.cpp test_compare.cpp + test_concepts.cpp test_constant.cpp test_const_map.cpp test_const_map_constexpr.cpp @@ -172,6 +173,7 @@ add_executable(etl_tests test_endian.cpp test_enum_type.cpp test_error_handler.cpp + test_etl_assert.cpp test_etl_traits.cpp test_exception.cpp test_expected.cpp @@ -183,6 +185,7 @@ add_executable(etl_tests test_flat_multiset.cpp test_flat_set.cpp test_fnv_1.cpp + test_format.cpp test_format_spec.cpp test_forward_list.cpp test_forward_list_shared_pool.cpp @@ -194,10 +197,12 @@ add_executable(etl_tests test_hash.cpp test_hfsm.cpp test_hfsm_recurse_to_inner_state_on_start.cpp + test_hfsm_transition_on_enter.cpp test_histogram.cpp test_index_of_type.cpp test_indirect_vector.cpp test_indirect_vector_external_buffer.cpp + test_inplace_function.cpp test_instance_count.cpp test_integral_limits.cpp test_intrusive_forward_list.cpp @@ -206,7 +211,9 @@ add_executable(etl_tests test_intrusive_queue.cpp test_intrusive_stack.cpp test_invert.cpp + test_invoke.cpp test_io_port.cpp + test_is_invocable.cpp test_iterator.cpp test_jenkins.cpp test_largest.cpp @@ -259,6 +266,7 @@ add_executable(etl_tests test_pool.cpp test_pool_external_buffer.cpp test_priority_queue.cpp + test_print.cpp test_pseudo_moving_average.cpp test_quantize.cpp test_queue.cpp @@ -286,6 +294,7 @@ add_executable(etl_tests test_scaled_rounding.cpp test_set.cpp test_shared_message.cpp + test_signal.cpp test_singleton.cpp test_singleton_base.cpp test_smallest.cpp diff --git a/test/iterators_for_unit_tests.h b/test/iterators_for_unit_tests.h index 16085f87..82d28c6c 100644 --- a/test/iterators_for_unit_tests.h +++ b/test/iterators_for_unit_tests.h @@ -113,6 +113,12 @@ struct non_random_iterator : public etl::iterator +bool operator ==(const non_random_iterator& lhs, const non_random_iterator& rhs) +{ + return lhs.ptr == rhs.ptr; +} + template bool operator !=(const non_random_iterator& lhs, const non_random_iterator& rhs) { diff --git a/test/run-syntax-checks.sh b/test/run-syntax-checks.sh old mode 100644 new mode 100755 diff --git a/test/test_algorithm.cpp b/test/test_algorithm.cpp index abdab9d5..1429a977 100644 --- a/test/test_algorithm.cpp +++ b/test/test_algorithm.cpp @@ -747,6 +747,62 @@ namespace } } + //************************************************************************* + TEST(binary_search_random_iterator) + { + for (int i = 0; i < 11; ++i) + { + bool expected = std::binary_search(std::begin(dataS), std::end(dataS), i); + bool result = etl::binary_search(std::begin(dataS), std::end(dataS), i); + + CHECK_EQUAL(expected, result); + } + } + + //************************************************************************* + TEST(binary_search_with_compare) + { + for (int i = 0; i < 11; ++i) + { + bool expected = std::binary_search(std::begin(dataS), std::end(dataS), i, etl::less()); + bool result = etl::binary_search(std::begin(dataS), std::end(dataS), i, etl::less()); + + CHECK_EQUAL(expected, result); + } + } + + //************************************************************************* + TEST(binary_search_duplicates) + { + for (int i = 0; i < 11; ++i) + { + bool expected = std::binary_search(std::begin(dataEQ), std::end(dataEQ), i); + bool result = etl::binary_search(std::begin(dataEQ), std::end(dataEQ), i); + + CHECK_EQUAL(expected, result); + } + } + + //************************************************************************* + TEST(binary_search_empty_range) + { + int empty[] = { 0 }; + + bool result = etl::binary_search(std::begin(empty), std::begin(empty), 1); + + CHECK_EQUAL(false, result); + } + + //************************************************************************* + TEST(binary_search_single_element) + { + int single[] = { 5 }; + + CHECK_EQUAL(true, etl::binary_search(std::begin(single), std::end(single), 5)); + CHECK_EQUAL(false, etl::binary_search(std::begin(single), std::end(single), 3)); + CHECK_EQUAL(false, etl::binary_search(std::begin(single), std::end(single), 7)); + } + //************************************************************************* TEST(fill_non_char) { @@ -795,6 +851,144 @@ namespace CHECK_EQUAL(1, b); } + //************************************************************************* + TEST(swap_ranges_pod_pointer) + { + int data1[] = { 1, 2, 3, 4, 5 }; + int data2[] = { 6, 7, 8, 9, 10 }; + + int expected1[] = { 6, 7, 8, 9, 10 }; + int expected2[] = { 1, 2, 3, 4, 5 }; + + int* result = etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data2)); + + CHECK_EQUAL(std::end(data2), result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_non_pod_pointer) + { + Data data1[] = { Data(1, 2), Data(3, 4), Data(5, 6) }; + Data data2[] = { Data(7, 8), Data(9, 10), Data(11, 12) }; + + Data expected1[] = { Data(7, 8), Data(9, 10), Data(11, 12) }; + Data expected2[] = { Data(1, 2), Data(3, 4), Data(5, 6) }; + + Data* result = etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data2)); + + CHECK_EQUAL(std::end(data2), result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_non_random_iterator) + { + List data1 = { 1, 2, 3, 4, 5 }; + List data2 = { 6, 7, 8, 9, 10 }; + + List expected1 = { 6, 7, 8, 9, 10 }; + List expected2 = { 1, 2, 3, 4, 5 }; + + List::iterator result = etl::swap_ranges(data1.begin(), data1.end(), data2.begin()); + + CHECK(data2.end() == result); + + bool isEqual1 = std::equal(data1.begin(), data1.end(), expected1.begin()); + CHECK(isEqual1); + + bool isEqual2 = std::equal(data2.begin(), data2.end(), expected2.begin()); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_empty_range) + { + int data1[] = { 1, 2, 3 }; + int data2[] = { 4, 5, 6 }; + + int expected1[] = { 1, 2, 3 }; + int expected2[] = { 4, 5, 6 }; + + int* result = etl::swap_ranges(std::begin(data1), std::begin(data1), std::begin(data2)); + + CHECK_EQUAL(std::begin(data2), result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_partial_range) + { + int data1[] = { 1, 2, 3, 4, 5 }; + int data2[] = { 6, 7, 8, 9, 10 }; + + int expected1[] = { 6, 7, 8, 4, 5 }; + int expected2[] = { 1, 2, 3, 9, 10 }; + + int* result = etl::swap_ranges(std::begin(data1), std::begin(data1) + 3, std::begin(data2)); + + CHECK_EQUAL(std::begin(data2) + 3, result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_same_data) + { + int data1[] = { 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data1)); + + bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(expected)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(swap_ranges_matches_std) + { + int data1_std[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int data2_std[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + int data1_etl[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int data2_etl[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + int* pstl = std::swap_ranges(std::begin(data1_std), std::end(data1_std), std::begin(data2_std)); + int* petl = etl::swap_ranges(std::begin(data1_etl), std::end(data1_etl), std::begin(data2_etl)); + + using difference_type_t = std::iterator_traits::difference_type; + + difference_type_t dstl = std::distance(data2_std, pstl); + difference_type_t detl = std::distance(data2_etl, petl); + + CHECK_EQUAL(dstl, detl); + + bool isEqual1 = std::equal(std::begin(data1_std), std::end(data1_std), std::begin(data1_etl)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2_std), std::end(data2_std), std::begin(data2_etl)); + CHECK(isEqual2); + } + //************************************************************************* TEST(equal) { @@ -854,6 +1048,314 @@ namespace CHECK(itr1 == itr2); } + //************************************************************************* + TEST(find_end_default) + { + int data[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern[] = { 1, 2, 3 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_predicate) + { + int data[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern[] = { 1, 2, 3 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern), std::equal_to()); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern), std::equal_to()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_single_occurrence) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int pattern[] = { 5, 6, 7 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_no_match) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int pattern[] = { 11, 12 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_empty_sequence) + { + int data[] = { 1, 2, 3, 4, 5 }; + int pattern[] = { 0 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::begin(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::begin(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_pattern_at_end) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7 }; + int pattern[] = { 5, 6, 7 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_pattern_at_start) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7 }; + int pattern[] = { 1, 2, 3 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_entire_range_matches) + { + int data[] = { 1, 2, 3, 4, 5 }; + int pattern[] = { 1, 2, 3, 4, 5 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_overlapping_occurrences) + { + int data[] = { 1, 1, 1, 1, 1 }; + int pattern[] = { 1, 1 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_single_element_pattern) + { + int data[] = { 1, 2, 3, 2, 5, 2, 7 }; + int pattern[] = { 2 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_non_random_iterator) + { + int data_array[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern_array[] = { 1, 2, 3 }; + + List data(std::begin(data_array), std::end(data_array)); + List pattern(std::begin(pattern_array), std::end(pattern_array)); + + List::iterator expected = std::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + List::iterator result = etl::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(find_end_non_random_iterator_predicate) + { + int data_array[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern_array[] = { 1, 2, 3 }; + + List data(std::begin(data_array), std::end(data_array)); + List pattern(std::begin(pattern_array), std::end(pattern_array)); + + List::iterator expected = std::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + List::iterator result = etl::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(find_end_pattern_longer_than_data) + { + int data[] = { 1, 2, 3 }; + int pattern[] = { 1, 2, 3, 4, 5 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_default) + { + int data[] = { 1, 2, 3, 3, 4, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_predicate) + { + int data[] = { 1, 2, 3, 3, 4, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data), std::equal_to()); + int* result = etl::adjacent_find(std::begin(data), std::end(data), std::equal_to()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_no_match) + { + int data[] = { 1, 2, 3, 4, 5, 6 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_at_beginning) + { + int data[] = { 1, 1, 2, 3, 4, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_at_end) + { + int data[] = { 1, 2, 3, 4, 5, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_multiple_pairs) + { + int data[] = { 1, 1, 2, 2, 3, 3 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_single_element) + { + int data[] = { 1 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_empty_range) + { + int data[] = { 1 }; + + int* expected = std::adjacent_find(std::begin(data), std::begin(data)); + int* result = etl::adjacent_find(std::begin(data), std::begin(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_all_same) + { + int data[] = { 5, 5, 5, 5, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_predicate_less) + { + int data[] = { 5, 4, 3, 2, 1 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data), std::greater()); + int* result = etl::adjacent_find(std::begin(data), std::end(data), std::greater()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_non_random_iterator) + { + int data_array[] = { 1, 2, 3, 3, 4, 5 }; + List data(std::begin(data_array), std::end(data_array)); + + List::iterator expected = std::adjacent_find(data.begin(), data.end()); + List::iterator result = etl::adjacent_find(data.begin(), data.end()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(adjacent_find_non_random_iterator_predicate) + { + int data_array[] = { 1, 2, 3, 3, 4, 5 }; + List data(std::begin(data_array), std::end(data_array)); + + List::iterator expected = std::adjacent_find(data.begin(), data.end(), std::equal_to()); + List::iterator result = etl::adjacent_find(data.begin(), data.end(), std::equal_to()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(adjacent_find_non_random_iterator_no_match) + { + int data_array[] = { 1, 2, 3, 4, 5, 6 }; + List data(std::begin(data_array), std::end(data_array)); + + List::iterator expected = std::adjacent_find(data.begin(), data.end()); + List::iterator result = etl::adjacent_find(data.begin(), data.end()); + + CHECK(expected == result); + } + //************************************************************************* TEST(heap) { @@ -1021,6 +1523,663 @@ namespace CHECK(isEqual); } + //************************************************************************* + TEST(is_heap_default_true) + { + std::vector data = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + std::make_heap(data.begin(), data.end()); + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_default_false) + { + std::vector data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(!result); + } + + //************************************************************************* + TEST(is_heap_compare_true) + { + std::vector data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::make_heap(data.begin(), data.end(), Greater()); + + bool expected = std::is_heap(data.begin(), data.end(), Greater()); + bool result = etl::is_heap(data.begin(), data.end(), Greater()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_compare_false) + { + std::vector data = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + bool expected = std::is_heap(data.begin(), data.end(), Greater()); + bool result = etl::is_heap(data.begin(), data.end(), Greater()); + + CHECK_EQUAL(expected, result); + CHECK(!result); + } + + //************************************************************************* + TEST(is_heap_empty) + { + std::vector data; + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_single_element) + { + std::vector data = { 42 }; + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_two_elements) + { + std::vector data1 = { 5, 3 }; + std::vector data2 = { 3, 5 }; + + CHECK_EQUAL(std::is_heap(data1.begin(), data1.end()), etl::is_heap(data1.begin(), data1.end())); + CHECK_EQUAL(std::is_heap(data2.begin(), data2.end()), etl::is_heap(data2.begin(), data2.end())); + } + + //************************************************************************* + TEST(is_heap_pointer) + { + int data[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + bool expected = std::is_heap(std::begin(data), std::end(data)); + bool result = etl::is_heap(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(is_heap_after_make_heap) + { + // Test all permutations of a small dataset + std::vector data = { 1, 2, 3, 4, 5 }; + + do + { + std::vector test_data(data); + etl::make_heap(test_data.begin(), test_data.end()); + CHECK(etl::is_heap(test_data.begin(), test_data.end())); + } while (std::next_permutation(data.begin(), data.end())); + } + + //************************************************************************* + TEST(sort_heap_default) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::make_heap(data1.begin(), data1.end()); + etl::make_heap(data2.begin(), data2.end()); + + std::sort_heap(data1.begin(), data1.end()); + etl::sort_heap(data2.begin(), data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + + // Verify sorted ascending + CHECK(std::is_sorted(data2.begin(), data2.end())); + } + + //************************************************************************* + TEST(sort_heap_compare) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::make_heap(data1.begin(), data1.end(), Greater()); + etl::make_heap(data2.begin(), data2.end(), Greater()); + + std::sort_heap(data1.begin(), data1.end(), Greater()); + etl::sort_heap(data2.begin(), data2.end(), Greater()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + + // Verify sorted descending + CHECK(std::is_sorted(data2.begin(), data2.end(), Greater())); + } + + //************************************************************************* + TEST(sort_heap_empty) + { + std::vector data; + + etl::sort_heap(data.begin(), data.end()); + + CHECK(data.empty()); + } + + //************************************************************************* + TEST(sort_heap_single_element) + { + std::vector data = { 42 }; + + etl::sort_heap(data.begin(), data.end()); + + CHECK_EQUAL(1U, data.size()); + CHECK_EQUAL(42, data[0]); + } + + //************************************************************************* + TEST(sort_heap_pointer) + { + int data1[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + int data2[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + + std::make_heap(std::begin(data1), std::end(data1)); + etl::make_heap(std::begin(data2), std::end(data2)); + + std::sort_heap(std::begin(data1), std::end(data1)); + etl::sort_heap(std::begin(data2), std::end(data2)); + + bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(sort_heap_all_permutations) + { + std::vector initial = { 1, 2, 3, 4, 5 }; + + do + { + std::vector data1(initial); + std::vector data2(initial); + + std::make_heap(data1.begin(), data1.end()); + etl::make_heap(data2.begin(), data2.end()); + + std::sort_heap(data1.begin(), data1.end()); + etl::sort_heap(data2.begin(), data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + } while (std::next_permutation(initial.begin(), initial.end())); + } + + //************************************************************************* + TEST(sort_heap_duplicates) + { + std::vector data1 = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 }; + std::vector data2(data1); + + std::make_heap(data1.begin(), data1.end()); + etl::make_heap(data2.begin(), data2.end()); + + std::sort_heap(data1.begin(), data1.end()); + etl::sort_heap(data2.begin(), data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_default) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end()); + + // The first 5 elements should be sorted and match std + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + // Verify sorted range + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5)); + } + + //************************************************************************* + TEST(partial_sort_compare) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end(), Greater()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end(), Greater()); + + // The first 5 elements should be sorted descending and match std + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + // Verify sorted range (descending) + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5, Greater())); + } + + //************************************************************************* + TEST(partial_sort_empty) + { + std::vector data; + + etl::partial_sort(data.begin(), data.begin(), data.end()); + + CHECK(data.empty()); + } + + //************************************************************************* + TEST(partial_sort_middle_equals_first) + { + std::vector data = { 5, 3, 8, 1, 9 }; + std::vector original(data); + + etl::partial_sort(data.begin(), data.begin(), data.end()); + + // Nothing should change when middle == first + bool isEqual = std::equal(data.begin(), data.end(), original.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_middle_equals_last) + { + std::vector data1 = { 5, 3, 8, 1, 9 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.end(), data1.end()); + etl::partial_sort(data2.begin(), data2.end(), data2.end()); + + // Full sort + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.end())); + } + + //************************************************************************* + TEST(partial_sort_single_element) + { + std::vector data = { 42 }; + + etl::partial_sort(data.begin(), data.end(), data.end()); + + CHECK_EQUAL(1U, data.size()); + CHECK_EQUAL(42, data[0]); + } + + //************************************************************************* + TEST(partial_sort_first_one) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 1, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 1, data2.end()); + + // The first element should be the minimum + CHECK_EQUAL(data1[0], data2[0]); + CHECK_EQUAL(1, data2[0]); + } + + //************************************************************************* + TEST(partial_sort_duplicates) + { + std::vector data1 = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 6, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 6, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 6, data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.begin() + 6)); + } + + //************************************************************************* + TEST(partial_sort_already_sorted) + { + std::vector data1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5)); + } + + //************************************************************************* + TEST(partial_sort_reverse_sorted) + { + std::vector data1 = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5)); + } + + //************************************************************************* + TEST(partial_sort_pointer) + { + int data1[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + int data2[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + + std::partial_sort(std::begin(data1), std::begin(data1) + 5, std::end(data1)); + etl::partial_sort(std::begin(data2), std::begin(data2) + 5, std::end(data2)); + + bool isEqual = std::equal(std::begin(data1), std::begin(data1) + 5, std::begin(data2)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_all_permutations) + { + std::vector initial = { 1, 2, 3, 4, 5 }; + + do + { + std::vector data1(initial); + std::vector data2(initial); + + std::partial_sort(data1.begin(), data1.begin() + 3, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 3, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 3, data2.begin()); + CHECK(isEqual); + } while (std::next_permutation(initial.begin(), initial.end())); + } + + //************************************************************************* + TEST(partial_sort_all_equal) + { + std::vector data = { 5, 5, 5, 5, 5, 5 }; + + etl::partial_sort(data.begin(), data.begin() + 3, data.end()); + + CHECK(std::is_sorted(data.begin(), data.begin() + 3)); + + for (size_t i = 0; i < data.size(); ++i) + { + CHECK_EQUAL(5, data[i]); + } + } + + //************************************************************************* + TEST(partial_sort_copy_default) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_compare) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end(), Greater()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end(), Greater()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end(), Greater())); + } + + //************************************************************************* + TEST(partial_sort_copy_empty_input) + { + std::vector input; + std::vector output(5, 99); + + auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result == output.begin()); + + // Output should be unchanged + for (size_t i = 0; i < output.size(); ++i) + { + CHECK_EQUAL(99, output[i]); + } + } + + //************************************************************************* + TEST(partial_sort_copy_empty_output) + { + std::vector input = { 5, 3, 8, 1, 9 }; + std::vector output; + + auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result == output.begin()); + } + + //************************************************************************* + TEST(partial_sort_copy_output_larger_than_input) + { + std::vector input = { 5, 3, 1 }; + std::vector output1(6, 99); + std::vector output2(6, 99); + + auto result1 = std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + auto result2 = etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + CHECK_EQUAL(std::distance(output1.begin(), result1), std::distance(output2.begin(), result2)); + + bool isEqual = std::equal(output1.begin(), result1, output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), result2)); + } + + //************************************************************************* + TEST(partial_sort_copy_output_smaller_than_input) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(3); + std::vector output2(3); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_output_same_size_as_input) + { + std::vector input = { 5, 3, 8, 1, 9 }; + std::vector output1(5); + std::vector output2(5); + + auto result1 = std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + auto result2 = etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + CHECK(result1 == output1.end()); + CHECK(result2 == output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_single_element_input) + { + std::vector input = { 42 }; + std::vector output(3, 0); + + auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result == output.begin() + 1); + CHECK_EQUAL(42, output[0]); + } + + //************************************************************************* + TEST(partial_sort_copy_single_element_output) + { + std::vector input = { 5, 3, 8, 1, 9 }; + std::vector output1(1); + std::vector output2(1); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + CHECK_EQUAL(output1[0], output2[0]); + CHECK_EQUAL(1, output2[0]); + } + + //************************************************************************* + TEST(partial_sort_copy_duplicates) + { + std::vector input = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 }; + std::vector output1(6); + std::vector output2(6); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_already_sorted) + { + std::vector input = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_reverse_sorted) + { + std::vector input = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_pointer) + { + int input[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + int output1[5] = {}; + int output2[5] = {}; + + std::partial_sort_copy(std::begin(input), std::end(input), std::begin(output1), std::end(output1)); + etl::partial_sort_copy(std::begin(input), std::end(input), std::begin(output2), std::end(output2)); + + bool isEqual = std::equal(std::begin(output1), std::end(output1), std::begin(output2)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_copy_all_equal) + { + std::vector input = { 5, 5, 5, 5, 5, 5 }; + std::vector output(3); + + etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(std::is_sorted(output.begin(), output.end())); + + for (size_t i = 0; i < output.size(); ++i) + { + CHECK_EQUAL(5, output[i]); + } + } + + //************************************************************************* + TEST(partial_sort_copy_input_not_modified) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector original(input); + std::vector output(5); + + etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + // Input should not be modified + bool isEqual = std::equal(input.begin(), input.end(), original.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_copy_from_list) + { + std::list input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + //************************************************************************* TEST(find) { @@ -1199,6 +2358,216 @@ namespace CHECK_EQUAL(5U, *(data2[4])); } + //************************************************************************* + TEST(move_s_random_iterator_same_size) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + CHECK_EQUAL(1U, *(data2[0])); + CHECK_EQUAL(2U, *(data2[1])); + CHECK_EQUAL(3U, *(data2[2])); + CHECK_EQUAL(4U, *(data2[3])); + CHECK_EQUAL(5U, *(data2[4])); + } + + //************************************************************************* + TEST(move_s_random_iterator_destination_smaller) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(3); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + CHECK_EQUAL(1U, *(data2[0])); + CHECK_EQUAL(2U, *(data2[1])); + CHECK_EQUAL(3U, *(data2[2])); + } + + //************************************************************************* + TEST(move_s_random_iterator_source_smaller) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.begin() + 3 == result); + + CHECK_EQUAL(1U, *(data2[0])); + CHECK_EQUAL(2U, *(data2[1])); + CHECK_EQUAL(3U, *(data2[2])); + } + + //************************************************************************* + TEST(move_s_non_random_iterator_same_size) + { + typedef std::list> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + Data::iterator itr = data2.begin(); + CHECK_EQUAL(1U, **(itr++)); + CHECK_EQUAL(2U, **(itr++)); + CHECK_EQUAL(3U, **(itr++)); + CHECK_EQUAL(4U, **(itr++)); + CHECK_EQUAL(5U, **(itr++)); + } + + //************************************************************************* + TEST(move_s_non_random_iterator_destination_smaller) + { + typedef std::list> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(3); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + Data::iterator itr = data2.begin(); + CHECK_EQUAL(1U, **(itr++)); + CHECK_EQUAL(2U, **(itr++)); + CHECK_EQUAL(3U, **(itr++)); + } + + //************************************************************************* + TEST(move_s_non_random_iterator_source_smaller) + { + typedef std::list> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + Data::iterator expected_pos = data2.begin(); + std::advance(expected_pos, 3); + CHECK(expected_pos == result); + + Data::iterator itr = data2.begin(); + CHECK_EQUAL(1U, **(itr++)); + CHECK_EQUAL(2U, **(itr++)); + CHECK_EQUAL(3U, **(itr++)); + } + + //************************************************************************* + TEST(move_s_empty_source) + { + typedef std::vector> Data; + + Data data1; + Data data2(3); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.begin() == result); + } + + //************************************************************************* + TEST(move_s_empty_destination) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + + Data data2; + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.begin() == result); + } + + //************************************************************************* + TEST(move_s_pod_random_iterator) + { + int data1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int data2[] = { 1, 2, 3, 4, 5 }; + + int out1[10]; + int out2[5]; + + int check1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int check2[] = { 1, 2, 3, 4, 5 }; + int check3[] = { 1, 2, 3, 4, 5, 0, 0, 0, 0, 0 }; + + int* result; + + // Same size. + std::fill(std::begin(out1), std::end(out1), 0); + result = etl::move_s(std::begin(data1), std::end(data1), std::begin(out1), std::end(out1)); + CHECK_EQUAL(std::end(out1), result); + bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1)); + CHECK(is_same); + + // Destination smaller. + std::fill(std::begin(out2), std::end(out2), 0); + result = etl::move_s(std::begin(data1), std::end(data1), std::begin(out2), std::end(out2)); + CHECK_EQUAL(std::end(out2), result); + is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2)); + CHECK(is_same); + + // Source smaller. + std::fill(std::begin(out1), std::end(out1), 0); + result = etl::move_s(std::begin(data2), std::end(data2), std::begin(out1), std::end(out1)); + CHECK_EQUAL(std::begin(out1) + 5, result); + is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check3)); + CHECK(is_same); + } + //************************************************************************* TEST(rotate_pod) { @@ -1236,6 +2605,93 @@ namespace } } + //************************************************************************* + TEST(rotate_return_value) + { + // Verify that etl::rotate returns the same iterator as std::rotate + // in all cases, including the degenerate first==middle and middle==last cases. + std::vector initial_data = { 1, 2, 3, 4, 5 }; + + for (size_t i = 0UL; i <= initial_data.size(); ++i) + { + std::vector data1(initial_data); + std::vector data2(initial_data); + + auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size()); + auto etl_result = etl::rotate(data2.data(), data2.data() + i, data2.data() + data2.size()); + + // Check that the return value offset matches + ptrdiff_t std_offset = std_result - data1.data(); + ptrdiff_t etl_offset = etl_result - data2.data(); + CHECK_EQUAL(std_offset, etl_offset); + } + + // Explicitly test first == middle (empty left half): should return last + { + std::vector data = { 1, 2, 3 }; + auto result = etl::rotate(data.data(), data.data(), data.data() + data.size()); + CHECK(result == data.data() + data.size()); + } + + // Explicitly test middle == last (empty right half): should return first + { + std::vector data = { 1, 2, 3 }; + auto result = etl::rotate(data.data(), data.data() + data.size(), data.data() + data.size()); + CHECK(result == data.data()); + } + } + + //************************************************************************* + TEST(rotate_return_value_non_random_iterator) + { + // Verify that etl::rotate returns the correct iterator when called with + // non-random (bidirectional) iterators, exercising rotate_general for + // bidirectional iterators rather than the random-access overload. + std::vector initial_data = { 1, 2, 3, 4, 5 }; + + for (size_t i = 0UL; i <= initial_data.size(); ++i) + { + std::vector data1(initial_data); + std::vector data2(initial_data); + + auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size()); + + non_random_iterator nr_first(data2.data()); + non_random_iterator nr_middle(data2.data() + i); + non_random_iterator nr_last(data2.data() + data2.size()); + auto etl_result = etl::rotate(nr_first, nr_middle, nr_last); + + // Check that the data was rotated correctly + bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2)); + CHECK(isEqual); + + // Check that the return value offset matches + ptrdiff_t std_offset = std_result - data1.data(); + ptrdiff_t etl_offset = etl_result.ptr - data2.data(); + CHECK_EQUAL(std_offset, etl_offset); + } + + // Explicitly test first == middle (empty left half): should return last + { + std::vector data = { 1, 2, 3 }; + non_random_iterator nr_first(data.data()); + non_random_iterator nr_middle(data.data()); + non_random_iterator nr_last(data.data() + data.size()); + auto result = etl::rotate(nr_first, nr_middle, nr_last); + CHECK(result.ptr == data.data() + data.size()); + } + + // Explicitly test middle == last (empty right half): should return first + { + std::vector data = { 1, 2, 3 }; + non_random_iterator nr_first(data.data()); + non_random_iterator nr_middle(data.data() + data.size()); + non_random_iterator nr_last(data.data() + data.size()); + auto result = etl::rotate(nr_first, nr_middle, nr_last); + CHECK(result.ptr == data.data()); + } + } + //************************************************************************* TEST(any_of) { @@ -1318,6 +2774,29 @@ namespace CHECK(!is_permutation); } + //************************************************************************* + TEST(is_permutation_different_lengths) + { + int shorter[] = { 1, 2 }; + int longer[] = { 1, 2, 3 }; + + // Four-iterator: range2 longer than range1 (extra elements only in range2) + bool result = etl::is_permutation(std::begin(shorter), std::end(shorter), std::begin(longer), std::end(longer)); + CHECK(!result); + + // Four-iterator: range1 longer than range2 + result = etl::is_permutation(std::begin(longer), std::end(longer), std::begin(shorter), std::end(shorter)); + CHECK(!result); + + // Four-iterator with predicate: range2 longer than range1 + result = etl::is_permutation(std::begin(shorter), std::end(shorter), std::begin(longer), std::end(longer), etl::equal_to()); + CHECK(!result); + + // Four-iterator with predicate: range1 longer than range2 + result = etl::is_permutation(std::begin(longer), std::end(longer), std::begin(shorter), std::end(shorter), etl::equal_to()); + CHECK(!result); + } + //************************************************************************* TEST(is_partitioned) { @@ -2140,6 +3619,30 @@ namespace } } + //************************************************************************* + TEST(selection_sort_empty_range) + { + // Forward iterators + std::forward_list fwd_data; + etl::selection_sort(fwd_data.begin(), fwd_data.end()); + CHECK(fwd_data.empty()); + + // Bidirectional iterators + std::list bidir_data; + etl::selection_sort(bidir_data.begin(), bidir_data.end()); + CHECK(bidir_data.empty()); + + // Random access iterators + std::vector ra_data; + etl::selection_sort(ra_data.begin(), ra_data.end()); + CHECK(ra_data.empty()); + + // With comparator + std::forward_list fwd_data2; + etl::selection_sort(fwd_data2.begin(), fwd_data2.end(), std::greater()); + CHECK(fwd_data2.empty()); + } + //************************************************************************* TEST(heap_sort_default) { @@ -2284,6 +3787,275 @@ namespace CHECK(is_same); } + //************************************************************************* + TEST(unique) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(5, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_empty_range) + { + std::array data = {}; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK(end == data.end()); + } + + //************************************************************************* + TEST(unique_single_element) + { + std::array data = { 42 }; + std::array expected = { 42 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(1, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_no_duplicates) + { + std::array data = { 1, 2, 3, 4, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(5, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_all_same) + { + std::array data = { 7, 7, 7, 7, 7 }; + std::array expected = { 7 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(1, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_with_predicate) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + + auto end = etl::unique(data.begin(), data.end(), std::equal_to()); + + CHECK_EQUAL(5, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_with_predicate_custom) + { + // Group elements that are close to each other (differ by less than 3) + std::array data = { 1, 2, 3, 7, 8, 9, 20, 21 }; + std::array expected = { 1, 7, 20 }; + + auto end = etl::unique(data.begin(), data.end(), [](int a, int b) { return (b - a) < 3; }); + + CHECK_EQUAL(3, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_matches_std) + { + std::array data1 = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array data2 = data1; + + auto std_end = std::unique(data1.begin(), data1.end()); + auto etl_end = etl::unique(data2.begin(), data2.end()); + + size_t std_size = std::distance(data1.begin(), std_end); + size_t etl_size = std::distance(data2.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(data1.begin(), std_end, data2.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_with_predicate_matches_std) + { + std::array data1 = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array data2 = data1; + + auto std_end = std::unique(data1.begin(), data1.end(), std::equal_to()); + auto etl_end = etl::unique(data2.begin(), data2.end(), std::equal_to()); + + size_t std_size = std::distance(data1.begin(), std_end); + size_t etl_size = std::distance(data2.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(data1.begin(), std_end, data2.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(5, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_empty_range) + { + std::array data = {}; + std::array result = { 99 }; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK(end == result.begin()); + CHECK_EQUAL(99, result[0]); // output unchanged + } + + //************************************************************************* + TEST(unique_copy_single_element) + { + std::array data = { 42 }; + std::array expected = { 42 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(1, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_no_duplicates) + { + std::array data = { 1, 2, 3, 4, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(5, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_all_same) + { + std::array data = { 7, 7, 7, 7, 7 }; + std::array expected = { 7 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(1, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_source_unchanged) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array original = data; + std::array result = {}; + + etl::unique_copy(data.begin(), data.end(), result.begin()); + + bool is_same = std::equal(original.begin(), original.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_with_predicate) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin(), std::equal_to()); + + CHECK_EQUAL(5, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_with_predicate_custom) + { + // Group elements that are close to each other (differ by less than 3) + std::array data = { 1, 2, 3, 7, 8, 9, 20, 21 }; + std::array expected = { 1, 7, 20 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin(), [](int a, int b) { return (b - a) < 3; }); + + CHECK_EQUAL(3, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_matches_std) + { + std::array data = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array std_result = {}; + std::array etl_result = {}; + + auto std_end = std::unique_copy(data.begin(), data.end(), std_result.begin()); + auto etl_end = etl::unique_copy(data.begin(), data.end(), etl_result.begin()); + + size_t std_size = std::distance(std_result.begin(), std_end); + size_t etl_size = std::distance(etl_result.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(std_result.begin(), std_end, etl_result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_with_predicate_matches_std) + { + std::array data = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array std_result = {}; + std::array etl_result = {}; + + auto std_end = std::unique_copy(data.begin(), data.end(), std_result.begin(), std::equal_to()); + auto etl_end = etl::unique_copy(data.begin(), data.end(), etl_result.begin(), std::equal_to()); + + size_t std_size = std::distance(std_result.begin(), std_end); + size_t etl_size = std::distance(etl_result.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(std_result.begin(), std_end, etl_result.begin()); + CHECK(is_same); + } + //************************************************************************* struct generator { @@ -2469,6 +4241,116 @@ namespace } } + //************************************************************************* + TEST(accumulate_default) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 0); + int result = etl::accumulate(std::begin(data), std::end(data), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_with_initial_value) + { + int data[] = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 100); + int result = etl::accumulate(std::begin(data), std::end(data), 100); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_custom_operation) + { + int data[] = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 1, std::multiplies()); + int result = etl::accumulate(std::begin(data), std::end(data), 1, std::multiplies()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_empty_range) + { + int data[] = { 1 }; + + int expected = std::accumulate(std::begin(data), std::begin(data), 42); + int result = etl::accumulate(std::begin(data), std::begin(data), 42); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_single_element) + { + int data[] = { 7 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 0); + int result = etl::accumulate(std::begin(data), std::end(data), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_negative_values) + { + int data[] = { -3, -2, -1, 0, 1, 2, 3 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 0); + int result = etl::accumulate(std::begin(data), std::end(data), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_custom_operation_subtraction) + { + int data[] = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 100, std::minus()); + int result = etl::accumulate(std::begin(data), std::end(data), 100, std::minus()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_non_random_iterator) + { + List data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + int expected = std::accumulate(data.begin(), data.end(), 0); + int result = etl::accumulate(data.begin(), data.end(), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_non_random_iterator_custom_operation) + { + List data = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(data.begin(), data.end(), 1, std::multiplies()); + int result = etl::accumulate(data.begin(), data.end(), 1, std::multiplies()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_double) + { + double data[] = { 1.5, 2.5, 3.5, 4.5, 5.5 }; + + double expected = std::accumulate(std::begin(data), std::end(data), 0.0); + double result = etl::accumulate(std::begin(data), std::end(data), 0.0); + + CHECK_CLOSE(expected, result, 1e-10); + } + //************************************************************************* TEST(clamp_run_time) { @@ -2504,5 +4386,432 @@ namespace CHECK_EQUAL(0, result5); CHECK_EQUAL(10, result6); } + + //************************************************************************* + TEST(merge_default_comparator) + { + int input1[] = { 1, 3, 5, 7, 9 }; + int input2[] = { 2, 4, 6, 8, 10 }; + int output[10]; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 10); + } + + //************************************************************************* + TEST(merge_custom_comparator) + { + int input1[] = { 9, 7, 5, 3, 1 }; + int input2[] = { 10, 8, 6, 4, 2 }; + int output[10]; + int expected[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output), + Greater()); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 10); + } + + //************************************************************************* + TEST(merge_first_range_empty) + { + int input1[] = { 0 }; // dummy, won't be used + int input2[] = { 1, 2, 3 }; + int output[3]; + int expected[] = { 1, 2, 3 }; + + int* result = etl::merge(input1, input1, // empty range + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 3); + } + + //************************************************************************* + TEST(merge_second_range_empty) + { + int input1[] = { 1, 2, 3 }; + int input2[] = { 0 }; // dummy, won't be used + int output[3]; + int expected[] = { 1, 2, 3 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + input2, input2, // empty range + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 3); + } + + //************************************************************************* + TEST(merge_both_ranges_empty) + { + int input1[] = { 0 }; + int output[] = { 99 }; + + int* result = etl::merge(input1, input1, + input1, input1, + std::begin(output)); + + CHECK_EQUAL(std::begin(output), result); + CHECK_EQUAL(99, output[0]); // output should be unchanged + } + + //************************************************************************* + TEST(merge_with_duplicates) + { + int input1[] = { 1, 3, 3, 5 }; + int input2[] = { 2, 3, 4, 5 }; + int output[8]; + int expected[] = { 1, 2, 3, 3, 3, 4, 5, 5 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 8); + } + + //************************************************************************* + TEST(merge_different_sizes) + { + int input1[] = { 1, 5 }; + int input2[] = { 2, 3, 4, 6, 7, 8 }; + int output[8]; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 8); + } + + //************************************************************************* + TEST(merge_single_elements) + { + int input1[] = { 1 }; + int input2[] = { 2 }; + int output[2]; + int expected[] = { 1, 2 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 2); + } + + //************************************************************************* + TEST(merge_with_list_iterators) + { + std::list input1 = { 1, 3, 5, 7 }; + std::list input2 = { 2, 4, 6, 8 }; + std::vector output(8); + std::vector expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + std::vector::iterator result = etl::merge(input1.begin(), input1.end(), + input2.begin(), input2.end(), + output.begin()); + + CHECK(output.end() == result); + CHECK_ARRAY_EQUAL(expected.data(), output.data(), 8); + } + + //************************************************************************* + TEST(merge_matches_std) + { + int input1[] = { 1, 4, 7, 8, 10 }; + int input2[] = { 2, 3, 5, 6, 9 }; + int etl_output[10]; + int std_output[10]; + + etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(etl_output)); + + std::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(std_output)); + + CHECK_ARRAY_EQUAL(std_output, etl_output, 10); + } + + //************************************************************************* + TEST(merge_matches_std_with_comparator) + { + int input1[] = { 10, 8, 7, 4, 1 }; + int input2[] = { 9, 6, 5, 3, 2 }; + int etl_output[10]; + int std_output[10]; + + etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(etl_output), + Greater()); + + std::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(std_output), + Greater()); + + CHECK_ARRAY_EQUAL(std_output, etl_output, 10); + } + + //************************************************************************* + TEST(merge_stability) + { + // Test that merge is stable: equivalent elements from the first range + // come before those from the second range. + Data input1[] = { Data(1, 1), Data(3, 1), Data(5, 1) }; + Data input2[] = { Data(1, 2), Data(3, 2), Data(5, 2) }; + Data output[6]; + + etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output), + DataPredicate()); + + // Elements from input1 (b==1) should come before elements from input2 (b==2) + // for equivalent keys. + CHECK_EQUAL(1, output[0].a); CHECK_EQUAL(1, output[0].b); // from input1 + CHECK_EQUAL(1, output[1].a); CHECK_EQUAL(2, output[1].b); // from input2 + CHECK_EQUAL(3, output[2].a); CHECK_EQUAL(1, output[2].b); // from input1 + CHECK_EQUAL(3, output[3].a); CHECK_EQUAL(2, output[3].b); // from input2 + CHECK_EQUAL(5, output[4].a); CHECK_EQUAL(1, output[4].b); // from input1 + CHECK_EQUAL(5, output[5].a); CHECK_EQUAL(2, output[5].b); // from input2 + } + + //************************************************************************* + TEST(inplace_merge_default_comparator) + { + int data[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_custom_comparator) + { + int data[] = { 9, 7, 5, 3, 1, 10, 8, 6, 4, 2 }; + int expected[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data), Greater()); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_first_range_empty) + { + int data[] = { 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data), std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 5); + } + + //************************************************************************* + TEST(inplace_merge_second_range_empty) + { + int data[] = { 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::end(data), std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 5); + } + + //************************************************************************* + TEST(inplace_merge_both_ranges_empty) + { + int data[] = { 99 }; + + etl::inplace_merge(data, data, data); // empty range + + CHECK_EQUAL(99, data[0]); // unchanged + } + + //************************************************************************* + TEST(inplace_merge_with_duplicates) + { + int data[] = { 1, 3, 3, 5, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 3, 3, 4, 5, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 4, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 8); + } + + //************************************************************************* + TEST(inplace_merge_different_sizes) + { + int data[] = { 1, 5, 2, 3, 4, 6, 7, 8 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 2, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 8); + } + + //************************************************************************* + TEST(inplace_merge_single_elements) + { + int data[] = { 2, 1 }; + int expected[] = { 1, 2 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 2); + } + + //************************************************************************* + TEST(inplace_merge_single_element_halves_already_sorted) + { + int data[] = { 1, 2 }; + int expected[] = { 1, 2 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 2); + } + + //************************************************************************* + TEST(inplace_merge_already_sorted) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_reverse_halves) + { + // Second half all less than first half + int data[] = { 6, 7, 8, 9, 10, 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_with_list_iterators) + { + std::vector data = { 1, 3, 5, 7, 2, 4, 6, 8 }; + std::vector expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + etl::inplace_merge(data.begin(), data.begin() + 4, data.end()); + + CHECK(expected == data); + } + + //************************************************************************* + TEST(inplace_merge_matches_std) + { + int etl_data[] = { 1, 4, 7, 8, 10, 2, 3, 5, 6, 9 }; + int std_data[] = { 1, 4, 7, 8, 10, 2, 3, 5, 6, 9 }; + + etl::inplace_merge(std::begin(etl_data), std::begin(etl_data) + 5, std::end(etl_data)); + std::inplace_merge(std::begin(std_data), std::begin(std_data) + 5, std::end(std_data)); + + CHECK_ARRAY_EQUAL(std_data, etl_data, 10); + } + + //************************************************************************* + TEST(inplace_merge_matches_std_with_comparator) + { + int etl_data[] = { 10, 8, 7, 4, 1, 9, 6, 5, 3, 2 }; + int std_data[] = { 10, 8, 7, 4, 1, 9, 6, 5, 3, 2 }; + + etl::inplace_merge(std::begin(etl_data), std::begin(etl_data) + 5, std::end(etl_data), Greater()); + std::inplace_merge(std::begin(std_data), std::begin(std_data) + 5, std::end(std_data), Greater()); + + CHECK_ARRAY_EQUAL(std_data, etl_data, 10); + } + + //************************************************************************* + TEST(inplace_merge_stability) + { + // Test that inplace_merge is stable: equivalent elements from the first + // range come before those from the second range. + Data data[] = { Data(1, 1), Data(3, 1), Data(5, 1), + Data(1, 2), Data(3, 2), Data(5, 2) }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 3, std::end(data), DataPredicate()); + + // Elements from first half (b==1) should come before elements from second half (b==2) + // for equivalent keys. + CHECK_EQUAL(1, data[0].a); CHECK_EQUAL(1, data[0].b); // from first half + CHECK_EQUAL(1, data[1].a); CHECK_EQUAL(2, data[1].b); // from second half + CHECK_EQUAL(3, data[2].a); CHECK_EQUAL(1, data[2].b); // from first half + CHECK_EQUAL(3, data[3].a); CHECK_EQUAL(2, data[3].b); // from second half + CHECK_EQUAL(5, data[4].a); CHECK_EQUAL(1, data[4].b); // from first half + CHECK_EQUAL(5, data[5].a); CHECK_EQUAL(2, data[5].b); // from second half + } + + //************************************************************************* + TEST(inplace_merge_single_element_first_half) + { + int data[] = { 5, 1, 2, 3, 4 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 5); + } + + //************************************************************************* + TEST(inplace_merge_single_element_second_half) + { + int data[] = { 1, 2, 3, 4, 5, 3 }; + int expected[] = { 1, 2, 3, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 6); + } + + //************************************************************************* + TEST(inplace_merge_all_equal) + { + int data[] = { 5, 5, 5, 5, 5, 5 }; + int expected[] = { 5, 5, 5, 5, 5, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 3, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 6); + } + + //************************************************************************* + TEST(inplace_merge_interleaved) + { + int data[] = { 1, 3, 5, 7, 9, 11, 2, 4, 6, 8, 10, 12 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 6, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 12); + } } } diff --git a/test/test_container.cpp b/test/test_container.cpp index ece1afba..3583be25 100644 --- a/test/test_container.cpp +++ b/test/test_container.cpp @@ -31,6 +31,7 @@ SOFTWARE. #include "etl/container.h" #include +#include #if ETL_NOT_USING_STL @@ -145,6 +146,34 @@ namespace size_t compiletime_size = sizeof(etl::array_size(data)); CHECK_EQUAL(SIZE, compiletime_size); } + + //************************************************************************* + TEST(test_stl_style_container_data) + { + const size_t SIZE = 10UL; + std::vector data(SIZE); + const std::vector cdata(SIZE); + + int* pdata = ETL_OR_STD17::data(data); + const int* pcdata = ETL_OR_STD17::data(cdata); + + CHECK(data.data() == pdata); + CHECK(cdata.data() == pcdata); + } + + //************************************************************************* + TEST(test_c_array_data) + { + const size_t SIZE = 10UL; + int data[SIZE]; + const int cdata[SIZE] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* pdata = ETL_OR_STD17::data(data); + const int* pcdata = ETL_OR_STD17::data(cdata); + + CHECK(&data[0] == pdata); + CHECK(&cdata[0] == pcdata); + } } } diff --git a/test/test_etl_assert.cpp b/test/test_etl_assert.cpp index e927a0b2..26fcced4 100644 --- a/test/test_etl_assert.cpp +++ b/test/test_etl_assert.cpp @@ -34,6 +34,8 @@ SOFTWARE. #include "etl/error_handler.h" #include "etl/exception.h" +#include + namespace { class TestException1 : public etl::exception diff --git a/test/test_format.cpp b/test/test_format.cpp index a6c25eab..3d3338fa 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -197,6 +197,7 @@ namespace CHECK_EQUAL("-6759414", test_format(s, "{}", static_cast(-6759414))); } +#if ETL_USING_FORMAT_FLOATING_POINT //************************************************************************* TEST(test_format_float) { @@ -266,6 +267,7 @@ namespace CHECK_EQUAL("0x1.92a738p-5", test_format(s, "{:a}", 0.0000015f)); CHECK_EQUAL("0x1.6345785d8ap+e", test_format(s, "{:a}", 100000000000000000.l)); } +#endif //************************************************************************* TEST(test_format_char_array) diff --git a/test/test_fsm.cpp b/test/test_fsm.cpp index c6a34e8e..61030b1b 100644 --- a/test/test_fsm.cpp +++ b/test/test_fsm.cpp @@ -515,7 +515,7 @@ namespace //************************************************************************* TEST(test_fsm_emergency_stop) { - motorControl.Initialise(stateList, ETL_OR_STD17::size(stateList)); + motorControl.Initialise(stateList, ETL_OR_STD17::size(stateList)); motorControl.reset(); motorControl.ClearStatistics(); @@ -758,7 +758,7 @@ namespace CHECK_EQUAL(StateId::Running, int(motorControl.get_state().get_state_id())); auto id2 = motorControl.transition_to(StateId::Idle); - + // Now in Locked state. CHECK_EQUAL(StateId::Locked, int(id2)); CHECK_EQUAL(StateId::Locked, int(motorControl.get_state_id())); diff --git a/test/test_functional.cpp b/test/test_functional.cpp index 1204d330..5bbe6beb 100644 --- a/test/test_functional.cpp +++ b/test/test_functional.cpp @@ -85,6 +85,24 @@ namespace mutable std::string result; }; +#if ETL_USING_CPP11 + // Lightweight type used to verify transparent heterogeneous comparison. + // Only operator<(int, Wrapper) is defined; operator<(Wrapper, int) is + // intentionally absent. less_equal is implemented as + // !(rhs < lhs), so less_equal{}(Wrapper, int) needs + // operator<(int, Wrapper) which IS provided. + struct Wrapper + { + int value; + constexpr explicit Wrapper(int v) : value(v) {} + }; + + // int < Wrapper -- defined + constexpr bool operator<(int lhs, const Wrapper& rhs) { return lhs < rhs.value; } + + // Wrapper < int -- intentionally NOT defined +#endif + SUITE(test_functional) { //************************************************************************* @@ -101,6 +119,32 @@ namespace CHECK((compare>(1, 2))); CHECK(!(compare>(2, 1))); CHECK((compare>(1, 1))); + +#if ETL_USING_CPP11 + CHECK((compare>(1, 2))); + CHECK(!(compare>(2, 1))); + CHECK((compare>(1, 1))); +#endif + } + + //************************************************************************* + TEST(test_less_equal_void_heterogeneous) + { +#if ETL_USING_CPP11 + // less_equal{}(lhs, rhs) is !(rhs < lhs). + // With only operator<(int, Wrapper) defined, we can call + // less_equal{}(Wrapper, int) because the implementation + // evaluates !(int < Wrapper). + + // Wrapper(1) <= 2 → !(2 < Wrapper(1)) → !(2 < 1) → !false → true + CHECK((etl::less_equal{}(Wrapper(1), 2))); + + // Wrapper(2) <= 1 → !(1 < Wrapper(2)) → !(1 < 2) → !true → false + CHECK(!(etl::less_equal{}(Wrapper(2), 1))); + + // Wrapper(3) <= 3 → !(3 < Wrapper(3)) → !(3 < 3) → !false → true + CHECK((etl::less_equal{}(Wrapper(3), 3))); +#endif } //************************************************************************* @@ -117,6 +161,32 @@ namespace CHECK(!(compare>(1, 2))); CHECK((compare>(2, 1))); CHECK((compare>(1, 1))); + +#if ETL_USING_CPP11 + CHECK(!(compare>(1, 2))); + CHECK((compare>(2, 1))); + CHECK((compare>(1, 1))); +#endif + } + + //************************************************************************* + TEST(test_greater_equal_void_heterogeneous) + { +#if ETL_USING_CPP11 + // greater_equal{}(lhs, rhs) is !(lhs < rhs). + // With only operator<(int, Wrapper) defined, we can call + // greater_equal{}(int, Wrapper) because the implementation + // evaluates !(int < Wrapper). + + // 2 >= Wrapper(1) → !(2 < Wrapper(1)) → !(2 < 1) → !false → true + CHECK((etl::greater_equal{}(2, Wrapper(1)))); + + // 1 >= Wrapper(2) → !(1 < Wrapper(2)) → !(1 < 2) → !true → false + CHECK(!(etl::greater_equal{}(1, Wrapper(2)))); + + // 3 >= Wrapper(3) → !(3 < Wrapper(3)) → !(3 < 3) → !false → true + CHECK((etl::greater_equal{}(3, Wrapper(3)))); +#endif } //************************************************************************* diff --git a/test/test_math_functions.cpp b/test/test_math_functions.cpp index 0b7c9205..f5ef7842 100644 --- a/test/test_math_functions.cpp +++ b/test/test_math_functions.cpp @@ -574,12 +574,11 @@ namespace constexpr uint16_t absolute1 = etl::absolute(int16_t(0)); constexpr uint16_t absolute2 = etl::absolute(int16_t(32767)); constexpr uint16_t absolute3 = etl::absolute(int16_t(-32767)); - constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); + //constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); // Compile error CHECK_EQUAL(uint16_t(0), absolute1); CHECK_EQUAL(uint16_t(32767), absolute2); CHECK_EQUAL(uint16_t(32767), absolute3); - CHECK_EQUAL(uint16_t(32768), absolute4); constexpr uint16_t absolute5 = etl::absolute(uint16_t(0)); constexpr uint16_t absolute6 = etl::absolute(uint16_t(65535)); diff --git a/test/test_span_dynamic_extent.cpp b/test/test_span_dynamic_extent.cpp index 586eadcb..832b4b22 100644 --- a/test/test_span_dynamic_extent.cpp +++ b/test/test_span_dynamic_extent.cpp @@ -1627,5 +1627,23 @@ namespace } #include "etl/private/diagnostic_pop.h" + + //************************************************************************* + TEST(test_not_constructible_from_rvalue_container) + { +#if ETL_USING_CPP17 + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); + + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); +#else + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); + + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); +#endif + } } } diff --git a/test/test_span_fixed_extent.cpp b/test/test_span_fixed_extent.cpp index a1825c2c..a79ebb70 100644 --- a/test/test_span_fixed_extent.cpp +++ b/test/test_span_fixed_extent.cpp @@ -496,13 +496,19 @@ namespace //************************************************************************* TEST(test_empty) { - View view1(etldata.begin(), etldata.begin()); - CHECK(!view1.empty()); - EView view2(etldata.begin(), etldata.begin()); CHECK(view2.empty()); } + //************************************************************************* + TEST(test_construction_from_mismatched_size) + { + CHECK_THROW((View(etldata.begin(), etldata.begin())), etl::span_size_mismatch); + CHECK_THROW((View(etldata.begin(), 1)), etl::span_size_mismatch); + CHECK_THROW((View(etldata.begin(), etldata.size() - 1)), etl::span_size_mismatch); + CHECK_THROW((View(etldata.begin(), etldata.size() + 1)), etl::span_size_mismatch); + } + //************************************************************************* TEST(test_size) { @@ -664,7 +670,7 @@ namespace CHECK_EQUAL(sub1.size(), cspan1.extent); CHECK_EQUAL(sub1.size(), cspan1.size()); - auto span2 = view.subspan<2>(); + auto span2 = view.subspan<2>(); isEqual = std::equal(sub2.begin(), sub2.end(), span2.begin()); CHECK(isEqual); CHECK_EQUAL(span2.size(), span2.extent); @@ -804,7 +810,7 @@ namespace //************************************************************************* #include "etl/private/diagnostic_unused_function_push.h" - + struct C_issue_482 {}; void f_issue_482(etl::span) @@ -1220,7 +1226,7 @@ namespace etl::span span2(span1); //etl::span span3(span1); // This line should fail to compile. } - + //************************************************************************* TEST(test_reinterpret_as) { @@ -1306,5 +1312,23 @@ namespace } #include "etl/private/diagnostic_pop.h" + + //************************************************************************* + TEST(test_not_constructible_from_rvalue_container) + { +#if ETL_USING_CPP17 + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); + + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); +#else + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); + + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); +#endif + } } } diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 6396b761..2b069673 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3552,6 +3552,7 @@ + @@ -10362,6 +10363,7 @@ + diff --git a/zephyr/module.yml b/zephyr/module.yml index b292d6a1..be27ae79 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -2,3 +2,6 @@ name: etl build: cmake: zephyr kconfig: zephyr/Kconfig +security: + external-references: + - pkg:github/ETLCPP/etl@20.46.2