Pregunta

Estoy utilizando BOOST_PP para hacer cálculos de precompilación en el preprocesador. Me estoy centrando en una aplicación donde el tamaño del código es muy importante para mí. (Por favor, no decir el compilador debe o general hace eso, tengo que controlar lo que se realiza en tiempo de compilación y qué código se genera). Sin embargo, quiero ser capaz de utilizar el mismo nombre de la función / macro para ambas constantes enteras y variables. Como ejemplo trivial, puedo tener

#define TWICE(n) BOOST_PP_MUL(n,2)
//.....
// somewhere else in code
int a = TWICE(5);

Esto hace lo que yo quiero que, evaluando a

int a = 10;

durante el tiempo de compilación.

Sin embargo, también quiero que sea utilizado en

int b = 5;
int a = TWICE(b);

Esto debería ser preprocesado a

int b = 5;
int a = 5 * 2;

Por supuesto, puede hacerlo mediante el uso de las macros tradicionales como

#define TWICE(n) n * 2

Pero entonces es imposible hacer lo que yo quiero que haga de constantes enteras (evaluación de las mismas durante el tiempo de compilación).

Por lo tanto, mi pregunta es, ¿hay un truco para comprobar si el argumento es un literal o una variable, y luego utilizar diferentes definiciones. es decir, algo como esto:

#define TWICE(n) BOOST_PP_IF( _IS_CONSTANT(n), \
                              BOOST_PP_MUL(n,2), \
                              n * 2 )

editar: Así que lo que estoy realmente después de una cierta manera de comprobar si algo es una constante disponible en tiempo de compilación, y por lo tanto un buen argumento para las funciones BOOST_PP_. Soy consciente de que esto es diferente de lo que la mayoría de la gente espera de un preprocesador y las recomendaciones generales de programación. Pero no hay mal modo de programación, así que por favor, no odio en la pregunta de si está de acuerdo con su filosofía. Hay una razón existe la biblioteca BOOST_PP, y esta cuestión se encuentra en el mismo espíritu. Simplemente no podría ser posible sin embargo.

¿Fue útil?

Solución

Usted está tratando de hacer algo que es mejor dejar a las optimizaciones del compilador.

int main (void) {
  int b = 5;
  int a = b * 2;

  return a; // return it so we use a and it's not optimized away
}

gcc -s -O3 t.c

 .file "t.c"
 .text
 .p2align 4,,15
.globl main
 .type main, @function
main:
.LFB0:
 .cfi_startproc
 movl $10, %eax
 ret
 .cfi_endproc
.LFE0:
 .size main, .-main
 .ident "GCC: (Debian 4.5.0-6) 4.5.1 20100617 (prerelease)"
 .section .note.GNU-stack,"",@progbits

La optimización del compilador optimizará.

EDIT: Estoy consciente de que usted no quiere oír que el compilador "debería" o "normalmente" lo hace. Sin embargo, lo que está tratando de hacer no es algo que está destinado a ser hecho en el preprocesador de C; la gente que diseñó el lenguaje C y el preprocesador de C diseñados para trabajar con el preprocesamiento de emergencia, ya que de átomo básico. El CPP es, en muchos sentidos, "tonto". Esto no es algo malo (de hecho, esto es, en muchos casos lo que hace que sea tan útil), pero al final del día, es un preprocesador. Se pre procesa archivos de origen antes de que se analizan. Se pre procesa archivos de origen antes de que ocurra el análisis semántico. Se pre procesa archivos de origen antes de comprobar la validez de un archivo fuente dada. Yo entiendo que usted no quiere oír que esto es algo que el analizador y el analizador semántico debe ser manipulado, o por lo general lo hace. Sin embargo, esta es la realidad de la situación. Si desea diseñar código que es increíblemente pequeño, entonces usted debe confiar en su compilador para hacer su trabajo en lugar de tratar de crear construcciones del preprocesador para hacer el trabajo. Piénsalo de esta manera: miles de horas de trabajo entraron en su compilador, así que trata de volver a utilizar la mayor cantidad de ese trabajo como sea posible

Otros consejos

No se enfoque bastante directa, sin embargo:

struct operation {
    template<int N>
    struct compile {
        static const int value = N;
    };
    static int runtime(int N) { return N; }
};

operation::compile<5>::value;
operation::runtime(5);

alternativamente

operation<5>();
operation(5);

No hay ninguna posibilidad real de la mezcla de los dos niveles (preprocesador y la evaluación de Variables ). Por lo que entiendo de que se trate, b debe ser una constante simbólica?

Creo que debería utilizar el tradicional

#define TWICE(n) ((n) * 2)

pero en lugar de inicializar las variables con las expresiones, se debe inicializar con constantes de tiempo de compilación. La única cosa que veo a forzar la evaluación en tiempo de compilación y tener constantes simbólicas en C son constantes de enumeración integrales. Estos se define para tener tipo int y se evalúan en tiempo de compilación.

enum { bInit = 5 };
int b = bInit;
enum { aInit = TWICE(bInit) };
int a = aInit; 

Y, en general no debe ser demasiado cuidadosa con const (como para su b) y comprobar el ensamblador producido con -S.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top