Skip to content

Header-only, single file, C++11 compatible version of `enumerate`.

License

Notifications You must be signed in to change notification settings

hipony/enumerate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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{});
}

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';
    });
}

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';
    }
}

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';
    }
}

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';
    }
}

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';
    }
}

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