Skip to content

Commit 49d19fe

Browse files
Implement binary version of make_index_sequence (#5751)
* Implement binary version of make_index_sequence * style: pre-commit fixes * typo --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 6aeae9c commit 49d19fe

File tree

1 file changed

+40
-5
lines changed

1 file changed

+40
-5
lines changed

include/pybind11/detail/common.h

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -720,14 +720,49 @@ using std::make_index_sequence;
720720
#else
721721
template <size_t...>
722722
struct index_sequence {};
723-
template <size_t N, size_t... S>
724-
struct make_index_sequence_impl : make_index_sequence_impl<N - 1, N - 1, S...> {};
725-
template <size_t... S>
726-
struct make_index_sequence_impl<0, S...> {
723+
// Comments about the algorithm below.
724+
//
725+
// Credit: This is based on an algorithm by taocpp here:
726+
// https://github.com/taocpp/sequences/blob/main/include/tao/seq/make_integer_sequence.hpp
727+
// but significantly simplified.
728+
//
729+
// We build up a sequence S by repeatedly doubling its length and sometimes adding 1 to the end.
730+
// E.g. if the current S is 0...3, then we either go to 0...7 or 0...8 on the next pass.
731+
// The goal is to end with S = 0...N-1.
732+
// The key insight is that the times we need to add an additional digit to S correspond
733+
// exactly to the 1's in the binary representation of the number N.
734+
//
735+
// Invariants:
736+
// - digit is a power of 2
737+
// - N_digit_is_1 is whether N's binary representation has a 1 in that digit's position.
738+
// - end <= N
739+
// - S is 0...end-1.
740+
// - if digit > 0, end * digit * 2 <= N < (end+1) * digit * 2
741+
//
742+
// The process starts with digit > N, end = 0, and S is empty.
743+
// The process concludes with digit=0, in which case, end == N and S is 0...N-1.
744+
745+
template <size_t digit, bool N_digit_is_1, size_t N, size_t end, size_t... S> // N_digit_is_1=false
746+
struct make_index_sequence_impl
747+
: make_index_sequence_impl<digit / 2, (N & (digit / 2)) != 0, N, 2 * end, S..., (S + end)...> {
748+
};
749+
template <size_t digit, size_t N, size_t end, size_t... S>
750+
struct make_index_sequence_impl<digit, true, N, end, S...>
751+
: make_index_sequence_impl<digit / 2,
752+
(N & (digit / 2)) != 0,
753+
N,
754+
2 * end + 1,
755+
S...,
756+
(S + end)...,
757+
2 * end> {};
758+
template <size_t N, size_t end, size_t... S>
759+
struct make_index_sequence_impl<0, false, N, end, S...> {
727760
using type = index_sequence<S...>;
728761
};
762+
constexpr size_t next_power_of_2(size_t N) { return N == 0 ? 1 : next_power_of_2(N >> 1) << 1; }
729763
template <size_t N>
730-
using make_index_sequence = typename make_index_sequence_impl<N>::type;
764+
using make_index_sequence =
765+
typename make_index_sequence_impl<next_power_of_2(N), false, N, 0>::type;
731766
#endif
732767

733768
/// Make an index sequence of the indices of true arguments

0 commit comments

Comments
 (0)