Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

hipony/enumerate

Open more actions menu

Repository files navigation

hipony::enumerate

Language License CI

C++11 compatible version of enumerate.

An adapter for "range-like" (and more) things which provides a range with a value type of struct with index and value members representing respectively the position and the value of the elements of the passed range.

It's partially based on the views::enumerate proposal, but extends its features greately.

#include <hipony/enumerate.hpp>
#include <array>
#include <iostream>

int main() {
    std::array array{0, 1, 2, 3, 4, 5};
    for(auto&& [index, value] : hipony::enumerate(array)) {
        std::cout << index << ' ' << value << '\n';
    }
}

Compiler Explorer

Notes

  • With C++17 structured bindings we can bind member variables to convenient aliases

Features

_as<type> to specify the type of the index to cast to

If the size of a container is bigger than the maximum value for the specified type - the behavior is undefined.

// C++17
#include <hipony/enumerate.hpp>
#include <type_traits>

int main() {
    using hipony::enumerate_as;
    using hipony::as_array;
    for(auto&& [index, value] : enumerate_as<int>(as_array, 0, 1, 2, 3, 4)) {
        static_assert(std::is_same_v<int, decltype(index)>);
    }
}

Compiler Explorer

Ranges

Note, Clang doesn't yet have full support for Concepts and Ranges

When you pass an lvalue expression to enumerate, then the result type will satisfy the std::ranges::view concept, allowing to use the function directly with range adapters.

When you pass an rvalue expression to enumerate, then the result type will satisfy the std::ranges::range, which requires an out of line declaration.

// C++20
#include <hipony/enumerate.hpp>
#include <array>
#include <iostream>
#include <ranges>

int main() {
    auto array = std::array{0, 1, 2, 3, 4, 5};
    for(auto&& [index, value] : hipony::enumerate(array) | std::ranges::views::take(3)) {
        std::cout << index << ' ' << value << '\n';
    }
    for(auto&& [index, value] : array | hipony::enumerate() | std::ranges::views::take(3)) {
        std::cout << index << ' ' << value << '\n';
    }

    auto range = hipony::enumerate(std::array{0, 1, 2, 3, 4, 5});
    for(auto&& [index, value] : range | std::ranges::views::take(3)) {
        std::cout << index << ' ' << value << '\n';
    }
}

Compiler Explorer

Sentinels

Requires C++17

#include <hipony/enumerate.hpp>

#include <iostream>
#include <list>

struct sentinel {
    friend auto operator==(std::list<int>::const_iterator const& it, sentinel) -> bool
    {
        return *it == 3;
    }
};

int main() {

    auto const list = std::list<int>({0, 1, 2, 3, 4});

    using hipony::enumerate;
    for (auto&& [index, value] : enumerate(list.begin(), sentinel())) {
        std::cout << index << ' ' << value << '\n';
    }
}

Compiler Explorer

Constexpr

Requires C++14

On MSVC requires at least VS2019 (v19.20). But works only with value enumeration before v19.28, eg for (auto item : enumerate(as_array, 0, 1, 2, 3, 4)), but not anything that references anything.

// C++20
#include <hipony/enumerate.hpp>

consteval auto get() -> int
{
    using hipony::enumerate;
    using hipony::as_array;
    for (auto&& [index, value] : enumerate(as_array, 0, 1, 2, 3, 4)) {
        if (index == 4) {
            return value;
        }
    }
    return 0;
}
// C++14
#include <hipony/enumerate.hpp>

struct function_object {
    constexpr auto operator()() const -> int
    {
        using hipony::enumerate;
        using hipony::as_array;
        for (auto item : enumerate(as_array, 0, 10, 20, 30, 40)) {
            if (item.index == 4) {
                return item.value;
            }
        }
        return 0;
    }
};

Compiler Explorer

Tuples

For tuples we use a special member function .each. The library checks for a tuple protocol with std::tuple_size specialized, so practically a user should be able to pass a custom tuple. You also can directly invoke the overload with a special as_tuple tag.

// C++14
#include <hipony/enumerate.hpp>

#include <tuple>
#include <iostream>

int main() {
    using hipony::enumerate;
    std::tuple tuple = {0, 1., "string"};
    enumerate(tuple).each([](auto index, auto& value) {
        std::cout << value << '\n';
    });
}
// C++14
#include <hipony/enumerate.hpp>

#include <iostream>

int main() {
    using hipony::enumerate;
    using hipony::as_tuple;
    enumerate(as_tuple, 0, 1., "string").each([](auto index, auto& value) {
        std::cout << value << '\n';
    });
}
// C++11
#include <hipony/enumerate.hpp>

#include <iostream>

struct function_object {
    template<typename Size, typename T>
    void operator()(Size index, T& value) {
        std::cout << index << ' ' << value << '\n';
    }
};

// or
// struct function_object {
//     void operator()(std::size_t index, int& value) {
//         std::cout << index << ' ' << value << '\n';
//     }
//     void operator()(std::size_t index, double& value) {
//         std::cout << index << ' ' << value << '\n';
//     }
//     void operator()(std::size_t index, char const(&value)[7]) {
//         std::cout << index << ' ' << value << '\n';
//     }
// };

int main() {
    using hipony::enumerate;
    using hipony::as_tuple;
    enumerate(as_tuple, 0, 1., "string").each(function_object{});
}

#include+ int+main()+{ ++++std::tuple+tuple+=+{0,+1.,+"string"}; ++++hipony::enumerate(tuple).each([](auto+index,+auto&+value)+{ ++++++++std::cout+<<+index+<<+!'+!'+<<+value+<<+!'\n!'; ++++}); } '),l:'5',n:'0',o:'C+++source+#1',t:'0')),k:53.14904865377949,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'1',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!((name:fmt,ver:trunk)),options:'-O3+-std=c++2a+-Wall+',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+gcc+(trunk)+(Editor+#1,+Compiler+#1)+C++',t:'0')),k:47.86035014023761,l:'4',m:70.68408643131683,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:1,editor:1,fontScale:10,fontUsePx:'0',wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+(trunk)+(Compiler+#1)',t:'0')),header:(),l:'4',m:29.31591356868317,n:'0',o:'',s:0,t:'0')),k:46.85095134622051,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow">Compiler Explorer

[Optional] Simple Aggregates via boost/pfr

Requires C++17

To enable the integration, use the HIPONY_ENUMERATE_AGGREGATES_ENABLED CMake option.

#include <hipony/enumerate.hpp>

#include <string>
#include <iostream>

struct aggregate_t {
    int         i;
    double      d;
    std::string str;
};

int main() {
    using hipony::enumerate;
    auto aggregate = aggregate_t{0, 1., "2"};
    enumerate(aggregate).each([](auto index, auto&& value) {
        std::cout << index << ' ' << value << '\n';
    });
}

#include+ struct+aggregate_t+{ ++++int+++++++++i; ++++double++++++d; ++++std::string+str; }; int+main()+{ ++++auto+aggregate+=+aggregate_t{0,+1.,+"2"}; ++++hipony::enumerate(aggregate).each([](auto+index,+auto&+value)+{ ++++++++std::cout+<<+index+<<+!'+!'+<<+value+<<+!'\n!'; ++++}); } '),l:'5',n:'0',o:'C+++source+#1',t:'0')),k:53.14904865377949,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'1',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!((name:boost,ver:'176'),(name:fmt,ver:trunk)),options:'-O3+-std=c++2a+-Wall+-DHIPONY_ENUMERATE_AGGREGATES_ENABLED',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+gcc+(trunk)+(Editor+#1,+Compiler+#1)+C++',t:'0')),k:47.86035014023761,l:'4',m:70.68408643131683,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:1,editor:1,fontScale:10,fontUsePx:'0',wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+(trunk)+(Compiler+#1)',t:'0')),header:(),l:'4',m:29.31591356868317,n:'0',o:'',s:0,t:'0')),k:46.85095134622051,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow">Compiler Explorer

The project uses Conan-provided config files for the pfr for dev purposes, but in practice a user only need to make sure that the header is visible when the option is enabled.

Containers

#include <hipony/enumerate.hpp>

#include <array>
#include <iostream>

int main() {
    using hipony::enumerate;
    auto array = std::array{1, 2, 3, 4, 5};
    for (auto&& [index, value] : enumerate(array)) {
        std::cout << value << '\n';
    }
}
#include <hipony/enumerate.hpp>

#include <vector>
#include <iostream>

int main() {
    using hipony::enumerate;
    for (auto&& item : enumerate(std::vector<int>{1, 2, 3, 4, 5})) {
        std::cout << item.index << '\n';
        std::cout << item.value << '\n';
    }
}

Compiler Explorer

Container + Size

If the size is a negative number - the behavior is undefined. If the size is bigger than the container.size() - enumeration will end at the container.size()

#include <hipony/enumerate.hpp>

#include <array>
#include <iostream>

int main() {
    using hipony::enumerate;
    auto array = std::array{1, 2, 3, 4, 5};
    for (auto&& [index, value] : enumerate(array, 3u)) {
        std::cout << value << '\n';
    }
}

Note that we compare with std::ranges instead of an alternative C-style loop implementation since we're relying on range designs .

Compiler Explorer

Begin + End

If the End iterator is not reachable from the Begin iterator - the behavior is undefined.

#include <hipony/enumerate.hpp>

#include <list>
#include <iostream>

int main() {
    using hipony::enumerate;
    auto list = std::list{1, 2, 3, 4, 5};
    for (auto&& [index, value] : enumerate(list.begin(), list.end())) {
        std::cout << value << '\n';
    }
}

Compiler Explorer

Const Propagation

#include <hipony/enumerate.hpp>

#include <vector>
#include <iostream>
#include <type_traits>

int main() {
    using hipony::enumerate;
    auto const vec = std::vector<int>{1, 2, 3, 4, 5};
    for (auto&& item : enumerate(vec)) {
        static_assert(
            std::is_same<int const&, decltype(item.value)>::value);
        std::cout << item.index << '\n';
        std::cout << item.value << '\n';
    }
}

#include+ auto&+function(std::list+const&+list)+{ ++++for(auto&&+[index,+value]+:+hipony::enumerate(list.begin(),+list.end()))+{ ++++++++static_assert( ++++++++++++std::is_same_v); ++++} ++++return+list; } '),l:'5',n:'0',o:'C+++source+#1',t:'0')),k:58.582415556978006,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'1',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!((name:fmt,ver:trunk)),options:'-O3+-std=c++2a+-Wall+-DNDEBUG',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+gcc+(trunk)+(Editor+#1,+Compiler+#1)+C++',t:'0')),k:47.86035014023761,l:'4',m:70.68408643131683,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:1,editor:1,fontScale:10,fontUsePx:'0',wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+(trunk)+(Compiler+#1)',t:'0')),header:(),l:'4',m:29.31591356868317,n:'0',o:'',s:0,t:'0')),k:41.417584443022,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow">Compiler Explorer

C-Arrays

#include <hipony/enumerate.hpp>

#include <iostream>

int main() {
    using hipony::enumerate;
    int container[] = {0, 1, 2, 3, 4};
    for (auto&& [index, value] : enumerate(container)) {
        std::cout << index << ' ' << value << '\n';
    }
}

int+main()+{ ++++int+container[]+=+{0,+1,+2,+3,+4}; ++++for(auto&&+[index,+value]+:+hipony::enumerate(container))+{ ++++++++std::cout+<<+index+<<+!'+!'+<<+value+<<+!'\n!'; ++++} } '),l:'5',n:'0',o:'C+++source+#1',t:'0')),k:58.582415556978006,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'1',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!((name:fmt,ver:trunk)),options:'-O3+-std=c++2a+-Wall+-DNDEBUG',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+gcc+(trunk)+(Editor+#1,+Compiler+#1)+C++',t:'0')),k:47.86035014023761,l:'4',m:70.68408643131683,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:1,editor:1,fontScale:10,fontUsePx:'0',wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+(trunk)+(Compiler+#1)',t:'0')),header:(),l:'4',m:29.31591356868317,n:'0',o:'',s:0,t:'0')),k:41.417584443022,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow">Compiler Explorer

C-Strings (null-terminated)

#include <hipony/enumerate.hpp>

#include <iostream>

int main() {
    using hipony::enumerate;
    for (auto&& item : enumerate("01234")) {
        std::cout << item.index << ' ';
        std::cout << item.value << '\n';
    }
}

int+main()+{ ++++for(auto&&+[index,+value]+:+hipony::enumerate("01234"))+{ ++++++++std::cout+<<+index+<<+!'+!'+<<+value+<<+!'\n!'; ++++} } '),l:'5',n:'0',o:'C+++source+#1',t:'0')),k:58.582415556978006,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'1',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!((name:fmt,ver:trunk)),options:'-O3+-std=c++2a+-Wall+-DNDEBUG',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+gcc+(trunk)+(Editor+#1,+Compiler+#1)+C++',t:'0')),k:47.86035014023761,l:'4',m:70.68408643131683,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:1,editor:1,fontScale:10,fontUsePx:'0',wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+(trunk)+(Compiler+#1)',t:'0')),header:(),l:'4',m:29.31591356868317,n:'0',o:'',s:0,t:'0')),k:41.417584443022,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow">Compiler Explorer

Pointers + Size

If the size is a negative number - the behavior is undefined.

#include <hipony/enumerate.hpp>

#include <iostream>

int main() {
    using hipony::enumerate;
    int        ptr[] = {0, 1, 2, 3, 4};
    auto const size = 3u;
    for (auto&& item : enumerate(&ptr[0], size)) {
        std::cout << item.index << '\n';
        std::cout << item.value << '\n';
    }
}

int+main()+{ ++++using+hipony::enumerate; ++++int++++++++ptr[]+=+{0,+1,+2,+3,+4}; ++++auto+const+size+=+3u; ++++for+(auto&&+[index,+value]+:+enumerate(&ptr[0],+size))+{ ++++++++std::cout+<<+index+<<+!'+!'+<<+value+<<+!'\n!'; ++++} } '),l:'5',n:'0',o:'C+++source+#1',t:'0')),k:58.582415556978006,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'1',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!((name:fmt,ver:trunk)),options:'-O3+-std=c++2a+-Wall+-DNDEBUG',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+gcc+(trunk)+(Editor+#1,+Compiler+#1)+C++',t:'0')),k:47.86035014023761,l:'4',m:70.68408643131683,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:1,editor:1,fontScale:10,fontUsePx:'0',wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+(trunk)+(Compiler+#1)',t:'0')),header:(),l:'4',m:29.31591356868317,n:'0',o:'',s:0,t:'0')),k:41.417584443022,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow">Compiler Explorer

Installation

Package Managers

Manual

It's a single-file header-only library, so you can put the hipony/enumerate.hpp in the include folder directly into the project tree.

Alternatively, project provides CMake instructions for usage with add_subdirectory or cmake install.

find_package(hipony-enumerate)
target_link_libraries(app PRIVATE hipony::enumerate)

Compatibility

The library provides the HIPONY_ENUMERATE_NAMESPACE macro to specify a different from the default hipony namespace. It encapsulates all the internals in the hipony_enumerate namespace to avoid accidental ODR conflicts by changing the external namespace.

Additionally, library uses tag types as_array_tag_t and as_tuple_tag_t in the interface. For potential reuse in other libraries in the hipony namespace or related, they are encapsulated by the HIPONY_AS_ARRAY_HPP_INCLUDED/HIPONY_AS_TUPLE_HPP_INCLUDED guards with additional flags HIPONY_ENUMERATE_AS_ARRAY_ENABLED/HIPONY_ENUMERATE_AS_TUPLE_ENABLED to force the declaration of the types.

Contributing

Project provides a conanfile.txt to pull in dependencies for testing, but uses a transparent integration otherwise. If used with another package manager - one should make sure the directory with config files is visible for find_package.

References

License

BSL-1.0

Morty Proxy This is a proxified and sanitized view of the page, visit original site.