Skip to content

Implement binary version of make_index_sequence #5751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
45 changes: 40 additions & 5 deletions include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -720,14 +720,49 @@ using std::make_index_sequence;
#else
template <size_t...>
struct index_sequence {};
template <size_t N, size_t... S>
struct make_index_sequence_impl : make_index_sequence_impl<N - 1, N - 1, S...> {};
template <size_t... S>
struct make_index_sequence_impl<0, S...> {
// Comments about the algorithm below.
//
// Credit: This is based on an algorithm by taocpp here:
// https://github.com/taocpp/sequences/blob/main/include/tao/seq/make_integer_sequence.hpp
// but significantly simplified.
//
// We build up a sequence S by repeatedly doubling its length and sometimes adding 1 to the end.
// E.g. if the current S is 0...3, then we either go to 0...7 or 0...8 on the next pass.
// The goal is to end with S = 0...N-1.
// The key insight is that the times we need to add an additional digit to S correspond
// exactly to the 1's in the binary representation of the number N.
//
// Invariants:
// - digit is a power of 2
// - N_digit_is_1 is whether N's binary representation has a 1 in that digit's position.
// - end <= N
// - S is 0...end-1.
// - if digit > 0, end * digit * 2 <= N < (end+1) * digit * 2
//
// The process starts with digit > N, end = 0, and S is empty.
// The process concludes with digit=0, in which case, end == N and S is 0...N-1.

template <size_t digit, bool N_digit_is_1, size_t N, size_t end, size_t... S> // N_digit_is_1=false
struct make_index_sequence_impl
: make_index_sequence_impl<digit / 2, (N & (digit / 2)) != 0, N, 2 * end, S..., (S + end)...> {
};
template <size_t digit, size_t N, size_t end, size_t... S>
struct make_index_sequence_impl<digit, true, N, end, S...>
: make_index_sequence_impl<digit / 2,
(N & (digit / 2)) != 0,
N,
2 * end + 1,
S...,
(S + end)...,
2 * end> {};
template <size_t N, size_t end, size_t... S>
struct make_index_sequence_impl<0, false, N, end, S...> {
using type = index_sequence<S...>;
};
constexpr size_t next_power_of_2(size_t N) { return N == 0 ? 1 : next_power_of_2(N >> 1) << 1; }
template <size_t N>
using make_index_sequence = typename make_index_sequence_impl<N>::type;
using make_index_sequence =
typename make_index_sequence_impl<next_power_of_2(N), false, N, 0>::type;
#endif

/// Make an index sequence of the indices of true arguments
Expand Down
Loading