diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 6f3de41458..126e428c30 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -720,14 +720,49 @@ using std::make_index_sequence; #else template struct index_sequence {}; -template -struct make_index_sequence_impl : make_index_sequence_impl {}; -template -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 // N_digit_is_1=false +struct make_index_sequence_impl + : make_index_sequence_impl { +}; +template +struct make_index_sequence_impl + : make_index_sequence_impl {}; +template +struct make_index_sequence_impl<0, false, N, end, S...> { using type = index_sequence; }; +constexpr size_t next_power_of_2(size_t N) { return N == 0 ? 1 : next_power_of_2(N >> 1) << 1; } template -using make_index_sequence = typename make_index_sequence_impl::type; +using make_index_sequence = + typename make_index_sequence_impl::type; #endif /// Make an index sequence of the indices of true arguments