A C++20 fixed size container, with arbitrary indexes. Provides support for indexing via enum values, arbitrary intervals (starting at non-zero values), and non-contiguous sets. Extensible to support virtually any kind of indexing, including multi-dimensional indexing.
Also offers a feature named “safe-initialization”, which allows providing the index with the value when initializing the array content. This can be seen as the pendant of C++20 designated initializer-lists, but for array content.
- Ranges:
interval<Start, End>for contiguous integer ranges. - Enums: Works with
enum class(greatly enhanced by usingC++26, or an enum reflection library such asboost::describe,magic_enumorwise_enum. Works with non-contiguous enums. - Sets:
union_of<...>for non-contiguous or mixed indexes. - Custom Indexers: Implement your own indexer with
lambda_indexeror by satisfying theindexerconcept.
- Compile-time checks: Ensures all indexes are initialized.
- Tagged Initializers: Prevents errors when inserting new values in the middle of an initialization list.
- multidimensional indexing with indexed span (non-owning view over the data) support
indexed_bitset: A bitset with arbitrary indexes (e.g., for enum-based flags).indexed_span: A non-owning view over anindexed_arrayor raw data.
Out of the box, indexed_array has support for the following enum reflection libraries:
- Boost Describe
- Magic Enum
- Wise Enum
- C++26 reflection
To enable the integration, you must define the relevant INDEXED_ARRAY_HAS_[DESCRIBE|MAGIC_ENUM|WISE_ENUM|REFLECTION] macro
when compiling.
The rationale for writing this library is the following:
- avoid the
static_castrepetitions when accessing some array elements via enum values, and avoid arithmetic fiascos when some indexes are not starting at 0. - enforce stricter rules than the C++ default ones for aggregate initialization, to detect more errors at compile time.
- transparently support “gaps” or “holes” in indexing values
Code such as:
auto& val = my_data[static_cast<std::size_t>(index) - 1];Should simply be written as:
auto& val = my_data[index];This makes it clear both what the index is (being a strongly typed enum), the range of acceptable values, and avoids any mistakes like forgetting to apply an offset or incorrectly using a wrongly typed variable as the index.
Non-contiguous indexing is a nice feature if you need to handle special values, or are handling both normative values and manufacturer specific extensions.
The main motivation for safe initialization comes from a project where several enums are generated from a description file in an early stage of the compilation, and later used in the code (for example they give the size of some arrays). Some changes in the source description file may lead to breakage, getting a compile time error for such breakages was thus a requirement.
Multi-dimensional indexing came as a convenience extension, as it can be seen as just a special case of indexing.
The library is header only.
git submodule add https://github.com/Julien-Blanc-tgcm/indexed_array.git
git submodule update --init --recursiveC++20compliant compiler (GCC 9+, Clang 11+, MSVC 2022+)C++23andC++26compiler brings extra features
- Boost (mp11, describe for enum support).
- Boost 1.80+ recommended.
- CMake >= 3.25
To compile tests, just run :
mkdir build
cd build
cmake ../.
make -j
ctestBy default, tests are built and run using boost::describe integration.
To enable C++26 reflection, use instead.
cmake -DUSE_CPP26_REFLECTION ../.Just add the indexed_array/include directory to your include path. For cmake
# include indexed_array base directory
include_directories(indexed_array/include)And in your cpp file
#include <jbc/indexed_array.hpp>Some macros needs to be defined to enable integrations. Only one can be picked at a time :
INDEXED_ARRAY_HAS_DESCRIBE # For Boost Describe
INDEXED_ARRAY_HAS_MAGIC_ENUM # For Magic Enum
INDEXED_ARRAY_HAS_WISE_ENUM # For Wise Enum
INDEXED_ARRAY_HAS_REFLECTION # For C++26 reflection
For cmake:
# Enable enum reflection (choose ONE)
add_definitions(-DINDEXED_ARRAY_HAS_MAGIC_ENUM) # For Magic Enum
# add_definitions(-DINDEXED_ARRAY_HAS_DESCRIBE) # For Boost Describe
# add_definitions(-DINDEXED_ARRAY_HAS_WISE_ENUM) # For Wise Enum
# add_definitions(-DINDEXED_ARRAY_HAS_REFLECTION) # For C++26 reflectionThe integrations can also be enabled directly in code by including the relevant header file instead
jbc/indexed_array/describe.hpp
jbc/indexed_array/magic_enum.hpp
jbc/indexed_array/wise_enum.hpp
jbc/indexed_array/reflection.hpp
- See Online doc
// we want an array of 10 strings, index starting at one
using MyArray = indexed_array<std::string, interval<1, 10>>;
MyArray arr;
arr[1] = "First element of the array";
// this one will be indexed by an enum value
using AnotherArray = indexed_array<std::string, interval<my_enum::first, my_enum::last>>;enum class Color { Red, Green, Blue };
using ColorArray = indexed_array<std::string, interval<Color::Red, Color::Blue>>;
ColorArray colors;
colors[Color::Red] = "Red";requires C++26 or an enum reflection library, see below)
enum class Color { Red, Green, Blue };
using ColorArray = indexed_array<std::string, Color>;
ColorArray colors;
colors[Color::Red] = "Red";
assert(arr.size() == 3); // 3 elementsusing MixedIndexes = union_of<
interval<1, 5>,
single_value<10>,
single_value<20>
>;
using MixedArray = indexed_array<int, MixedIndexes>{};
MixedArray arr;
arr[1] = 100;
arr[10] = 200;
arr[20] = 300;
assert(arr.size() == 7); // 7 elements in array
assert(sizeof(arr) == 7 * sizeof(int)); // no holes, only the required size is usedenum class Permission { Read, Write, Execute };
using PermissionBitset = indexed_bitset<Permission>>;
PermissionBitset perms{};
perms.set(Permission::Read);
if (perms.test(Permission::Write)) { /* ... */ }enum class Color { Red, Green, Blue };
std::array<int, 2> arr{1, 2, 3};
indexed_span<int, Color> sp(arr.data()); // fixed extent, no size parameter
assert(sp.size() == 3);
assert(sp[Color::Green] == 2);enum class Color { Red, Green, Blue};
indexed_array<int, invertal<0, 10>, Color> arr;
arr[5, Green] = 15;
assert(arr.size() == 30);
- Static Indexes Only: All indexes must be known at compile time.
- No Dynamic Resizing: The size of an
indexed_arrayis fixed at compile time. - Performance: For non-contiguous indexes, access by index is no longer O(1). This can be checked at compile time to ensure that there is no overhead.
Distributed under the Boost Software License, Version 1.0.
Contributions are welcome! Please follow these guidelines:
- Code Style: This project uses
clang-format. Runclang-format -i file.hppto format your code. - Tests: Add tests for new features in the
tests/directory. - Documentation: Add doc for new features in the
docs/directory. - Pull Requests: Open a PR against the
mainbranch and ensure all tests pass. - Issues: Report bugs or feature requests via GitHub Issues.