diff --git a/README.md b/README.md index 09e5f82..b7e22b1 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,14 @@ SPDX-License-Identifier: # beman.inplace\_vector: Dynamically-resizable vector with fixed capacity - - ![Continuous Integration Tests](https://github.com/bemanproject/inplace_vector/actions/workflows/ci_tests.yml/badge.svg) +![Library Status](https://github.com/bemanproject/beman/blob/c6997986557ec6dda98acbdf502082cdf7335526/images/badges/beman_badge-beman_library_under_development.svg) +![Continuous Integration Tests](https://github.com/bemanproject/inplace_vector/actions/workflows/ci_tests.yml/badge.svg) ![Code Format](https://github.com/bemanproject/inplace_vector/actions/workflows/pre-commit.yml/badge.svg) - **Implements**: [`inplace_vector` (P0843R14)](https://wg21.link/P0843R14) - -**Status**: [Under development and not yet ready for production use.](https://github.com/bemanproject/beman/blob/main/docs/BEMAN_LIBRARY_MATURITY_MODEL.md#under-development-and-not-yet-ready-for-production-use) - +**Status**: +[Under development and not yet ready for production use.](https://github.com/bemanproject/beman/blob/main/docs/BEMAN_LIBRARY_MATURITY_MODEL.md#under-development-and-not-yet-ready-for-production-use) ## Usage @@ -30,13 +28,16 @@ which dynamic memory allocations are undesired. #### Note on implementation progress Current implementation implements all public interfaces defined in the paper. -However constexpr related functionalities are not tested and maybe broken. -There have been minor updates to the wording after the paper is accepted, notably [P3247](wg21.link/P3247). +There have been minor updates to the wording after the paper is accepted, +notably [P3247](wg21.link/P3247). Which changes the requirements for constexpr support. This will likely be preceded with [P3074](wg21.link/P3074). These has not been implemented yet. +You can follow [this link](eel.is/c++draft/inplace.vector) +to checkout the status of `inplace_vector` in the latest draft. + Contributions are welcome. ### Code example @@ -53,12 +54,13 @@ using namespace beman; * Generates fibonacci sequence using inplace_vector. * See: https://en.wikipedia.org/wiki/Fibonacci_sequence */ -template inplace_vector fibonacci_to(int num) { +template +constexpr inplace_vector fibonacci_to(int num) { assert(num < Capacity); inplace_vector vec; - constexpr static std::array first_two{0, 1}; + constexpr std::array first_two{0, 1}; for (auto i = 0; i <= num; ++i) { auto new_val = i < 2 ? first_two[i] : vec[i - 1] + vec[i - 2]; vec.push_back(new_val); @@ -66,8 +68,32 @@ template inplace_vector fibonacci_to(int num) { return vec; } + +/* + * Check the result of the computation at compile time. + */ +constexpr bool check_5() { + auto got = fibonacci_to<10>(5); + constexpr inplace_vector correct{0, 1, 1, 2, 3, 5}; + return got == correct; +} + +static_assert(check_5()); + ``` +### Note on constexpr support + +Since `constexpr` requirements are actively changing, +you can use `beman::has_constexpr_support` to detect if our implementation +provide constexpr support for a specific specialization of `inplace_vector`. + +Note this is not part of the standard Library and should not be relied on once +`constexpr` requirement stabilize. + +Example Usage: +`static_assert(beman::has_constexpr_support>)`. + ## How to Build ### Compiler support diff --git a/examples/fibonacci.cpp b/examples/fibonacci.cpp index 0c77ceb..b2d1e17 100644 --- a/examples/fibonacci.cpp +++ b/examples/fibonacci.cpp @@ -10,12 +10,13 @@ using namespace beman; * Generates fibonacci sequence using inplace_vector. * See: https://en.wikipedia.org/wiki/Fibonacci_sequence */ -template inplace_vector fibonacci_to(int num) { +template +constexpr inplace_vector fibonacci_to(int num) { assert(num < Capacity); inplace_vector vec; - constexpr static std::array first_two{0, 1}; + constexpr std::array first_two{0, 1}; for (auto i = 0; i <= num; ++i) { auto new_val = i < 2 ? first_two[i] : vec[i - 1] + vec[i - 2]; vec.push_back(new_val); @@ -24,6 +25,17 @@ template inplace_vector fibonacci_to(int num) { return vec; } +/* + * Check the result of the computation at compile time. + */ +constexpr bool check_5() { + auto got = fibonacci_to<10>(5); + constexpr inplace_vector correct{0, 1, 1, 2, 3, 5}; + return got == correct; +} + +static_assert(check_5()); + /** * Expected program output: * diff --git a/include/beman/inplace_vector/inplace_vector.hpp b/include/beman/inplace_vector/inplace_vector.hpp index 0d7fc6a..320cfd1 100644 --- a/include/beman/inplace_vector/inplace_vector.hpp +++ b/include/beman/inplace_vector/inplace_vector.hpp @@ -306,6 +306,9 @@ concept container_compatible_range = std::ranges::input_range && std::convertible_to, T>; +template +concept satify_constexpr = N == 0 || std::is_trivial_v; + } // namespace beman::details::inplace_vector // Types implementing the `inplace_vector`'s storage @@ -425,13 +428,18 @@ template struct non_trivial { // Selects the vector storage. template using storage_for = std::conditional_t< - N == 0, zero_sized, - std::conditional_t, trivial, non_trivial>>; + !satify_constexpr, non_trivial, + std::conditional_t, trivial>>; } // namespace beman::details::inplace_vector::storage namespace beman { +template +concept has_constexpr_support = + details::inplace_vector::satify_constexpr; + /// Dynamically-resizable fixed-N vector with inplace storage. template struct inplace_vector diff --git a/tests/beman/inplace_vector/CMakeLists.txt b/tests/beman/inplace_vector/CMakeLists.txt index 24126a6..e0a01b4 100644 --- a/tests/beman/inplace_vector/CMakeLists.txt +++ b/tests/beman/inplace_vector/CMakeLists.txt @@ -40,3 +40,14 @@ add_gtest(triviality) add_gtest(constructors) add_gtest(size_n_data) add_gtest(erasure) + +# constexpr test +add_executable(beman.inplace_vector.tests.constexpr constexpr.test.cpp) +target_link_libraries( + beman.inplace_vector.tests.constexpr + PRIVATE beman.inplace_vector +) +add_test( + NAME beman.inplace_vector.tests.constexpr + COMMAND beman.inplace_vector.tests.constexpr +) diff --git a/tests/beman/inplace_vector/README.md b/tests/beman/inplace_vector/README.md index 97cd8ee..b385f0d 100644 --- a/tests/beman/inplace_vector/README.md +++ b/tests/beman/inplace_vector/README.md @@ -50,7 +50,12 @@ Not tested for now. > then no `inplace_vector` member functions are usable in > constant expressions. -Not tested for now. +See [constexpr.test.cpp](constexpr.test.cpp), +note that this test suite only ensure functions are usable in a constexpr +environment. +The validity of those functions are tested in the main test suite. +This test also doesn't exhaustivly test constexpr functions' behavior +when exception throwing is expected. #### 6.5 Bad alloc requirement @@ -99,7 +104,6 @@ See [erasure.test.cpp](erasure.test.cpp) ## Known Issues/ Missed Tests -- Constexpr related functionalities. - Emplacement minimal copy/ construction. - Exception safety on mutation. diff --git a/tests/beman/inplace_vector/constexpr.test.cpp b/tests/beman/inplace_vector/constexpr.test.cpp new file mode 100644 index 0000000..2d44642 --- /dev/null +++ b/tests/beman/inplace_vector/constexpr.test.cpp @@ -0,0 +1,363 @@ +#include +#include +#include + +// used for testing beman::has_constexpr_support +#include +#include + +/** + * These tests are only meant to test that suitable constexpr functions compiles + * in a constant evaluation environment. + * + * The validity of the underlying implementation is tested in the main test + * case as there's no diverging logic between constexpr code and runtime code. + */ + +struct Some { + constexpr Some() = default; + constexpr Some(int v) : val(v) {} + + int val; + + constexpr bool operator==(const Some &other) const { + return val == other.val; + } + constexpr auto operator<=>(const Some &other) const { + return val <=> other.val; + } +}; +static_assert(std::is_trivial_v); + +using beman::has_constexpr_support; +using beman::inplace_vector; + +static_assert(has_constexpr_support>); +static_assert(has_constexpr_support>); + +static_assert(has_constexpr_support>); +static_assert(!has_constexpr_support>); + +static_assert(has_constexpr_support, 0>>); +static_assert(!has_constexpr_support, 50>>); + +#define TEST(NAME) \ + static_assert(std::invoke([]() { \ + NAME>(); \ + NAME>(); \ + return true; \ + }), \ + "##NAME"); + +template constexpr void test_constructors() { + using T = IV::value_type; + + std::array arr; + arr.fill(T(20)); + + { + IV v; + } + { + IV v(10); + } + { + IV v(5, T(10)); + } + { + IV v(arr.begin(), arr.end()); + } + { + IV v(beman::from_range_t{}, arr); + } + { + IV other{0, 1}; + IV copy(other); + } + { + IV other{0, 1}; + IV copy(std::move(other)); + } + { + IV v({0, 1}); + } +} +TEST(test_constructors); + +template constexpr void test_op_eq() { + using T = IV::value_type; + + { + IV v, other{0, 1}; + v = other; + } + { + IV v, other{0, 1}; + v = std::move(other); + } + { + IV v; + v = {0, 1}; + } +} +TEST(test_op_eq); + +template constexpr void test_assignment() { + using T = IV::value_type; + + std::array arr; + arr.fill(T(20)); + + { + IV v; + v.assign(arr.begin(), arr.end()); + } + { + IV v; + v.assign_range(arr); + } + { + IV v; + v.assign(5, T(20)); + } + { + IV v; + v.assign({0, 1, 2}); + } +} +TEST(test_assignment); + +template constexpr void test_iterator_access() { + using T = IV::value_type; + + { + IV v{0, 1}; + (void)v.begin(); + (void)v.end(); + (void)v.rbegin(); + (void)v.rend(); + (void)v.cbegin(); + (void)v.cend(); + (void)v.crbegin(); + (void)v.crend(); + } +} +TEST(test_iterator_access); + +template constexpr void test_size_capacity() { + using T = IV::value_type; + + { + IV v{0, 1}; + (void)v.empty(); + (void)v.size(); + (void)v.max_size(); + (void)v.capacity(); + } + + { + IV v{0, 1}; + v.resize(3); + v.resize(2, T(20)); + } + + { + IV v{0, 1}; + v.reserve(5); + v.shrink_to_fit(); + } +} +TEST(test_size_capacity); + +template constexpr void test_element_access() { + { + IV v{0, 1}; + (void)v[0]; + (void)v.at(0); + (void)v.front(); + (void)v.back(); + } + + { + const IV v{0, 1}; + (void)v[0]; + (void)v.at(0); + (void)v.front(); + (void)v.back(); + } +} +TEST(test_element_access); + +template constexpr void test_data_access() { + { + IV v{0, 1}; + v.data(); + } + { + const IV v{0, 1}; + v.data(); + } +} +TEST(test_data_access); + +template constexpr void test_modifiers() { + using T = IV::value_type; + + std::array arr; + arr.fill(T(20)); + + { + IV v; + v.emplace_back(20); + v.push_back(arr[0]); + v.push_back(T(20)); + v.append_range(arr); + v.pop_back(); + } + + { + IV v; + v.try_emplace_back(20); + v.try_push_back(arr[0]); + v.try_push_back(T(20)); + // v.try_append_range(arr); + } + + { + IV v; + v.unchecked_emplace_back(20); + v.unchecked_push_back(arr[0]); + v.unchecked_push_back(T(20)); + } + + { + IV v{0, 1}; + v.emplace(v.begin(), 20); + v.insert(v.begin(), arr[0]); + v.insert(v.begin(), T(20)); + v.insert(v.begin(), 2, arr[0]); + v.insert(v.begin(), arr.begin(), arr.end()); + v.insert_range(v.begin(), arr); + v.insert(v.begin(), {1, 2}); + v.erase(v.begin()); + v.erase(v.begin(), v.begin() + 2); + } + + { + IV v, other{0, 1}; + v.swap(other); + } + + { + IV v{0, 1}; + v.clear(); + } +} +TEST(test_modifiers); + +template constexpr void test_op_comp() { + IV v{0, 1, 2}, other{3, 4}; + + (void)(v == v); + (void)(v == other); + (void)(v <=> v); + (void)(v <=> other); +} +TEST(test_op_comp); + +template constexpr void test_erase() { + IV v{0, 1, 2, 3, 3, 5}; + (void)beman::erase(v, 3); + (void)beman::erase_if(v, [](auto v) { return v < 3; }); +} +TEST(test_erase) + +struct Complex { + int val = 0; + + constexpr bool operator==(const Complex &other) const { + return val == other.val; + } + constexpr auto operator<=>(const Complex &other) const { + return val <=> other.val; + } +}; +static_assert(!std::is_trivially_default_constructible_v); + +static_assert(has_constexpr_support>); +static_assert(!has_constexpr_support>); + +#define TEST_EMPTY(NAME) \ + static_assert(std::invoke([]() { \ + NAME(); \ + return true; \ + }), \ + "##NAME"); + +template constexpr void speical_test_empty() { + static_assert(!std::is_trivially_default_constructible_v); + using IV = beman::inplace_vector; + + std::array arr; + arr.fill(T{50}); + + { + IV v; + } + { + IV v(0, T{50}); + } + { + IV a, b; + a = b; + a = IV(); + } + { + IV v; + v.assign(0, T{50}); + } + { + IV v; + (void)v.begin(); + (void)v.end(); + (void)v.rbegin(); + (void)v.rend(); + (void)v.cbegin(); + (void)v.cend(); + (void)v.crbegin(); + (void)v.crend(); + } + { + IV v; + (void)v.empty(); + (void)v.size(); + (void)v.max_size(); + (void)v.capacity(); + v.resize(0); + v.resize(0, T{40}); + v.reserve(0); + v.shrink_to_fit(); + } + { + IV v; + v.try_emplace_back(50); + v.try_push_back(T(50)); + v.try_push_back(arr[0]); + // v.try_append_range(arr); + v.clear(); + } + { + IV a, b; + a.swap(b); + } + { + IV a, b; + (void)(a == b); + (void)(a <=> b); + } +} +TEST_EMPTY(speical_test_empty); + +int main() { + // compile means passing +}