Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions universal/include/userver/utils/enumerate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,22 @@ struct IteratorWrapper {
}
};

template <typename Range>
using IteratorTypeOf = decltype(std::begin(std::declval<Range&>()));

template <typename Range>
using SentinelTypeOf = decltype(std::end(std::declval<Range&>()));

template <typename Container>
struct ContainerWrapper {
constexpr IteratorWrapper<IteratorTypeOf<Container>> begin() {
return {.iterator = std::begin(container), .pos = 0};
constexpr auto begin() {
return IteratorWrapper{.iterator = std::begin(container), .pos = 0};
}

constexpr IteratorWrapper<SentinelTypeOf<Container>> end() { return {.iterator = std::end(container), .pos = 0}; }
constexpr auto end() {
return IteratorWrapper{.iterator = std::end(container), .pos = 0};
}

constexpr IteratorWrapper<IteratorTypeOf<const Container>> begin() const {
return {.iterator = std::begin(container), .pos = 0};
constexpr auto begin() const {
return IteratorWrapper{.iterator = std::begin(std::as_const(container)), .pos = 0};
}

constexpr IteratorWrapper<SentinelTypeOf<const Container>> end() const {
return {.iterator = std::end(container), .pos = 0};
constexpr auto end() const {
return IteratorWrapper{.iterator = std::end(std::as_const(container)), .pos = 0};
}

Container container;
Expand All @@ -81,9 +77,9 @@ namespace utils {
/// @brief Implementation of python-style enumerate function for range-for loops
/// @param iterable: Container to iterate
/// @returns ContainerWrapper, which iterator after dereference returns pair
/// of index and (!!!)non-const reference to element(it seems impossible to make
/// this reference const). It can be used in "range based for loop" with
/// "structured binding" like this
/// of index and reference to element. The reference is const-qualified if either
/// the wrapper itself or the underlying container is const; otherwise, it is non-const.
/// It can be used in "range based for loop" with "structured binding" like this
Copy link
Copy Markdown
Member

@apolukhin apolukhin Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, add tests for the functionality

Copy link
Copy Markdown
Contributor Author

@ddvamp ddvamp Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to write compile-time tests correctly?

Are targeted changes and side changes (refactoring, removing/adding headers, removing obsolete code, etc.) allowed in one PR?

Is there documentation on formatting, as well as on the rules for using headers? I've seen names used without their corresponding headers. There are also unused headers.

/// @code
/// for (auto [pos, elem] : enumerate(someContainer)) {...}
/// @endcode
Expand Down
27 changes: 27 additions & 0 deletions universal/src/utils/enumerate_test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <array>
#include <type_traits>
#include <utility>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
Expand All @@ -9,6 +11,31 @@ USERVER_NAMESPACE_BEGIN

namespace {

template <typename Container>
using EnumerateImpl = decltype(utils::enumerate(std::declval<Container>()));

template <typename Container, bool ConstWrapper>
using Enumerate = std::conditional_t<ConstWrapper, const EnumerateImpl<Container>, EnumerateImpl<Container>>;

template <typename Container, bool ConstWrapper>
using EnumerateElem = decltype(*std::begin(std::declval<Enumerate<Container, ConstWrapper>&>()))::second_type;

static_assert(std::is_same_v<int&, EnumerateElem<std::vector<int>, false>>);
static_assert(std::is_same_v<int&, EnumerateElem<std::vector<int>&, false>>);
static_assert(std::is_same_v<int&, EnumerateElem<std::vector<int>&&, false>>);

static_assert(std::is_same_v<const int&, EnumerateElem<const std::vector<int>, false>>);
static_assert(std::is_same_v<const int&, EnumerateElem<const std::vector<int>&, false>>);
static_assert(std::is_same_v<const int&, EnumerateElem<const std::vector<int>&&, false>>);

static_assert(std::is_same_v<const int&, EnumerateElem<std::vector<int>, true>>);
static_assert(std::is_same_v<const int&, EnumerateElem<std::vector<int>&, true>>);
static_assert(std::is_same_v<const int&, EnumerateElem<std::vector<int>&&, true>>);

static_assert(std::is_same_v<const int&, EnumerateElem<const std::vector<int>, true>>);
static_assert(std::is_same_v<const int&, EnumerateElem<const std::vector<int>&, true>>);
static_assert(std::is_same_v<const int&, EnumerateElem<const std::vector<int>&&, true>>);

constexpr int ConstexprTest(std::array<int, 2> data) {
int result = 0;
for (auto [pos, elem] : utils::enumerate(data)) {
Expand Down
Loading