Skip to content

Instantly share code, notes, and snippets.

@furrz
Last active June 4, 2025 20:24
Show Gist options
  • Select an option

  • Save furrz/ca4a657fd5de9f973fc9b55ddaae7412 to your computer and use it in GitHub Desktop.

Select an option

Save furrz/ca4a657fd5de9f973fc9b55ddaae7412 to your computer and use it in GitHub Desktop.
multi_vector - A little C++ SoA container
#pragma once
#include <tuple>
#include <vector>
/** multi_vector
* The beginnings of a structure-of-arrays vector.
* (Currently append-only.
* Just makes iteration and appending friendlier and less error-prone.)
* @author PrinceZyntaks
* @date 2025-06-04
*/
template<typename ...Types>
struct multi_vector {
using tuple_type = std::tuple<std::vector<Types> ...>;
using tuple_size = std::tuple_size<tuple_type>;
static constexpr auto index_sequence =
std::make_index_sequence<tuple_size::value>{};
template<bool Const>
struct iterator_impl {
using iterator_category = std::forward_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = std::conditional_t<Const,
std::tuple<const Types&...>,
std::tuple<Types&...>>;
using self = iterator_impl<Const>;
tuple_type& container;
size_t index;
template<size_t... Indices>
[[nodiscard]] value_type construct_tuple_impl(std::index_sequence<Indices...>) const {
return std::make_tuple(std::ref(std::get<Indices>(container)[index])...);
}
[[nodiscard]] value_type operator*() const {
constexpr size_t tuple_size = tuple_size::value;
return construct_tuple_impl(index_sequence);
}
self& operator++() { index++; return *this; }
self operator++(int) { iterator tmp = *this; ++(*this); return tmp; }
friend bool operator== (const self& a, const self& b) { return a.index == b.index && &a.container == &b.container; }
friend bool operator!=(const self& a, const self& b) { return !(a == b); }
};
using iterator = iterator_impl<false>;
using const_iterator = iterator_impl<true>;
size_t size() const { return std::get<0>(vectors).size(); }
void emplace_back(const Types& ...values) {
constexpr size_t tuple_size = tuple_size::value;
emplace_back_impl(values..., index_sequence);
}
[[nodiscard]] iterator begin() { return { vectors, 0 }; }
[[nodiscard]] iterator end() { return { vectors, size() }; }
[[nodiscard]] const_iterator cbegin() const { return { vectors, 0 }; }
[[nodiscard]] const_iterator cend() const { return { vectors, size() }; }
private:
template<size_t... Indices>
void emplace_back_impl(Types ...values, std::index_sequence<Indices...>) {
(void) std::initializer_list {
(std::get<Indices>(vectors).emplace_back(values), 0)...
};
}
std::tuple<std::vector<Types> ...> vectors;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment