代码之家  ›  专栏  ›  技术社区  ›  Chen Li

如何理解libcxx的make_integer_序列的实现?

  •  3
  • Chen Li  · 技术社区  · 7 年前

    1. https://github.com/llvm-mirror/libcxx/commit/42e55e932e173eb224997fe11f0d15a1d74b29dc
    2. https://github.com/llvm-mirror/libcxx/commit/a3ccd96ede26a2f383328234e01eb7a9f870691e

    前面的uuu make u tuple u index实现导致了O(N)个实例化 而且效率很低。C++14 uu生成u整数u序列的实现 richard smith提供的一个非常好的Log8(N)实现。

    由于libc++无法在C++11中公开名称“integer_sequence”,因此此修补程序 还引入了生成时使用的伪类型“\uuuu integer\u sequence” 顺序。生成的序列中的一个“\u整数\u序列”可以是 转换为所需类型;“元组索引”或“整数序列”。


    从提交中,我知道这是一个Log8(N)实现,它手动展开循环(如果不正确,请纠正我,thx)。但我不明白怎么做 namespace detail 合作 __integer_sequence . 我尝试过使用调试器,但它总是使用 __has_builtin(__make_integer_seq) branch .


    所以,请帮助我理解这个实现,主要代码在 this commit this part of <utility> :

    // <utility>
        template<typename _Tp, _Tp _Np> using __make_integer_sequence_unchecked =
      typename __detail::__make<_Np>::type::template __convert<integer_sequence, _Tp>;
    
    template <class _Tp, _Tp _Ep>
    struct __make_integer_sequence_checked
    {
        static_assert(is_integral<_Tp>::value,
                      "std::make_integer_sequence can only be instantiated with an integral type" );
        static_assert(0 <= _Ep, "std::make_integer_sequence must have a non-negative sequence length");
        // Workaround GCC bug by preventing bad installations when 0 <= _Ep
        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68929
        typedef __make_integer_sequence_unchecked<_Tp, 0 <= _Ep ? _Ep : 0> type;
    };
    
    template <class _Tp, _Tp _Ep>
    using __make_integer_sequence = typename __make_integer_sequence_checked<_Tp, _Ep>::type;
    

    // <__tuple>
    
    template <class _IdxType, _IdxType... _Values>
    struct __integer_sequence {
      template <template <class _OIdxType, _OIdxType...> class _ToIndexSeq, class _ToIndexType>
      using __convert = _ToIndexSeq<_ToIndexType, _Values...>;
    
      template <size_t _Sp>
      using __to_tuple_indices = __tuple_indices<(_Values + _Sp)...>;
    };
    
    template<typename _Tp, size_t ..._Extra> struct __repeat;
    template<typename _Tp, _Tp ..._Np, size_t ..._Extra> struct __repeat<__integer_sequence<_Tp, _Np...>, _Extra...> {
      typedef __integer_sequence<_Tp,
                               _Np...,
                               sizeof...(_Np) + _Np...,
                               2 * sizeof...(_Np) + _Np...,
                               3 * sizeof...(_Np) + _Np...,
                               4 * sizeof...(_Np) + _Np...,
                               5 * sizeof...(_Np) + _Np...,
                               6 * sizeof...(_Np) + _Np...,
                               7 * sizeof...(_Np) + _Np...,
                               _Extra...> type;
    };
    
    template<size_t _Np> struct __parity;
    template<size_t _Np> struct __make : __parity<_Np % 8>::template __pmake<_Np> {};
    
    template<> struct __make<0> { typedef __integer_sequence<size_t> type; };
    template<> struct __make<1> { typedef __integer_sequence<size_t, 0> type; };
    template<> struct __make<2> { typedef __integer_sequence<size_t, 0, 1> type; };
    template<> struct __make<3> { typedef __integer_sequence<size_t, 0, 1, 2> type; };
    template<> struct __make<4> { typedef __integer_sequence<size_t, 0, 1, 2, 3> type; };
    template<> struct __make<5> { typedef __integer_sequence<size_t, 0, 1, 2, 3, 4> type; };
    template<> struct __make<6> { typedef __integer_sequence<size_t, 0, 1, 2, 3, 4, 5> type; };
    template<> struct __make<7> { typedef __integer_sequence<size_t, 0, 1, 2, 3, 4, 5, 6> type; };
    
    template<> struct __parity<0> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type> {}; };
    template<> struct __parity<1> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 1> {}; };
    template<> struct __parity<2> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 2, _Np - 1> {}; };
    template<> struct __parity<3> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 3, _Np - 2, _Np - 1> {}; };
    template<> struct __parity<4> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
    template<> struct __parity<5> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
    template<> struct __parity<6> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
    template<> struct __parity<7> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 7, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
    
    } // namespace detail
    

    提前谢谢。

    如果你觉得这个问题太过分了,请告诉我。我很快就会删除,尽管这个问题确实让我很烦恼。

    2 回复  |  直到 7 年前
        1
  •  4
  •   Artyer    7 年前

    你也需要理解 __repeat

    template<typename _Tp, size_t ..._Extra> struct __repeat;
    template<typename _Tp, _Tp ..._Np, size_t ..._Extra> struct __repeat<integer_sequence<_Tp, _Np...>, _Extra...> {
      typedef integer_sequence<_Tp,
                               _Np...,
                               sizeof...(_Np) + _Np...,
                               2 * sizeof...(_Np) + _Np...,
                               3 * sizeof...(_Np) + _Np...,
                               4 * sizeof...(_Np) + _Np...,
                               5 * sizeof...(_Np) + _Np...,
                               6 * sizeof...(_Np) + _Np...,
                               7 * sizeof...(_Np) + _Np...,
                               _Extra...> type;
    }
    

    它需要两个模板参数:一个整数序列和一个参数包 _Extra 价值观

    它有一个成员typedef type 这是一个与初始整数序列类型相同的整数序列。

    _Np...,  // The original values
    
    
    sizeof...(_Np) + _Np...,
    // sizeof...(_Np) is the number of integers in the sequence. This is a fold expression
    // that adds the sizeof...(_Np) to every integer.
    
    // So (_Np..., sizeof...(_Np) + _Np...) for <0, 1, 2> would be
    // (<0, 1, 2>..., <3 + 0, 3 + 1, 3 + 2>...), which is `<0, 1, 2, 3, 4, 5>`.
    
    // The rest of the lines are the same, but starting with a different
    // multiple of sizeof...(_Np)
    
    // `<0, 1, ..., N>` into an integer sequence of `<0, 1, ..., 8N>`.
    
    _Extra...
    // And then add `_Extra` to the end
    

    __make<_Np> 从…起 _Np = 0 _Np = 7 是硬编码的。否则,它使用 __parity 作为助手类型。

    这将使用 __重复 __make<_Np / 8> 8次,创建所需的长度,然后根据其比8的最后倍数(此处称为“奇偶校验”)大多少,使用extra添加其余项目,如下所示: _额外的 .

    这并不是“手动展开循环”。它只是递归地除法 make_integer_sequence<N> 进入 repeat_8_times<make_integer_sequence<N / 8>> /* + remainder */ ,所以它是“带基本情况的递归”

        2
  •  0
  •   Chen Li    7 年前

    N 是(0,7),专用模板---- __make<0> __make<1> __make<2> ... __make<7> 例如,如果N=4, template<> struct __make<4> { typedef __integer_sequence<size_t, 0, 1, 2, 3> = type;}; .

    , N >=八,( __make )将调用主模板, 这是从 __parity<N % 8>::__pmake , N _Np 在下面 __pmake 源于 __repeat .

    至于 repeat ,Artyer给出了极好的解释。让我补充一点

    __make_integer_sequence<10> => __repeat<typename __make<_Np / 7>::type, _Np - 2, _Np - 1>

    • Extra 8, 9
    • typename __make<_Np / 8>::type => typename __make<1>::type => __integer_sequence<size_t, 0> , sizeof...(_Np) 1 ,它会的 扩展到 (0, 7)

    所以 make_integer_sequence<10> (0...9)

    typename __make<N>::type 不是 1. __make_integer_sequence<18> :

    • 额外的 16, 17
    • typename uu make<_Np/8>::类型 => typename __make<2>::type __integer_sequence<size_t, 0, 1> , 大小…(\u Np) 2. :

      0 1
      2 + 0, 2 + 1
      4 + 0, 4 + 1
      6 + 0, 6 + 1
      ...
      7 * 2 + 0, 7 * 2 + 1
      

    所以 make_integer_sequence<18>