Implementación C++14 make_integer_sequence

Resuelto Khurshid asked hace 11 años • 8 respuestas

Intenté implementar la plantilla de alias C++ 14make_integer_sequence , que simplifica la creación de la plantilla de clase integer_sequence.

template< class T, T... I> struct integer_sequence
{
    typedef T value_type;
    static constexpr size_t size() noexcept { return sizeof...(I) ; }

};

template< class T, T N>
using make_integer_sequence = integer_sequence< T, 0,1,2, ... ,N-1 >; // only for illustration.

Para implementarlo make_integer_sequencenecesitamos una estructura auxiliar make_helper.

template< class T , class N >
using make_integer_sequence = typename make_helper<T,N>::type;

La implementación make_helperno es demasiado difícil.

template< class T, T N, T... I >
struct make_helper
{
   typedef typename mpl::if_< T(0) == N,  
                  mpl::identity< integer_sequence<T,I...> >,
                  make_helper< T, N-1, N-1,I...> 
               >::type;
};

Para probar make_integer_sequencehice esta función principal:

int main()
{
    #define GEN(z,n,temp)   \
     typedef make_integer_sequence< int, n >  BOOST_PP_CAT(int_seq,n) ;

   BOOST_PP_REPEAT(256, GEN, ~);
}

Compilé el programa con GCC 4.8.0, en un sistema i5 de cuatro núcleos con 8 GB de RAM. La compilación exitosa tomó 4 segundos.

Pero cuando cambié la macro GEN a:

int main() {

#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n * 4 > BOOST_PP_CAT(int_seq, n) ;

BOOST_PP_REPEAT(256, GEN, ~ );
}

La compilación no tuvo éxito y generó el mensaje de error:

memoria virtual agotada.

¿Alguien podría explicar este error y qué lo causó?

EDITAR:

Simplifiqué la prueba a:

int main()
{
   typedef make_integer_sequence< int, 4096 > int_seq4096;
}

Luego compilé exitosamente con GCC 4.8.0 -ftemplate-profundidad=65536.

Sin embargo esta segunda prueba:

int main()
{
    typedef make_integer_sequence< int, 16384 > int_seq16384;
}

No se compiló con GCC 4.8.0 -ftemplate-profundidad=65536 y generó el error:

memoria virtual agotada.

Entonces, mi pregunta es, ¿cómo puedo disminuir la creación de instancias profundas de plantillas?

Saludos, Khurshid.

Khurshid avatar Jul 02 '13 18:07 Khurshid
Aceptado

Aquí hay una log Nimplementación que ni siquiera necesita una mayor profundidad máxima para las instancias de plantillas y se compila bastante rápido:

// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;

template<unsigned...> struct seq{ using type = seq; };

template<class S1, class S2> struct concat;

template<unsigned... I1, unsigned... I2>
struct concat<seq<I1...>, seq<I2...>>
  : seq<I1..., (sizeof...(I1)+I2)...>{};

template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;

template<unsigned N> struct gen_seq;
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;

template<unsigned N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};

template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
Xeo avatar Jul 02 '2013 13:07 Xeo

Básicamente soy yo analizando la solución de Xeo: crear un wiki comunitario; si lo agradeces, vota a favor de Xeo .

... simplemente lo modifiqué hasta que sentí que no podía ser más simple, lo renombré y lo agregué value_typesegún size()el Estándar (pero solo no lo hice index_sequence) integer_sequence, y el código que funciona con GCC 5.2 -std=c++14podría ejecutarse sin modificaciones en compiladores más antiguos o de otro tipo con los que estoy atrapado. . Podría ahorrarle a alguien algo de tiempo/confusión.

// based on http://stackoverflow.com/a/17426611/410767 by Xeo
namespace std  // WARNING: at own risk, otherwise use own namespace
{
    template <size_t... Ints>
    struct index_sequence
    {
        using type = index_sequence;
        using value_type = size_t;
        static constexpr std::size_t size() noexcept { return sizeof...(Ints); }
    };

    // --------------------------------------------------------------

    template <class Sequence1, class Sequence2>
    struct _merge_and_renumber;

    template <size_t... I1, size_t... I2>
    struct _merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
      : index_sequence<I1..., (sizeof...(I1)+I2)...>
    { };

    // --------------------------------------------------------------

    template <size_t N>
    struct make_index_sequence
      : _merge_and_renumber<typename make_index_sequence<N/2>::type,
                            typename make_index_sequence<N - N/2>::type>
    { };

    template<> struct make_index_sequence<0> : index_sequence<> { };
    template<> struct make_index_sequence<1> : index_sequence<0> { };
}

Notas:

  • La "magia" de la solución de Xeo está en la declaración de _merge_and_renumber( concaten su código) con exactamente dos parámetros, mientras que la especificación expone efectivamente sus paquetes de parámetros individuales.

  • el typename... ::typeen...

    struct make_index_sequence
      : _merge_and_renumber<typename make_index_sequence<N/2>::type,
                            typename make_index_sequence<N - N/2>::type>
    

    evita el error:

invalid use of incomplete type 'struct std::_merge_and_renumber<std::make_index_sequence<1ul>, std::index_sequence<0ul> >'
Tony Delroy avatar Aug 26 '2015 09:08 Tony Delroy