diff --git a/CMakeLists.txt b/CMakeLists.txt index a3027529..569c1c11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,3 +144,28 @@ export( FILE "${CMAKE_CURRENT_BINARY_DIR}/${POCLIB}Targets.cmake") +# Configuration summary --------------------------------------------------------------------------- + +include(config_summary) + +config_summary_header("Ports-of-Call" "ports-of-call" 30) # 30 characters seems wide enough + +config_summary_block("CMake Options") +config_summary_variable("CMAKE_BUILD_TYPE") +config_summary_variable("CMAKE_C_COMPILER") # Does PoC use C or only C++? +config_summary_variable("CMAKE_C_COMPILER_VERSION") +config_summary_variable("CMAKE_C_FLAGS") +config_summary_variable("CMAKE_CXX_COMPILER") +config_summary_variable("CMAKE_CXX_COMPILER_VERSION") +config_summary_variable("CMAKE_CXX_FLAGS") + +config_summary_block("Dependencies") +config_summary_dependency("Kokkos" "Kokkos") # Should this always be listed? + +config_summary_block("User Options") # Are these the right user options? +config_summary_option("PORTS_OF_CALL_BUILD_TESTING") +config_summary_option("PORTABILITY_STRATEGY_CUDA") +config_summary_option("PORTABILITY_STRATEGY_KOKKOS") +config_summary_option("PORTABILITY_STRATEGY_NONE") + +config_summary_print() diff --git a/cmake/config_summary.cmake b/cmake/config_summary.cmake new file mode 100644 index 00000000..1a9e888b --- /dev/null +++ b/cmake/config_summary.cmake @@ -0,0 +1,152 @@ +# ------------------------------------------------------------------------------------------------- +# Set some colors + +string(ASCII 27 Esc) +set(color_reset "${Esc}[m") +set(color_boldblue "${Esc}[1;34m") +set(color_boldcyan "${Esc}[1;36m") +set(color_boldgreen "${Esc}[1;32m") +set(color_boldgrey "${Esc}[1;30m") +set(color_boldmagenta "${Esc}[1;35m") +set(color_boldplain "${Esc}[1m") +set(color_boldred "${Esc}[1;31m") +set(color_boldyellow "${Esc}[1;33m") +set(color_cyan "${Esc}[36m") +set(color_yellow "${Esc}[33m") + +# ------------------------------------------------------------------------------------------------- +# A macro for internal use -- see below for the user macros + +# Print out a key-value pair (colored differently than a user option) +macro(config_summary_kvc key value color) + # Figure out spacing + string(LENGTH ${key} _slen) + math(EXPR _tlen "${_width} - ${_slen}") + string(REPEAT " " ${_tlen} _tail) + # Key + string(APPEND _summary + "${color_boldplain}" + " ${key}${_tail} : " + "${color_reset}" + ) + # Value + string(APPEND _summary + "${color}" + "${value}" + "${color_reset}" + ) + string(APPEND _summary "\n") +endmacro() + +# ------------------------------------------------------------------------------------------------- +# Macros to write the configuration summary. +# -- config_summary_header should always be called before all other config_summary_* +# -- config_summary_print should always be called after all other config_summary_* + +# Print a header and do some setup +# -- Arguments: +# 0) display_name: The name of your project for printouts +# 1) cmake_name: The name for your project as known to CMake (to find variables) +# 2) optional: number of characters for variable names (must be large enough for the longest +# variable name, defaults to 48) +macro(config_summary_header display_name cmake_name) + # save the terminal width (`tput cols` was not portable, so hard-code) + set(_termwidth 80) + # pretty-print a header bar + math(EXPR _bwid "${_termwidth} - 1") + string(REPEAT "-" ${_bwid} _bar) + set(_bar "${color_boldcyan}${_bar}${color_reset}") + string(APPEND _summary "${_bar}\n") + # header message + string(APPEND _summary + "${color_boldcyan}" + "${display_name} configuration summary (version ${${cmake_name}_VERSION})\n" + "${color_reset}" + ) + # pretty-print a header bar + string(APPEND _summary "${_bar}\n") + # Set the width for variable names + if (${ARGC} GREATER 2) + set(_width "${ARGV2}") + else() + set(_width 48) #default length if none provided + endif() +endmacro() + +# Start a block within the configuration summary +# -- Arguments: +# 0) The title of the block +macro(config_summary_block title) + string(APPEND _summary + "${color_boldcyan}" + "${title}" + "${color_reset}" + "\n" + ) +endmacro() + +# Print out a key-value pair for information +# -- Arguments: +# 0) the key of the key-value pair +# 1) the value of the key-value pair +macro(config_summary_keyval key value) + config_summary_kvc("${key}" "${value}" "${color_yellow}") +endmacro() + +# Shorthand to print out the name and value of an internal variable +# -- Arguments: +# 0) The name of the variable +macro(config_summary_variable varname) + config_summary_keyval("${varname}" "${${varname}}") +endmacro() + +# Print out a dependency: not found or version +# -- Arguments: +# 0) The display name of the dependency (for printing) +# 1) The CMake name of the dependency (for finding variables) +# 2) optional: condition for whether or not to print this dependency (e.g., if a dependency is +# only conditionally included, you may only want to conditionally print out whether or not +# the dependency was found) +macro(config_summary_dependency display_name cmake_name) + if (${ARGC} GREATER 2) + set(_condition "${ARGV2}") + else() + set(_condition ON) # always print if no condition provided + endif() + if (${_condition}) + if (NOT DEFINED ${cmake_name}_VERSION) + set(_color "${color_boldred}") + set(_version "not found") + else() + set(_color "${color_boldgreen}") + set(_version "${${cmake_name}_VERSION}") + endif() + config_summary_kvc("${display_name}" "${_version}" "${_color}") + endif() +endmacro() + +# Print out the setting used for a user-settable variable. This differs from +# config_summary_variable mostly in how the output is colored in order to highlight what the user +# turned on or off at a quick glance. +# -- Arguments: +# 0) The name of the variable +macro(config_summary_option varname) + set(value "${${varname}}") + if (${value}) + set(_color "${color_boldgreen}") + else() + set(_color "${color_boldgrey}") + endif() + config_summary_kvc("${varname}" "${value}" "${_color}") +endmacro() + +# Finalize the configuration summary and do the actual printing to the terminal +macro(config_summary_print) + # pretty-print a footer bar to match the header + string(APPEND _summary "${_bar}") + # actually print the summary, along with the footer bar + message(NOTICE + "${_summary}" + "${_footerbar}" + ) +endmacro() diff --git a/doc/sphinx/src/using.rst b/doc/sphinx/src/using.rst index 60d96603..5abf037b 100644 --- a/doc/sphinx/src/using.rst +++ b/doc/sphinx/src/using.rst @@ -230,6 +230,7 @@ not yet ``constexpr``, so even with the "relaxed ``constexpr``" compilation mode not feature-complete on GPUs. This will change when those member functions become ``constexpr`` in C++20. + span.hpp @@ -251,3 +252,14 @@ more information, see `C++ reference page +#include +#include +#include + +namespace PortsOfCall { + +/* The static_vector has an interface modeled after std::vector, but it uses + * statically-allocated memory to (a) avoid dynamic memory allocation, deallocation, or + * reallocation; and (b) ensure that the data is contained entirely inside the + * static_vector so that this type can be memcopied to a CPU. We had an initial + * implementation that provided additional features, but was not portable to GPUs. This + * implementation is simpler and less feature-rich, but works on GPUs. Tests have been + * added to the test suite to ensure that this is true and to enforce that it continues to + * be true. Features that are missing: + * -- The insert, emplace, and erase methods from std::vector were included in the initial + * implementation, but have not been included in this implementation. These methods + * could likely be added to this implementation if needed. + * -- The initial implementation could handle a size of N = 0, while this implementation + * will fail to compile if the size is zero. Support for zero-size static_vector could + * likely be added to this implementation if needed. + * -- The initial implementation propagated triviality. If the type held by the + * static_vector (Element) is trivially-destructible, then the initial implementation of + * static_vector would also be trivially-destructible. We have not yet been able to + * reproduce this behavior in a GPU-compatible way, so the new implementation of + * static_vector is never trivially-destructible. + * -- The initial implementation could adapt to holding types that are not copyable and/or + * not movable. We have not yet been able to reproduce this behavior in a GPU-compatible + * way, so the new implementation assumes that objects of type Element are both copyable + * and movable. + * -- The initial implementation was able to perform some optimizations on triviality of + * the Element type. We have not yet been able to reproduce this behavior in a + * GPU-compatible way, so the new implementation may not perform as fast in some cases. + */ + +template +class static_vector { + public: + // iterator stolen from CJ's static_vector + // ---------------------------------------------------- + template + class iterator_type { + friend class static_vector; + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = Element_; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + + using const_iterator_type = iterator_type; + + PORTABLE_FUNCTION constexpr iterator_type() : it_{nullptr} {} + + PORTABLE_FUNCTION constexpr iterator_type(Element_ *it) : it_{it} {} + + // const iterator from non-const iterator + template ::value> * = nullptr> + PORTABLE_FUNCTION constexpr iterator_type(iterator_type> it) + : it_{it.it_} {} + + private: + Element_ *it_; + + // non-const iterator from const iterator...generally dangerous + // (used for convinience in static_vector implementation) + template ::value> * = nullptr> + PORTABLE_FUNCTION constexpr iterator_type(iterator_type> it) + : it_{const_cast(it.it_)} {} + + public: + PORTABLE_FUNCTION constexpr iterator_type &operator++() { + ++(this->it_); + return *this; + } + + PORTABLE_FUNCTION constexpr iterator_type operator++(int) const { + iterator_type temp{*this}; + ++temp; + return temp; + } + + PORTABLE_FUNCTION constexpr iterator_type &operator--() { + --(this->it_); + return *this; + } + + PORTABLE_FUNCTION constexpr iterator_type operator--(int) const { + iterator_type temp{*this}; + --temp; + return temp; + } + + PORTABLE_FUNCTION constexpr iterator_type &operator+=(difference_type m) { + this->it_ += m; + return *this; + } + + PORTABLE_FUNCTION constexpr iterator_type &operator-=(difference_type m) { + return *this += -m; + } + + PORTABLE_FUNCTION constexpr iterator_type operator+(difference_type m) const { + iterator_type temp{*this}; + return temp += m; + } + + PORTABLE_FUNCTION constexpr iterator_type operator-(difference_type m) const { + iterator_type temp{*this}; + return temp -= m; + } + + PORTABLE_FUNCTION constexpr difference_type + operator-(const_iterator_type other) const { + return this->it_ - other.it_; + } + + PORTABLE_FUNCTION constexpr bool operator==(const_iterator_type other) const { + return this->it_ == other.it_; + } + + PORTABLE_FUNCTION constexpr bool operator!=(const_iterator_type other) const { + return not(this->it_ == other.it_); + } + + PORTABLE_FUNCTION constexpr bool operator<(const_iterator_type other) const { + return this->it_ < other.it_; + } + + PORTABLE_FUNCTION constexpr bool operator>(const_iterator_type other) const { + return other < *this; + } + + PORTABLE_FUNCTION constexpr bool operator>=(const_iterator_type other) const { + return not(*this < other); + } + + PORTABLE_FUNCTION constexpr bool operator<=(const_iterator_type other) const { + return not(*this > other); + } + + PORTABLE_FUNCTION constexpr reference operator*() const { return *(this->it_); } + + PORTABLE_FUNCTION constexpr pointer operator->() const { return this->it_; } + + PORTABLE_FUNCTION constexpr reference operator[](difference_type n) const { + return *(this->it_ + n); + } + + template ::value> * = nullptr> + PORTABLE_FUNCTION friend constexpr void advance(iterator_type &it, IntT n) { + it += n; + } + + PORTABLE_FUNCTION friend constexpr iterator_type next(iterator_type it, + difference_type n) { + advance(it, n); + return it; + } + + PORTABLE_FUNCTION friend constexpr iterator_type prev(iterator_type it, + difference_type n) { + advance(it, -n); + return it; + } + + PORTABLE_FUNCTION friend constexpr iterator_type::difference_type + distance(iterator_type first, iterator_type last) { + return last - first; + } + }; + // end of iterator + // ---------------------------------------------------------------------------- + + public: + using value_type = Element; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = Element &; + using const_reference = Element const &; + using pointer = Element *; + using const_pointer = Element const *; + using iterator = iterator_type; + using const_iterator = iterator_type; + + private: + size_type count_ = 0; + using byte = unsigned char; + byte storage_[sizeof(Element) * N]; + PORTABLE_FUNCTION constexpr pointer ptr() { + return reinterpret_cast(storage_); + } + PORTABLE_FUNCTION constexpr pointer ptr(size_type const i) { return ptr() + i; } + PORTABLE_FUNCTION constexpr reference ref() { return *ptr(); } + PORTABLE_FUNCTION constexpr reference ref(size_type const i) { return *ptr(i); } + PORTABLE_FUNCTION constexpr const_pointer ptr() const { + return reinterpret_cast(storage_); + } + PORTABLE_FUNCTION constexpr const_pointer ptr(size_type const i) const { + return ptr() + i; + } + PORTABLE_FUNCTION constexpr const_reference ref() const { return *ptr(); } + PORTABLE_FUNCTION constexpr const_reference ref(size_type const i) const { + return *ptr(i); + } + + public: + // constructor + // -------------------------------------------------------------------------------- + // PORTABLE_FUNCTION constexpr static_vector() = default; + PORTABLE_FUNCTION constexpr static_vector() {} + + PORTABLE_FUNCTION constexpr static_vector(std::initializer_list const &list) { + assert(list.size() <= N); + for (auto &element : list) { + push_back(element); + } + } + + PORTABLE_FUNCTION constexpr static_vector(std::initializer_list &&list) { + assert(list.size() <= N); + for (auto &&element : list) { + push_back(element); + } + } + + // copy constructor + // --------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr static_vector(static_vector const &other) { + clear(); + for (auto const &element : other) { + push_back(element); + } + } + + // move constructor + // --------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr static_vector(static_vector &&other) noexcept { + clear(); + for (auto &&element : other) { + push_back(element); + } + } + + // copy assignment + // ---------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr static_vector &operator=(static_vector const &other) { + clear(); + for (auto const &element : other) { + push_back(element); + } + return *this; + } + + // move assignment + // ---------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr static_vector &operator=(static_vector &&other) noexcept { + clear(); + for (auto &&element : other) { + push_back(element); + } + return *this; + } + + // destructor + // --------------------------------------------------------------------------------- + PORTABLE_FUNCTION ~static_vector() { clear(); } + + // operator[] + // --------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr reference operator[](size_type const i) & { return ref(i); } + + PORTABLE_FUNCTION constexpr const_reference operator[](size_type const i) const & { + return ref(i); + } + + PORTABLE_FUNCTION constexpr Element &&operator[](size_type const i) && { + return std::move(ref(i)); + } + + PORTABLE_FUNCTION constexpr Element const &&operator[](size_type const i) const && { + return std::move(ref(i)); + } + + // front + // -------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr reference front() & { return ref(); } + + PORTABLE_FUNCTION constexpr const_reference front() const & { return ref(); } + + PORTABLE_FUNCTION constexpr Element &&front() && { return ref(); } + + PORTABLE_FUNCTION constexpr Element const &&front() const && { return ref(); } + + // back + // --------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr reference back() & { return ref(count_ - 1); } + + PORTABLE_FUNCTION constexpr const_reference back() const & { return ref(count_ - 1); } + + PORTABLE_FUNCTION constexpr Element &&back() && { return ref(count_ - 1); } + + PORTABLE_FUNCTION constexpr Element const &&back() const && { return ref(count_ - 1); } + + // data + // --------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr pointer data() { return ptr(); } + + PORTABLE_FUNCTION constexpr const_pointer data() const { return ptr(); } + + // begin + // -------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr iterator begin() { return ptr(); } + + PORTABLE_FUNCTION constexpr const_iterator begin() const { return ptr(); } + + PORTABLE_FUNCTION constexpr const_iterator cbegin() const { return ptr(); } + + // end + // ---------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr iterator end() { return next(begin(), count_); } + + PORTABLE_FUNCTION constexpr const_iterator end() const { return next(begin(), count_); } + + PORTABLE_FUNCTION constexpr const_iterator cend() const { + return next(cbegin(), count_); + } + + // empty + // -------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr bool empty() const { return count_ == 0; } + + // size + // --------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr size_type size() const { return count_; } + + // max_size + // ----------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr size_type max_size() const { return N; } + + // capacity + // ----------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr size_type capacity() const { return max_size(); } + + // clear + // -------------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr void clear() noexcept { + for (size_type i = 0; i < count_; ++i) { + ptr(i)->~value_type(); + } + count_ = 0; + } + + // push_back + // ---------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr void push_back(value_type const &value) { + assert(count_ < N); + new (ptr(count_)) value_type{value}; + ++count_; + } + + PORTABLE_FUNCTION constexpr void push_back(value_type &&value) { + assert(count_ < N); + new (ptr(count_)) value_type{std::move(value)}; + ++count_; + } + + // emplace_back + // ------------------------------------------------------------------------------- + template + PORTABLE_FUNCTION constexpr reference emplace_back(Args &&...args) { + assert(count_ < N); + new (ptr(count_)) value_type(std::forward(args)...); + ++count_; + return back(); + } + + // pop_back + // ----------------------------------------------------------------------------------- + PORTABLE_FUNCTION constexpr void pop_back() { + if (count_ > 0) { + ptr(count_ - 1)->~value_type(); + --count_; + } + } + + // free function begin + // ------------------------------------------------------------------------ + PORTABLE_FUNCTION friend constexpr iterator begin(static_vector &sv) { + return sv.begin(); + } + + PORTABLE_FUNCTION friend constexpr const_iterator begin(static_vector const &sv) { + return sv.begin(); + } + + PORTABLE_FUNCTION friend constexpr const_iterator cbegin(static_vector const &sv) { + return sv.cbegin(); + } + + // free function end + // -------------------------------------------------------------------------- + PORTABLE_FUNCTION friend constexpr iterator end(static_vector &sv) { return sv.end(); } + + PORTABLE_FUNCTION friend constexpr const_iterator end(static_vector const &sv) { + return sv.end(); + } + + PORTABLE_FUNCTION friend constexpr const_iterator cend(static_vector const &sv) { + return sv.cend(); + } +}; + +} // namespace PortsOfCall + +#endif // ifndef _PORTS_OF_CALL_STATIC_VECTOR_HPP_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b0d6ee82..eb8c5e2d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -64,4 +64,12 @@ target_link_libraries(test_portsofcall include(Catch) catch_discover_tests(test_portsofcall) -target_sources(test_portsofcall PRIVATE test_portability.cpp test_array.cpp test_span.cpp) +target_sources(test_portsofcall + PRIVATE + test_portability.cpp + test_array.cpp + test_span.cpp + test_static_vector.cpp + test_static_vector_iterator.cpp + "$<$:test_static_vector_kokkos.cpp>" +) diff --git a/test/test_static_vector.cpp b/test/test_static_vector.cpp new file mode 100644 index 00000000..88548b27 --- /dev/null +++ b/test/test_static_vector.cpp @@ -0,0 +1,211 @@ +#include "ports-of-call/portability.hpp" +#include "ports-of-call/static_vector.hpp" + +#ifndef CATCH_CONFIG_FAST_COMPILE +#define CATCH_CONFIG_FAST_COMPILE +#include +#endif + +#include +#include + +namespace static_vector_test { + +template +constexpr static bool really_const = std::is_const>::value; +} + +TEST_CASE("static_vector", "[util][static_vector]") { + using std::begin; + using std::cbegin; + using std::cend; + using std::end; + + using static_vector_test::really_const; + + SECTION("begin/end iteration") { + SECTION("with non-zero size") { + using test_t = PortsOfCall::static_vector; + test_t data = {1, 2, 3}; + + auto b = begin(data); + auto e = end(data); + + CHECK(std::distance(b, e) == 3); + CHECK(b + 3 == e); + CHECK(not really_const); + CHECK(not really_const); + + auto cb = cbegin(data); + auto ce = cend(data); + + CHECK(really_const); + CHECK(really_const); + } + } + + SECTION("operator[]") { + SECTION("with non-zero size") { + using test_t = PortsOfCall::static_vector; + + SECTION("non-const data") { + test_t data = {1, 2, 3}; + + for (int i = 0; i < 3; ++i) { + CHECK(data[i] == i + 1); + } + CHECK(not really_const); + } + + SECTION("with const data") { + test_t const data = {1, 2, 3}; + + for (int i = 0; i < 3; ++i) { + CHECK(data[i] == i + 1); + } + CHECK(really_const); + } + } + } + + SECTION("front/back") { + using test_t = PortsOfCall::static_vector; + + SECTION("non-const data") { + test_t data = {1, 2, 3}; + + CHECK(data.front() == 1); + CHECK(data.back() == 3); + CHECK(not really_const); + CHECK(not really_const); + } + + SECTION("with const data") { + test_t const data = {1, 2, 3}; + + CHECK(data.front() == 1); + CHECK(data.back() == 3); + CHECK(really_const); + CHECK(really_const); + } + } + + SECTION("data") { + SECTION("with non-zero size") { + using test_t = PortsOfCall::static_vector; + + SECTION("non-const data") { + test_t data = {1, 2, 3}; + + CHECK(data.data() == std::addressof(data[0])); + } + + SECTION("const data") { + test_t const data = {1, 2, 3}; + + CHECK(data.data() == std::addressof(data[0])); + } + } + } + + SECTION("empty/size/max_size/capacity") { + SECTION("with non-zero size") { + using test_t = PortsOfCall::static_vector; + + test_t const data1 = {}; + + CHECK(data1.empty()); + CHECK(data1.size() == 0); + CHECK(data1.max_size() == 5); + CHECK(data1.capacity() == 5); + + test_t const data2 = {1, 2, 3}; + + CHECK(not data2.empty()); + CHECK(data2.size() == 3); + CHECK(data2.max_size() == 5); + CHECK(data2.capacity() == 5); + } + } + + SECTION("clear") { + SECTION("with trivial type") { + using test_t = PortsOfCall::static_vector; + + test_t data = {1, 2, 3}; + CHECK(data.size() == 3); + data.clear(); + CHECK(data.size() == 0); + } + + SECTION("with non-trivial type") { + using test_t = PortsOfCall::static_vector, 5>; + + test_t data = {std::vector(2), std::vector(4), + std::vector(6)}; + CHECK(data.size() == 3); + data.clear(); + CHECK(data.size() == 0); + } + } + + SECTION("push_back/emplace_back") { + using test_t = PortsOfCall::static_vector, 5>; + + test_t data; + + auto insert = std::vector(1); + data.push_back(insert); + + // Note: Some compilers struggle to parse the Catch2 macros, and will generate errors + // if you declare a lambda inside a CHECK macro. Therefore we move the line with the + // lambda outside of the CHECK. + auto expected1 = {std::vector(1)}; + auto size_check1 = + std::equal(begin(data), end(data), begin(expected1), end(expected1), + [](auto const &l, auto const &r) { return l.size() == r.size(); }); + CHECK(size_check1); + + data.push_back(std::vector(2)); + auto expected2 = {std::vector(1), std::vector(2)}; + auto size_check2 = + std::equal(begin(data), end(data), begin(expected2), end(expected2), + [](auto const &l, auto const &r) { return l.size() == r.size(); }); + CHECK(size_check2); + + data.emplace_back(3); + auto expected3 = {std::vector(1), std::vector(2), + std::vector(3)}; + auto size_check3 = + std::equal(begin(data), end(data), begin(expected3), end(expected3), + [](auto const &l, auto const &r) { return l.size() == r.size(); }); + CHECK(size_check3); + } + + SECTION("pop_back") { + SECTION("with trivial type") { + using test_t = PortsOfCall::static_vector; + test_t data = {1, 2, 3, 4, 5}; + + data.pop_back(); + test_t const expected = {1, 2, 3, 4}; + CHECK(std::equal(begin(data), end(data), begin(expected))); + } + + SECTION("with non-trivial type") { + using test_t = PortsOfCall::static_vector, 5>; + test_t data = {std::vector(1), std::vector(2), + std::vector(3), std::vector(4), + std::vector(5)}; + + data.pop_back(); + test_t const expected = {std::vector(1), std::vector(2), + std::vector(3), std::vector(4)}; + + auto size_check = + std::equal(begin(data), end(data), begin(expected), end(expected), + [](auto const &l, auto const &r) { return l.size() == r.size(); }); + CHECK(size_check); + } + } +} diff --git a/test/test_static_vector_iterator.cpp b/test/test_static_vector_iterator.cpp new file mode 100644 index 00000000..478a1caa --- /dev/null +++ b/test/test_static_vector_iterator.cpp @@ -0,0 +1,230 @@ +#include "ports-of-call/portability.hpp" +#include "ports-of-call/static_vector.hpp" + +#ifndef CATCH_CONFIG_FAST_COMPILE +#define CATCH_CONFIG_FAST_COMPILE +#include +#endif + +#include +#include +#include + +namespace static_vector_iterator_test { + +template +constexpr static bool really_const = std::is_const>::value; +} + +TEST_CASE("static_vector iterator", "[util][static_vector]") { + using std::begin; + using std::cbegin; + using std::cend; + using std::end; + + using PortsOfCall::static_vector; + + SECTION("non-const iterator") { + static_vector sv{1, 2, 3}; + auto it = begin(sv); + auto end_it = end(sv); + + SECTION("construct const iterator") { + using static_vector_iterator_test::really_const; + decltype(cbegin(sv)) const_it{it}; + REQUIRE(really_const); + } + + SECTION("operator++ and mutate") { + while (it != end_it) { + *it *= 2; + ++it; + } + std::array expected{{2, 4, 6}}; + REQUIRE(std::equal(begin(sv), end(sv), begin(expected))); + } + + SECTION("operator++(int)") { + auto next_it = it++; + REQUIRE(*next_it == 2); + } + + SECTION("operator--") { + auto next_it = it++; + --next_it; + REQUIRE(*next_it == 1); + } + + SECTION("operator--(int)") { + auto next_it = it++; + auto prev_it = next_it--; + REQUIRE(*prev_it == 1); + } + + SECTION("operator+=") { + it += 2; + REQUIRE(*it == 3); + } + + SECTION("operator+") { + auto next_it = it + 2; + REQUIRE(*next_it == 3); + } + + SECTION("operator-=") { + end_it -= 2; + REQUIRE(*end_it == 2); + } + + SECTION("operator-") { + auto prev_it = end_it - 2; + REQUIRE(*prev_it == 2); + } + + SECTION("difference operator-") { REQUIRE(end_it - it == 3); } + + SECTION("operator==") { REQUIRE(it + 3 == end_it); } + + SECTION("operator!=") { REQUIRE(it != end_it); } + + SECTION("operator<") { + REQUIRE(it < end_it); + REQUIRE(not(it + 3 < end_it)); + } + + SECTION("operator<=") { + REQUIRE(it <= end_it); + REQUIRE(it + 3 <= end_it); + } + + SECTION("operator>") { + REQUIRE(end_it > it); + REQUIRE(not(end_it > it + 3)); + } + + SECTION("operator>=") { + REQUIRE(end_it >= it); + REQUIRE(end_it >= it + 3); + } + + SECTION("operator->") { + REQUIRE(std::is_same()), int *>::value); + } + + SECTION("operator[]") { REQUIRE(it[1] == 2); } + + SECTION("advance") { + advance(it, 2); + REQUIRE(*it == 3); + } + + SECTION("next") { + auto next_it = next(it, 2); + REQUIRE(*next_it == 3); + } + + SECTION("prev") { + auto prev_it = prev(end_it, 2); + REQUIRE(*prev_it == 2); + } + + SECTION("distance") { REQUIRE(distance(it, end_it) == 3); } + } + + SECTION("const iterator") { + static_vector sv{1, 2, 3}; + auto it = cbegin(sv); + auto end_it = cend(sv); + + SECTION("operator++ and mutate") { + ++it; + REQUIRE(*it == 2); + } + + SECTION("operator++(int)") { + auto next_it = it++; + REQUIRE(*next_it == 2); + } + + SECTION("operator--") { + auto next_it = it++; + --next_it; + REQUIRE(*next_it == 1); + } + + SECTION("operator--(int)") { + auto next_it = it++; + auto prev_it = next_it--; + REQUIRE(*prev_it == 1); + } + + SECTION("operator+=") { + it += 2; + REQUIRE(*it == 3); + } + + SECTION("operator+") { + auto next_it = it + 2; + REQUIRE(*next_it == 3); + } + + SECTION("operator-=") { + end_it -= 2; + REQUIRE(*end_it == 2); + } + + SECTION("operator-") { + auto prev_it = end_it - 2; + REQUIRE(*prev_it == 2); + } + + SECTION("difference operator-") { REQUIRE(end_it - it == 3); } + + SECTION("operator==") { REQUIRE(it + 3 == end_it); } + + SECTION("operator!=") { REQUIRE(it != end_it); } + + SECTION("operator<") { + REQUIRE(it < end_it); + REQUIRE(not(it + 3 < end_it)); + } + + SECTION("operator<=") { + REQUIRE(it <= end_it); + REQUIRE(it + 3 <= end_it); + } + + SECTION("operator>") { + REQUIRE(end_it > it); + REQUIRE(not(end_it > it + 3)); + } + + SECTION("operator>=") { + REQUIRE(end_it >= it); + REQUIRE(end_it >= it + 3); + } + + SECTION("operator->") { + REQUIRE(std::is_same()), int const *>::value); + } + + SECTION("operator[]") { REQUIRE(it[1] == 2); } + + SECTION("advance") { + advance(it, 2); + REQUIRE(*it == 3); + } + + SECTION("next") { + auto next_it = next(it, 2); + REQUIRE(*next_it == 3); + } + + SECTION("prev") { + auto prev_it = prev(end_it, 2); + REQUIRE(*prev_it == 2); + } + + SECTION("distance") { REQUIRE(distance(it, end_it) == 3); } + } +} diff --git a/test/test_static_vector_kokkos.cpp b/test/test_static_vector_kokkos.cpp new file mode 100644 index 00000000..12097cdf --- /dev/null +++ b/test/test_static_vector_kokkos.cpp @@ -0,0 +1,117 @@ +#ifdef PORTABILITY_STRATEGY_KOKKOS + +#include "ports-of-call/portability.hpp" +#include "ports-of-call/static_vector.hpp" + +#ifndef CATCH_CONFIG_FAST_COMPILE +#define CATCH_CONFIG_FAST_COMPILE +#include +#endif + +#include + +#include + +// ================================================================================================ + +template +PORTABLE_FUNCTION void print(PortsOfCall::static_vector const &sv) { + switch (sv.size()) { + case 0: + printf("[%s] {}\n", Space::name()); + break; + case 1: + printf("[%s] {%d}\n", Space::name(), sv[0]); + break; + case 2: + printf("[%s] {%d,%d}\n", Space::name(), sv[0], sv[1]); + break; + case 3: + printf("[%s] {%d,%d,%d}\n", Space::name(), sv[0], sv[1], sv[2]); + break; + case 4: + printf("[%s] {%d,%d,%d,%d}\n", Space::name(), sv[0], sv[1], sv[2], sv[3]); + break; + case 5: + printf("[%s] {%d,%d,%d,%d,%d}\n", Space::name(), sv[0], sv[1], sv[2], sv[3], sv[4]); + break; + case 6: + printf("[%s] {%d,%d,%d,%d,%d,%d}\n", Space::name(), sv[0], sv[1], sv[2], sv[3], sv[4], + sv[5]); + break; + default: + printf("[%s] {%d,%d,...,%d,%d} (size=%d)\n", Space::name(), sv[0], sv[1], + sv[sv.size() - 2], sv[sv.size() - 1], static_cast(sv.size())); + break; + } +} + +// ================================================================================================ + +template +PORTABLE_FUNCTION void do_stuff(int const n, + PortsOfCall::static_vector &sv) { + printf("[%s] start\n", Space::name()); + print(sv); + sv.push_back(n + 0); + print(sv); + sv.push_back(n + 1); + print(sv); + sv.push_back(n + 2); + print(sv); +} + +// ================================================================================================ + +TEST_CASE("static vector GPU", "[reaction_network]") { + std::cout << "start" << std::endl; + + using HostSpace = Kokkos::HostSpace; + using ExecSpace = Kokkos::DefaultExecutionSpace::memory_space; + + constexpr int N{10}; + + std::cout << "declare sv_list_c" << std::endl; + Kokkos::View *, HostSpace> sv_list_c("sv_list", + N); + + std::cout << "print sv_list_c" << std::endl; + for (int n{0}; n < N; ++n) { + for (int i{0}; i < n; ++i) { + sv_list_c(n).push_back(i); + } + print(sv_list_c(n)); + } + + std::cout << "build GPU mirror (sv_list_g)" << std::endl; + auto sv_list_g = Kokkos::create_mirror_view_and_copy(ExecSpace(), sv_list_c); + + std::cout << "run on the GPU" << std::endl; + Kokkos::fence(); // ensure the data has arrived on the GPU + Kokkos::parallel_for( + N, KOKKOS_LAMBDA(int const n) { do_stuff(n, sv_list_g(n)); }); + + std::cout << "copy back to the CPU" << std::endl; + Kokkos::fence(); // ensure the calculation is complete + Kokkos::deep_copy(sv_list_c, sv_list_g); + + std::cout << "print again" << std::endl; + Kokkos::fence(); // ensure the data has arrived back on the CPU + for (int n{0}; n < N; ++n) { + print(sv_list_c(n)); + } + + std::cout << "check results" << std::endl; + for (int n{0}; n < N; ++n) { + REQUIRE(sv_list_c(n).size() == static_cast(n + 3)); + for (std::size_t i{0}; i < sv_list_c(n).size(); ++i) { + CHECK(sv_list_c(n)[i] == static_cast(i)); + } + } + + std::cout << "done" << std::endl; +} + +// ================================================================================================ + +#endif //PORTABILITY_STRATEGY_KOKKOS