Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,50 @@ jobs:
- name: Test Debug
run: ctest --test-dir build --build-config Debug

configuration-test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
args:
- name: "Disable build testing"
arg: "-DBEMAN_INPLACE_VECTOR_BUILD_TESTS=OFF"
- name: "Disable example building"
arg: "-DBEMAN_INPLACE_VECTOR_BUILD_EXAMPLES=OFF"
- name: "Enable fixed size type"
arg: "-DBEMAN_INPLACE_VECTOR_FIXED_SIZE_T=ON"
name: "CMake: ${{ matrix.args.name }}"
steps:
- uses: actions/checkout@v4
- name: Setup build environment
uses: lukka/get-cmake@latest
with:
cmakeVersion: "~3.25.0"
ninjaVersion: "^1.11.1"
- name: Print installed software
run: |
cmake --version
ninja --version
- name: Configure CMake
run: |
cmake -B build -S . -DCMAKE_CXX_STANDARD=20 ${{ matrix.args.arg }}
env:
CMAKE_GENERATOR: "Ninja Multi-Config"
- name: Build Release
run: |
# Portable commands only
cmake --build build --config Release --parallel --verbose
# cmake --build build --config Release --target all_verify_interface_header_sets
cmake --install build --config Release --prefix /opt/beman.inplace_vector
ls -R /opt/beman.inplace_vector
- name: Build Debug
run: |
# Portable commands only
cmake --build build --config Debug --parallel --verbose
# cmake --build build --config Debug --target all_verify_interface_header_sets
cmake --install build --config Debug --prefix /opt/beman.inplace_vector
ls -R /opt/beman.inplace_vector

compiler-test:
runs-on: ubuntu-latest
strategy:
Expand Down
16 changes: 15 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ project(

# [CMAKE.SKIP_EXAMPLES]
option(
BEMAN_EXEMPLAR_BUILD_EXAMPLES
BEMAN_INPLACE_VECTOR_BUILD_EXAMPLES
"Enable building examples. Default: ON. Values: { ON, OFF }."
${PROJECT_IS_TOP_LEVEL}
)
Expand All @@ -27,6 +27,19 @@ option(
${PROJECT_IS_TOP_LEVEL}
)

option(
BEMAN_INPLACE_VECTOR_FIXED_SIZE_T
"Fixes size_t in inplace_vector, otherwise size_t is dynamic against \
inplace_vector's capacity. Default: OFF. Values: { ON, OFF }."
OFF
)

configure_file(
"${PROJECT_SOURCE_DIR}/include/beman/inplace_vector/config.hpp.in"
"${PROJECT_BINARY_DIR}/include/beman/inplace_vector/config.hpp"
@ONLY
)

include(FetchContent)
include(GNUInstallDirs)

Expand All @@ -38,6 +51,7 @@ target_include_directories(
beman.inplace_vector
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
)

Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,24 @@ Test project /.../inplace_vector/build
Total Test time (real) = 0.01 sec
```

## Build arguments

### Disable dynamic size type in control block

By default,
the type of the size variable in the control block is selected to the smallest
it could be
(as an upper bound can be established given we know the capacity of the vector).
Comment on lines +211 to +214
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for the way the lines are broken here?


You can turn off this behavior by setting `BEMAN_INPLACE_VECTOR_FIXED_SIZE_T`
to `ON`, which fixes the type to `std::size_t`.

Example: configuring the project with fixed size type.

```bash
cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DBEMAN_INPLACE_VECTOR_FIXED_SIZE_T=ON
```

## Development

### Linting
Expand Down
7 changes: 7 additions & 0 deletions include/beman/inplace_vector/config.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#pragma once

#define BEMAN_INPLACE_VECTOR_CONFIG

#cmakedefine01 BEMAN_INPLACE_VECTOR_FIXED_SIZE_T
69 changes: 47 additions & 22 deletions include/beman/inplace_vector/inplace_vector.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#pragma once
#pragma GCC diagnostic ignored "-Wunused-parameter"

#include <beman/inplace_vector/config.hpp>
#ifndef BEMAN_INPLACE_VECTOR_CONFIG
#error "Config file not run"
#endif

/*
* SPDX-FileCopyrightText: Copyright (c) 2023 Gonzalo Brito Gadeschi. All rights
reserved.
Expand Down Expand Up @@ -282,15 +287,31 @@ namespace beman::details::inplace_vector {

// clang-format off
// Smallest unsigned integer that can represent values in [0, N].
template <size_t N>
template <std::size_t N>
using smallest_size_t
= std::conditional_t<(N < std::numeric_limits<uint8_t>::max()), uint8_t,
std::conditional_t<(N < std::numeric_limits<uint16_t>::max()), uint16_t,
std::conditional_t<(N < std::numeric_limits<uint32_t>::max()), uint32_t,
std::conditional_t<(N < std::numeric_limits<uint64_t>::max()), uint64_t,
size_t>>>>;
std::size_t>>>>;
// clang-format on

/*
* This is for managing the type of size in the control block.
*
* This behavior can be turned off by setting the
* BEMAN_INPLACE_VECTOR_FIXED_SIZE_T build option.
*
* This macro is set in the config.hpp.
*/
template <std::size_t N>
using size_t_for =
#if BEMAN_INPLACE_VECTOR_FIXED_SIZE_T == 1
std::size_t;
#else
smallest_size_t<N>;
#endif

// Index a random-access and sized range doing bound checks in debug builds
template <std::ranges::random_access_range Rng, std::integral Index>
static constexpr decltype(auto) index(Rng &&rng, Index i) noexcept
Expand Down Expand Up @@ -320,7 +341,7 @@ template <class T> struct zero_sized {
using size_type = uint8_t;
static constexpr T *storage_data() noexcept { return nullptr; }
static constexpr size_type storage_size() noexcept { return 0; }
static constexpr void unsafe_set_size(size_t new_size) noexcept {
static constexpr void unsafe_set_size(std::size_t new_size) noexcept {
IV_EXPECT(new_size == 0 &&
"tried to change size of empty storage to non-zero value");
}
Expand All @@ -335,13 +356,13 @@ template <class T> struct zero_sized {
};

// Storage for trivial types.
template <class T, size_t N> struct trivial {
template <class T, std::size_t N> struct trivial {
static_assert(std::is_trivial_v<T>,
"storage::trivial<T, C> requires Trivial<T>");
static_assert(N != size_t{0}, "N == 0, use zero_sized");
static_assert(N != std::size_t{0}, "N == 0, use zero_sized");

protected:
using size_type = smallest_size_t<N>;
using size_type = size_t_for<N>;

private:
// If value_type is const, then const std::array of non-const elements:
Expand All @@ -357,7 +378,7 @@ template <class T, size_t N> struct trivial {
}
constexpr T *storage_data() noexcept { return storage_data_.data(); }
constexpr size_type storage_size() const noexcept { return storage_size_; }
constexpr void unsafe_set_size(size_t new_size) noexcept {
constexpr void unsafe_set_size(std::size_t new_size) noexcept {
IV_EXPECT(size_type(new_size) <= N && "new_size out-of-bounds [0, N]");
storage_size_ = size_type(new_size);
}
Expand All @@ -371,26 +392,27 @@ template <class T, size_t N> struct trivial {
constexpr ~trivial() = default;
};

template <class T, size_t N> struct raw_byte_based_storage {
template <class T, std::size_t N> struct raw_byte_based_storage {
public:
alignas(T) std::byte _d[sizeof(T) * N];
constexpr T *storage_data(size_t i) noexcept {
constexpr T *storage_data(std::size_t i) noexcept {
IV_EXPECT(i < N);
return reinterpret_cast<T *>(_d) + i;
}
constexpr const T *storage_data(size_t i) const noexcept {
constexpr const T *storage_data(std::size_t i) const noexcept {
IV_EXPECT(i < N);
return reinterpret_cast<const T *>(_d) + i;
}
};

/// Storage for non-trivial elements.
template <class T, size_t N> struct non_trivial {
template <class T, std::size_t N> struct non_trivial {
static_assert(!std::is_trivial_v<T>,
"use storage::trivial for Trivial<T> elements");
static_assert(N != size_t{0}, "use storage::zero for N==0");
static_assert(N != std::size_t{0}, "use storage::zero for N==0");

protected:
using size_type = smallest_size_t<N>;
using size_type = size_t_for<N>;

private:
using byte_based_storage = std::conditional_t<
Expand All @@ -405,7 +427,7 @@ template <class T, size_t N> struct non_trivial {
}
constexpr T *storage_data() noexcept { return storage_data_.storage_data(0); }
constexpr size_type storage_size() const noexcept { return storage_size_; }
constexpr void unsafe_set_size(size_t new_size) noexcept {
constexpr void unsafe_set_size(std::size_t new_size) noexcept {
IV_EXPECT(size_type(new_size) <= N && "new_size out-of-bounds [0, N)");
storage_size_ = size_type(new_size);
}
Expand All @@ -426,7 +448,7 @@ template <class T, size_t N> struct non_trivial {
};

// Selects the vector storage.
template <class T, size_t N>
template <class T, std::size_t N>
using storage_for = std::conditional_t<
!satify_constexpr<T, N>, non_trivial<T, N>,
std::conditional_t<N == 0, zero_sized<T>, trivial<T, N>>>;
Expand All @@ -441,7 +463,7 @@ concept has_constexpr_support =
IV::capacity()>;

/// Dynamically-resizable fixed-N vector with inplace storage.
template <class T, size_t N>
template <class T, std::size_t N>
struct inplace_vector
: private details::inplace_vector::storage::storage_for<T, N> {
private:
Expand All @@ -458,7 +480,8 @@ struct inplace_vector
using const_pointer = const T *;
using reference = value_type &;
using const_reference = const value_type &;
using size_type = size_t;
// Note: This may be different from base_t::size_type
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = pointer;
using const_iterator = const_pointer;
Expand Down Expand Up @@ -526,7 +549,9 @@ struct inplace_vector
[[nodiscard]] constexpr bool empty() const noexcept {
return storage_size() == 0;
};
constexpr size_type size() const noexcept { return storage_size(); }
constexpr size_type size() const noexcept {
return size_type(storage_size());
}
static constexpr size_type max_size() noexcept { return N; }
static constexpr size_type capacity() noexcept { return N; }
// constexpr void resize(size_type sz);
Expand Down Expand Up @@ -738,8 +763,8 @@ struct inplace_vector
{
assert_iterator_in_range(position);
if constexpr (std::random_access_iterator<InputIterator>) {
if (size() + static_cast<size_type>(std::distance(first, last)) >
capacity()) [[unlikely]]
if (size() + size_type(std::distance(first, last)) > capacity())
[[unlikely]]
throw std::bad_alloc{};
}
auto b = end();
Expand Down Expand Up @@ -772,7 +797,7 @@ struct inplace_vector
{
assert_iterator_in_range(position);
auto b = end();
for (size_type i = 0; i < n; ++i)
for (auto i = size_type(0); i < n; ++i)
emplace_back(x);
auto pos = begin() + (position - begin());
std::rotate(pos, b, end());
Expand Down Expand Up @@ -835,7 +860,7 @@ struct inplace_vector
iterator f = begin() + (first - begin());
if (first != last) {
unsafe_destroy(std::move(f + (last - first), end(), f), end());
unsafe_set_size(size() - static_cast<size_type>(last - first));
unsafe_set_size(size() - size_type(last - first));
}
return f;
}
Expand Down