{"id":21498492,"url":"https://github.com/notapenguin0/multi_container","last_synced_at":"2025-07-09T09:10:19.617Z","repository":{"id":117727807,"uuid":"150791771","full_name":"NotAPenguin0/multi_container","owner":"NotAPenguin0","description":"Container capable of storing and manipulating multiple different containers","archived":false,"fork":false,"pushed_at":"2018-10-06T13:17:36.000Z","size":37,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-23T22:24:48.331Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NotAPenguin0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-28T20:33:08.000Z","updated_at":"2018-10-06T13:17:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"fc2aa22f-2af1-464f-a35c-02f3995e0111","html_url":"https://github.com/NotAPenguin0/multi_container","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fmulti_container","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fmulti_container/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fmulti_container/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fmulti_container/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NotAPenguin0","download_url":"https://codeload.github.com/NotAPenguin0/multi_container/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244036760,"owners_count":20387570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-23T16:39:08.055Z","updated_at":"2025-03-17T12:42:39.479Z","avatar_url":"https://github.com/NotAPenguin0.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# multi_container\n\nContainer that is capable of storing and iterating over any number of other containers. Another name would be `tied_container`, that's why I included an alias for that. It also features a `multi_iterator` class that is used for iterating over it, and works much like a `zip_iterator`. One important note is that `multi_container` owns the containers it stores, and copies the containers given to it in the constructor.\n\n# Examples\n\nWe'll start by declaring some containers to use in the following examples.\n\n```cpp\nstd::vector\u003cint\u003e vi { 0, 1, -1, 2, -2 };\nstd::vector\u003cfloat\u003e vf {11.0f, 12.0f, 13.0f, 14.0f, 15.0f};\nstd::array\u003clong long, 5\u003e all = { 1, 2, 3, 4, 5 };\n```\n\nNow, we declare our `multi_container`. Luckily we have type deduction from the constructor, so there is no need to specify all container types manually:\n\n```cpp\nmvg::multi_container m(vi, vf, all);\n```\n\n***Structured bindings***\n\n```cpp\nfor(auto[a, b, c] : m)\n{\n  //use a, b and c\n}\n```\n\nThis does require some explanation, because you can probably see something with this: There is no `auto\u0026`, just `auto`. Still, `a`, `b` and `c` are references to the elements. This is because under the hood, the `multi_iterator` used for iterating over the `multi_container` uses a wrapper around `std::tuple` for references to return when dereferencing. So even when copying the result of the dereference, you keep an iterator. This does have the downside that you can't explicitely add `const`, e.g\n\n```cpp\nfor(auto const[a, b, c] : m)\n{\n\n}\n```\n\nWill not give const references to `a`, `b` and `c`. Using structured bindings on a `const multi_container`, will still give const references though.\n\n***STL Algorithms***\n\n`multi_container` is fully compatible with most/all STL algorithms. For example:\n```cpp\n\nmvg::multi_container m(vi, vf, all); //See the containers above\n\nauto it = std::find(m.begin(), m.end(), std::make_tuple(1, 2.0f, 5ll));\nif (it == m.end())\n{\n    std::cout \u003c\u003c \"Element not found\";\n}\n\nm.erase(std::remove_if(m.begin(), m.end(),\n            [](auto elem)\n            {\n                return std::get\u003c0\u003e(elem) \u003c= 3;\n            }), m.end());\n```\n\nThese are just 2 examples of standard algorithms that works smoothly with `multi_container`. There is one special case, and that is when you start using an algorithm that compares elements, like `std::sort`. As there is no good way of ordering when comparing with `operator\u003c`, `operator\u003e`, or any other relational comparison, `multi_container` uses the result of the comparison of the first 2 elements. This has an interesting side effect, that when sorting a `multi_container`, the first container will be sorted, and all others will be sorted ***in the order of the first one***. Example:\n\n```cpp\nmvg::multi_container m(vi, vf, all);\nstd::sort(m.begin(), m.end());\n```\nSince `vi`, the first container, looks like this: `{ 0, 1, -1, 2, -2 }`, It will be sorted to: `{-2, -1, 0, 1, 2}`. Containers `vf` and `all` will be sorted in the same order. A way to visualize this is that `vi` specifies the indices of the elements in the sorted vector.\n`\n\n\n***Other features***\n\nBelow you can find a complete list of all member types and methods.\n\n# Member types\n\n| member type              | definition                                                                 |\n|--------------------------|----------------------------------------------------------------------------|\n| `iterator `              | `multi_iterator\u003ctypename detail::underlying_iterator\u003cTs\u003e::type ...\u003e`       |\n| `const_iterator`         | `multi_iterator\u003ctypename detail::underlying_const_iterator\u003cTs\u003e::type ...\u003e` |\n| `reverse_iterator `      | `std::reverse_iterator\u003citerator\u003e`                                          |\n| `const_reverse_iterator` | `std::reverse_iterator\u003cconst_iterator\u003e `                                   |\n| `value_type`             | `detail::tuple_wrapper\u003cTs...\u003e `                                            |\n| `reference`              | `detail::tuple_wrapper\u003cstd::add_lvalue_reference_t\u003cTs\u003e ...\u003e`               |\n| `pointer`                | `std::add_pointer_t\u003cvalue_type\u003e`                                           |\n| `size_type`              | `std::size_t`                                                              |\n| `difference_type`        | `std::ptrdiff_t `                                                          |\n\n# Methods\n\nVery simply said, `multi_container` supports nearly all operations `std::vector` supports, except a few. Also, if any of the containers is for example an `std::array`, calling `push_back` on the `multi_container` will trigger a compiler error. Below is a list of all operations allowed on `multi_container`, assuming the containers it holds support those operations too.\n\n- ***Iterators***\n  - `iterator begin()` returns an iterator to the beginning of the sequence\n  - `iterator end()` returns an iterator to the element past the end of the sequence\n  - `const_iterator begin() const` is a `const` version of `begin()`\n  - `const_iterator end() const` is a `const` version of `end()`\n  - `const_iterator cbegin() const` returns a constant iterator to the beginning of the sequence\n  - `const_iterator cend() const` returns a constant iterator to the element past the end of the sequence\n  - `reverse_iterator rbegin()` returns a reverse iterator to the beginning of the sequence\n  - `reverse_iterator rend()` returns a reverse iterator to the element past the end of the sequence\n  - `const_reverse_iterator crbegin() const` returns a const reverse iterator to the beginning of the sequence\n  - `const_reverse_iterator crend() const` returns a const reverse iterator to the element past the end of the sequence\n- ***Access to underlying containers***\n  - `std::tuple\u003cTs...\u003e\u0026 data()` access the underlying tuple storing the containers\n  - `std::tuple\u003cTs...\u003e const\u0026 data() const` access the underlying tuple storing the containers\n  - `template\u003cclass C\u003e C\u0026 get_container()` returns the container with specified type\n  - `template\u003cclass C\u003e C const\u0026 get_container() const` returns the container with specified type\n  - `templace\u003csize_t I\u003e tuple_element_t\u003cI, tuple\u003cTs...\u003e\u003e\u0026 get_container()` returns the container at specified index\n  - `templace\u003csize_t I\u003e tuple_element_t\u003cI, tuple\u003cTs...\u003e\u003e const\u0026 get_container() const` returns the container at specified index\n- ***Element access***\n  - `detail::tuple_wrapper\u003cTs\u0026 ...\u003e operator[](std::size_t index)` returns a tuple containing references to the elements of all containers at the specified index. Does not perform bounds checking.\n  - `detail::tuple_wrapper\u003cTs const\u0026 ...\u003e operator[](std::size_t index) const` returns a tuple containing references to the elements of all containers at the specified index. Does not perform bounds checking.\n  - `detail::tuple_wrapper\u003cTs\u0026 ...\u003e at(std::size_t index)` returns a tuple containing references to the elements of all containers at the specified index. Throws an instance of `std::out_of_range` when `index \u003e= size()`.\n  - `detail::tuple_wrapper\u003cTs const\u0026 ...\u003e at(std::size_t index) const` returns a tuple containing const references to the elements of all containers at the specified index. Throws an instance of `std::out_of_range` when `index \u003e= size()`.\n  - `detail::tuple_wrapper\u003cTs\u0026 ...\u003e front()` returns a tuple containing references to the first element of every container. Same as calling `*(m.begin())`.\n  - `detail::tuple_wrapper\u003cTs const\u0026 ...\u003e front() const` returns a tuple containing const references to the first element of every container. Same as calling `*(m.begin())`.\n  - `detail::tuple_wrapper\u003cTs\u0026 ...\u003e back()` returns a tuple containing references to the last element of every container. Same as calling `*(m.end() - 1)`.\n  - `detail::tuple_wrapper\u003cTs const\u0026 ...\u003e back() const` returns a tuple containing references to the last element of every container. Same as calling `*(m.end() - 1)`.\n- ***Capacity***\n  - `bool empty() const` returns `true` if all containers stored are empty\n  - `size_type size() const` returns the size of the ***smallest container***.\n- ***Modifiers***\n  - `void clear()` clears all stored containers\n  - `template\u003cclass... Elems\u003e void push_back(std::tuple\u003cElems...\u003e const\u0026 elems)` Appends elements at the end of every container\n  - `template\u003cclass... Elems\u003e iterator insert(iterator pos, std::tuple\u003cElems...\u003e const\u0026 elems` Inserts elements before `pos`\n  - `template\u003cclass... Elems\u003e iterator insert(const_iterator pos, std::tuple\u003cElems...\u003e const\u0026 elems)` Inserts elements before `pos`\n  - `template\u003cclass InputIt\u003e iterator insert(iterator pos, InputIt first, InputIt last)` Inserts elements in the range `[first, last[`. `InputIt` must be a `multi_iterator` matching this `multi_container`. The behavior is undefined when `[first, last[` is not a valid range.\n  - `template\u003cclass... Elems\u003e iterator insert(iterator pos, size_type count, std::tuple\u003cElems...\u003e const\u0026 elems)` Inserts `count` copies of `elems` before `pos`\n  - `iterator erase(iterator pos)` Erases element at `pos`.\n  - `iterator erase(const_iterator pos` Erases element at `pos`.\n  - `iterator erase(iterator first, iterator last)` Erases elements in the range `[first, last[`. The behavior in undefined when `[first, last[` is not a valid range.\n  - `iterator erase(const_iterator first, const_iterator last)` Erases elements in the range `[first, last[`. The behavior in undefined when `[first, last[` is not a valid range.\n  - `void pop_back()` removes the last element from the container\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotapenguin0%2Fmulti_container","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotapenguin0%2Fmulti_container","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotapenguin0%2Fmulti_container/lists"}