programacion - Crear mediante programación compilaciones estáticas en tiempo de compilación en C++
librerias estaticas y dinamicas c++ (10)
Uno puede definir una matriz estática en tiempo de compilación de la siguiente manera:
const std::size_t size = 5;
unsigned int list[size] = { 1, 2, 3, 4, 5 };
Pregunta 1 : ¿es posible utilizar varios tipos de técnicas de metaprogramación para asignar estos valores "programáticamente" en tiempo de compilación?
Pregunta 2 : suponiendo que todos los valores en la matriz son los mismos barr pocos, ¿es posible asignar selectivamente valores en tiempo de compilación de forma programática?
p.ej:
const std::size_t size = 7;
unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 };
- Las soluciones que usan C ++ 0x son bienvenidas
- La matriz puede ser bastante grande, pocos cientos de elementos de largo
- La matriz por ahora solo consistirá en tipos de POD
- También se puede suponer que el tamaño de la matriz se conocerá de antemano, de manera compatible con el tiempo de compilación estático.
- Las soluciones deben estar en C ++ (sin script, sin macros, sin pp o con soluciones basadas en generador de código)
ACTUALIZACIÓN: La solución de Georg Fritzsche es increíble, necesita un poco de trabajo para compilar en msvc y compiladores de inteligencia, pero no obstante es un enfoque muy interesante para el problema.
¿De verdad necesitas hacerlo en tiempo de compilación? Sería mucho más fácil hacerlo en tiempo de inicialización estático. Podrías hacer algo como esto.
#include <cstddef>
#include <algorithm>
template<std::size_t n>
struct Sequence
{
int list[n];
Sequence()
{
for (std::size_t m = 0; m != n; ++m)
{
list[m] = m + 1;
}
}
};
const Sequence<5> seq1;
struct MostlyZero
{
int list[5];
MostlyZero()
{
std::fill_n(list, 5, 0); // Not actually necessary if our only
// are static as static objects are
// always zero-initialized before any
// other initialization
list[2] = 2;
list[3] = 3;
}
};
const MostlyZero mz1;
#include <iostream>
#include <ostream>
int main()
{
for (std::size_t n = 0; n != 5; ++n)
{
std::cout << seq1.list[n] << ", " << mz1.list[n] << ''/n'';
}
}
Podría empujar las listas fuera de las estructuras si quisiera, pero pensé que era un poco más limpio como este.
¿Qué hay de construir una estructura anidada utilizando plantillas, y fundir eso como una matriz del tipo correcto. El siguiente ejemplo funciona para mí, pero tengo la sensación de que estoy caminando o caminando muy cerca de un comportamiento indefinido.
#include <iostream>
template<int N>
struct NestedStruct
{
NestedStruct<N-1> contained;
int i;
NestedStruct<N>() : i(N) {}
};
template<>
struct NestedStruct<0>
{
int i;
NestedStruct<0>() : i(0) {}
};
int main()
{
NestedStruct<10> f;
int *array = reinterpret_cast<int*>(&f);
for(unsigned int i=0;i<10;++i)
{
std::cout<<array[i]<<std::endl;
}
}
Y, por supuesto, podría argumentar que la matriz no se inicializa en tiempo de compilación (lo que creo que es imposible), pero los valores que irán a la matriz se calculan en tiempo de compilación, y puede acceder a ellos como lo haría con una matriz normal. Creo que eso es lo más cerca que puedes llegar.
Algo como Boost.Assignment podría funcionar para contenedores estándar. Si realmente necesita usar matrices, puede usarlo a lo largo de Boost.Array .
Bueno, sus requisitos son tan vagos que es difícil hacer algo al respecto ... El problema principal es, por supuesto, de dónde viene ese valor.
De todos modos, una construcción en C ++ se puede considerar como 4 pasos:
- Pasos previos a la compilación: generación de encabezado / fuente desde otros formatos
- Preprocesamiento
- Instancias de plantilla
- Compilación adecuada
Si desea descartar la generación de scripts, entonces le quedan 2 alternativas: Programación previa y Programación de plantillas.
No hay manera de que sepa que la programación de meta-plantillas puede hacer el truco aquí, porque hasta donde sé, no es posible concatenar dos matrices en tiempo de compilación. Por lo tanto, nos quedamos con el salvador del día: programación del preprocesador
Sugeriría usar una biblioteca hecha y derecha para ayudarnos: Boost.Preprocessor .
De particular interés aquí:
Ahora bien, si supiéramos dónde elegir los valores, podríamos dar ejemplos más significativos.
En algún momento (no siempre) dicha matriz se genera a partir de una matriz de tipos. Por ejemplo, si ya tiene una lista de clase variable (como plantilla) y desea almacenar el valor de uint32_t encapsulado, puede usar:
uint32_t tab[sizeof(A)]= {A::value...};
Hay muchas cosas que puedes hacer con la meta-programación. Pero primero me gustaría preguntar: ¿por qué querrías hacer esto en tu caso? Pude entender si necesitaba declarar una matriz de este tipo en diferentes lugares, por lo que exigiría volver a escribir las mismas cosas varias veces. ¿Es este tu caso?
Al decir "definir programáticamente" sugiero lo siguiente:
#define MyArr(macro, sep) /
macro(0) sep /
macro(0) sep /
macro(2) sep /
macro(3) sep /
macro(0) sep /
macro(0) sep /
macro(0)
Por ahora, hemos definido todos los valores que quería de la manera más abstracta. Por cierto, si esos valores realmente significan algo para usted, puede agregarlo a la declaración:
#define MyArr(macro, sep) /
macro(0, Something1) sep /
macro(0, Something2) sep /
// ...
Ahora respiremos la vida en la declaración anterior.
#define NOP
#define COMMA ,
#define Macro_Count(num, descr) 1
#define Macro_Value(num, descr) num
const std::size_t size = MyArr(Macro_Count, +);
unsigned int list[size] = { MyArr(Macro_Value, COMMA) };
También puede manejar la situación donde la mayoría de las entradas de la matriz son iguales, con cierta creatividad pervertida :)
Pero siempre debes preguntarte: ¿realmente vale la pena? Porque, como puede ver, convierte el código en un acertijo.
Lo más cercano que puede obtener es usar las características de C ++ 0x para inicializar arreglos de plantillas locales o miembros desde una lista de argumentos de plantilla variadica.
Esto, por supuesto, está limitado por la profundidad máxima de creación de instancias de la plantilla y, si esto realmente marca una diferencia notable en su caso, debería medirse.
Ejemplo:
template<unsigned... args> struct ArrayHolder {
static const unsigned data[sizeof...(args)];
};
template<unsigned... args>
const unsigned ArrayHolder<args...>::data[sizeof...(args)] = { args... };
template<size_t N, template<size_t> class F, unsigned... args>
struct generate_array_impl {
typedef typename generate_array_impl<N-1, F, F<N>::value, args...>::result result;
};
template<template<size_t> class F, unsigned... args>
struct generate_array_impl<0, F, args...> {
typedef ArrayHolder<F<0>::value, args...> result;
};
template<size_t N, template<size_t> class F>
struct generate_array {
typedef typename generate_array_impl<N-1, F>::result result;
};
Uso para su caso de 1..5
:
template<size_t index> struct MetaFunc {
enum { value = index + 1 };
};
void test() {
const size_t count = 5;
typedef generate_array<count, MetaFunc>::result A;
for (size_t i=0; i<count; ++i)
std::cout << A::data[i] << "/n";
}
Solo usa un generador de código. Cree una o más plantillas que puedan generar el código que desee, utilizando una tabla o incluso funciones matemáticas. Luego incluya el archivo que generó en su aplicación.
En serio, un generador de código haría su vida mucho más fácil.
de impulso,
boost::mpl::range_c<int,1,5>
Generará una lista de números ordenados del 1 al 5 en tiempo de compilación. Para el segundo, no menciona ningún criterio para los valores que se cambiarían. Estoy bastante seguro de que no puedes descifrar y luego redefinar una nueva var una vez que se crea una lista.
la pregunta 1''t Puedes hacerlo así.
template <int num, int cur>
struct ConsequentListInternal {
enum {value = cur};
ConsequentListInternal<num-1,cur+1> next_elem;
};
template <int cur>
struct ConsequentListInternal<0, cur> {
enum {value = cur};
};
template <int v>
struct ConsequentList {
ConsequentListInternal<v, 0> list;
};
int main() {
ConsequentList<15> list;
return 0;
}