Estoy haciendo esta pregunta a partir de enseñanza y/o la piratería punto de vista, (no realmente quiero a un código como este).

Es posible implementar un bucle while utilizando sólo C las directivas de preprocesador. Entiendo que las macros no se puede ampliar de forma recursiva, de modo que ¿cómo podría lograrse esto?

InformationsquelleAutor Tarski | 2008-11-26

8 Comentarios

  1. 104

    Si quieres implementar un bucle while, usted tendrá que utilizar la recursividad en el preprocesador. La manera más fácil de hacer la recursividad es el uso de un aplazamiento de expresión. Un aplazamiento de expresión es una expresión que requiere más análisis para que se expanda:

    #define EMPTY()
    #define DEFER(id) id EMPTY()
    #define OBSTRUCT(id) id DEFER(EMPTY)()
    #define EXPAND(...) __VA_ARGS__
    
    #define A() 123
    A() //Expands to 123
    DEFER(A)() //Expands to A () because it requires one more scan to fully expand
    EXPAND(DEFER(A)()) //Expands to 123, because the EXPAND macro forces another scan

    ¿Por qué es esto importante? Así cuando una macro es escaneada y en expansión, crea una desactivación de contexto. Este incapacitante contexto provocará un token, que se refiere a la actualidad, la expansión de la macro, para ser pintado de azul. Por lo tanto, una vez que está pintado de azul, la macro ya no se puede expandir. Esto es por qué las macros no se expanden de forma recursiva. Sin embargo, la desactivación de un contexto sólo existe durante un análisis, por lo que mediante el aplazamiento de una expansión podemos evitar que nuestras macros de convertirse pintado de azul. Nosotros sólo tendrá que aplicar más análisis para la expresión. Podemos hacer que el uso de este EVAL macro:

    #define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
    #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
    #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
    #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
    #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
    #define EVAL5(...) __VA_ARGS__

    A continuación, vamos a definir algunos de los operadores para hacer algo de lógica(como si, etc):

    #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
    #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
    
    #define CHECK_N(x, n, ...) n
    #define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
    
    #define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
    #define NOT_0 ~, 1,
    
    #define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
    #define COMPL_0 1
    #define COMPL_1 0
    
    #define BOOL(x) COMPL(NOT(x))
    
    #define IIF(c) PRIMITIVE_CAT(IIF_, c)
    #define IIF_0(t, ...) __VA_ARGS__
    #define IIF_1(t, ...) t
    
    #define IF(c) IIF(BOOL(c))

    Ahora con todas estas macros se puede escribir una recursiva WHILE macro. Utilizamos un WHILE_INDIRECT macro para referirse a sí mismo de forma recursiva. Esto evita que la macro de ser pintada de azul, ya que se expanda en una exploración diferentes(y usando un tipo diferente de desactivación de contexto). El WHILE macro toma un predicado macro, un operador de la macro, y un estado(que es el variadic argumentos). Se mantiene la aplicación de este operador macro para el estado hasta que el predicado de la macro devuelve false(0).

    #define WHILE(pred, op, ...) \
        IF(pred(__VA_ARGS__)) \
        ( \
            OBSTRUCT(WHILE_INDIRECT) () \
            ( \
                pred, op, op(__VA_ARGS__) \
            ), \
            __VA_ARGS__ \
        )
    #define WHILE_INDIRECT() WHILE

    Para fines de demostración, sólo se va a crear un predicado que comprueba cuando el número de argumentos son la 1:

    #define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
    #define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
    
    #define IS_1(x) CHECK(PRIMITIVE_CAT(IS_1_, x))
    #define IS_1_1 ~, 1,
    
    #define PRED(x, ...) COMPL(IS_1(NARGS(__VA_ARGS__)))

    A continuación creamos un operador, que le concat dos testigos. También creamos un operador final(llamado M) que el proceso de la producción final:

    #define OP(x, y, ...) CAT(x, y), __VA_ARGS__ 
    #define M(...) CAT(__VA_ARGS__)

    A continuación, utilizando el WHILE macro:

    M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz

    Por supuesto, cualquier tipo de predicado o el operador puede ser transmitido.

    • Puede agregar un poco de explicación para las macros, por favor? Es realmente itneresting, pero difícil de conseguir. todos aquellos cruz refferences. No entiendo la última línea. ¿Qué es PRED como es definido por tomar x y aquí: M(EVAL(WHILE(**PRED**, OP, x, y, z))) //Expands to xyz es no tomar nada
    • Gracias por esto, me gustaría doble voto si pudiera.
    • Algo más de explicación sobre cómo funciona esto, con más de pasos: aquí o aquí. Los ejemplos de los artículos que no son exactamente los mismos que en esta respuesta, pero están lo suficientemente cerca.
    • Bien, yo me quedo con m4
    • Hermosa respuesta – si sólo iba a funcionar. Pero en lugar de xyz, que se expande a WHILE ( PRED, OP, xy, z ), cf. coliru.stacked-crooked.com/a/1a7b4e7879bd7d4b
    • Uy no fue un error de tipeo. Debe ser OBSTRUCT(WHILE_INDIRECT) no DEFER(WHILE_INDIRECT). Ver: coliru.stacked-crooked.com/a/a381ddcbcae0b890

  2. 10

    Utilizar recursiva incluir archivos. Desafortunadamente, no se puede recorrer en iteración del bucle más que la profundidad máxima a la que el preprocesador permite.

    Resulta que las plantillas de C++ son Turing Completo y puede ser utilizado de manera similar. Echa un vistazo Generativo de Programación

  3. 5

    Aquí hay un abuso de las reglas que hacerlo legalmente. Escribir su propio preprocesador de C. Hacen interpretar algunos #pragma directivas de la manera que usted desea.

    • Esto no es responder a la pregunta, que dice explícitamente que «el preprocesador de C», no «un preprocesador de C». Que, obviamente, refiriéndose a la existente toolchains.
  4. 5

    Yo uso el meta-modelo de programación para este propósito, es divertido una vez que usted consigue un bloqueo de la misma. Y muy útil cuando se utiliza con discreción. Ya que como se menciona su turing completo, hasta el punto donde usted puede incluso hacer que el compilador de entrar en un bucle infinito, o la pila de desbordamiento! No hay nada como ir a tomar un café sólo para encontrar su compilación es el uso de hasta 30+ gigabytes de memoria y la CPU para compilar el bucle infinito de código!

  5. 5

    bueno, no es que sea un bucle while, pero un contador de bucle, sin embargo, el bucle es posible en limpio CPP (no plantillas y no C++)

    #ifdef pad_always
    #define pad(p,f) p##0
    #else
    #define pad0(p,not_used) p
    #define pad1(p,not_used) p##0
    #define pad(p,f) pad##f(p,)
    #endif
    //f - padding flag
    //p - prefix so far
    //a,b,c - digits
    //x - action to invoke
    #define n0(p,x)
    #define n1(p,x)         x(p##1)
    #define n2(p,x) n1(p,x) x(p##2)
    #define n3(p,x) n2(p,x) x(p##3)
    #define n4(p,x) n3(p,x) x(p##4)
    #define n5(p,x) n4(p,x) x(p##5)
    #define n6(p,x) n5(p,x) x(p##6)
    #define n7(p,x) n6(p,x) x(p##7)
    #define n8(p,x) n7(p,x) x(p##8)
    #define n9(p,x) n8(p,x) x(p##9)
    #define n00(f,p,a,x)                       n##a(pad(p,f),x)
    #define n10(f,p,a,x) n00(f,p,9,x) x(p##10) n##a(p##1,x)
    #define n20(f,p,a,x) n10(f,p,9,x) x(p##20) n##a(p##2,x)
    #define n30(f,p,a,x) n20(f,p,9,x) x(p##30) n##a(p##3,x)
    #define n40(f,p,a,x) n30(f,p,9,x) x(p##40) n##a(p##4,x)
    #define n50(f,p,a,x) n40(f,p,9,x) x(p##50) n##a(p##5,x)
    #define n60(f,p,a,x) n50(f,p,9,x) x(p##60) n##a(p##6,x)
    #define n70(f,p,a,x) n60(f,p,9,x) x(p##70) n##a(p##7,x)
    #define n80(f,p,a,x) n70(f,p,9,x) x(p##80) n##a(p##8,x)
    #define n90(f,p,a,x) n80(f,p,9,x) x(p##90) n##a(p##9,x)
    #define n000(f,p,a,b,x)                           n##a##0(f,pad(p,f),b,x)
    #define n100(f,p,a,b,x) n000(f,p,9,9,x) x(p##100) n##a##0(1,p##1,b,x)
    #define n200(f,p,a,b,x) n100(f,p,9,9,x) x(p##200) n##a##0(1,p##2,b,x)
    #define n300(f,p,a,b,x) n200(f,p,9,9,x) x(p##300) n##a##0(1,p##3,b,x)
    #define n400(f,p,a,b,x) n300(f,p,9,9,x) x(p##400) n##a##0(1,p##4,b,x)
    #define n500(f,p,a,b,x) n400(f,p,9,9,x) x(p##500) n##a##0(1,p##5,b,x)
    #define n600(f,p,a,b,x) n500(f,p,9,9,x) x(p##600) n##a##0(1,p##6,b,x)
    #define n700(f,p,a,b,x) n600(f,p,9,9,x) x(p##700) n##a##0(1,p##7,b,x)
    #define n800(f,p,a,b,x) n700(f,p,9,9,x) x(p##800) n##a##0(1,p##8,b,x)
    #define n900(f,p,a,b,x) n800(f,p,9,9,x) x(p##900) n##a##0(1,p##9,b,x)
    #define n0000(f,p,a,b,c,x)                               n##a##00(f,pad(p,f),b,c,x)
    #define n1000(f,p,a,b,c,x) n0000(f,p,9,9,9,x) x(p##1000) n##a##00(1,p##1,b,c,x)
    #define n2000(f,p,a,b,c,x) n1000(f,p,9,9,9,x) x(p##2000) n##a##00(1,p##2,b,c,x)
    #define n3000(f,p,a,b,c,x) n2000(f,p,9,9,9,x) x(p##3000) n##a##00(1,p##3,b,c,x)
    #define n4000(f,p,a,b,c,x) n3000(f,p,9,9,9,x) x(p##4000) n##a##00(1,p##4,b,c,x)
    #define n5000(f,p,a,b,c,x) n4000(f,p,9,9,9,x) x(p##5000) n##a##00(1,p##5,b,c,x)
    #define n6000(f,p,a,b,c,x) n5000(f,p,9,9,9,x) x(p##6000) n##a##00(1,p##6,b,c,x)
    #define n7000(f,p,a,b,c,x) n6000(f,p,9,9,9,x) x(p##7000) n##a##00(1,p##7,b,c,x)
    #define n8000(f,p,a,b,c,x) n7000(f,p,9,9,9,x) x(p##8000) n##a##00(1,p##8,b,c,x)
    #define n9000(f,p,a,b,c,x) n8000(f,p,9,9,9,x) x(p##9000) n##a##00(1,p##9,b,c,x)
    #define n00000(f,p,a,b,c,d,x)                                   n##a##000(f,pad(p,f),b,c,d,x)
    #define n10000(f,p,a,b,c,d,x) n00000(f,p,9,9,9,9,x) x(p##10000) n##a##000(1,p##1,b,c,d,x)
    #define n20000(f,p,a,b,c,d,x) n10000(f,p,9,9,9,9,x) x(p##20000) n##a##000(1,p##2,b,c,d,x)
    #define n30000(f,p,a,b,c,d,x) n20000(f,p,9,9,9,9,x) x(p##30000) n##a##000(1,p##3,b,c,d,x)
    #define n40000(f,p,a,b,c,d,x) n30000(f,p,9,9,9,9,x) x(p##40000) n##a##000(1,p##4,b,c,d,x)
    #define n50000(f,p,a,b,c,d,x) n40000(f,p,9,9,9,9,x) x(p##50000) n##a##000(1,p##5,b,c,d,x)
    #define n60000(f,p,a,b,c,d,x) n50000(f,p,9,9,9,9,x) x(p##60000) n##a##000(1,p##6,b,c,d,x)
    #define n70000(f,p,a,b,c,d,x) n60000(f,p,9,9,9,9,x) x(p##70000) n##a##000(1,p##7,b,c,d,x)
    #define n80000(f,p,a,b,c,d,x) n70000(f,p,9,9,9,9,x) x(p##80000) n##a##000(1,p##8,b,c,d,x)
    #define n90000(f,p,a,b,c,d,x) n80000(f,p,9,9,9,9,x) x(p##90000) n##a##000(1,p##9,b,c,d,x)
    #define cycle5(c1,c2,c3,c4,c5,x) n##c1##0000(0,,c2,c3,c4,c5,x)
    #define cycle4(c1,c2,c3,c4,x) n##c1##000(0,,c2,c3,c4,x)
    #define cycle3(c1,c2,c3,x) n##c1##00(0,,c2,c3,x)
    #define cycle2(c1,c2,x) n##c1##0(0,,c2,x)
    #define cycle1(c1,x) n##c1(,x)
    #define concat(a,b,c) a##b##c
    #define ck(arg) a[concat(,arg,-1)]++;
    #define SIZEOF(x) (sizeof(x) / sizeof((x)[0]))
    void check5(void)
    {
    int i, a[32769];
    for (i = 0; i < SIZEOF(a); i++) a[i]=0;
    cycle5(3,2,7,6,9,ck);
    for (i = 0; i < SIZEOF(a); i++) if (a[i] != 1) printf("5: [%d] = %d\n", i+1, a[i]);
    }
  6. 2

    He encontrado este esquema útil cuando el compilador tengo mal humor y no se desenrolle ciertos bucles para mí

    #define REPEAT20(x) { x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;}

    REPEAT20( val = pleaseconverge(val) );

    Pero en mi humilde opinión, si necesitas algo mucho más complicado que eso, entonces usted debe escribir su propio pre-preprocesador. Su pre-preprocesador podría, por ejemplo, generar un archivo de encabezado apropiados para usted, y es bastante fácil de incluir este paso en un archivo Makefile para tener todo compilar sin problemas mediante un único comando. Yo lo he hecho.

  7. 1

    No es exactamente lo que usted pidió, pero checkout estos enlaces a un programa en C que también es válido makefile y shell script.

    La C, la marca y el código de shell construir sobre
    cada uno de los otros para crear un programa en C (?)
    que cuando se ejecuta como un script de shell
    compilará sí mismo a través de la C
    compilador mediante un makefile!

    Un ganador en el 2000 ofuscado C concurso.

    http://www.ioccc.org/2000/tomx.c

    http://www.ioccc.org/2000/tomx.hint

    • No hay bucles en este ioccc programa
    • en el «tomx.sugerencia»: By extending this program's idea, It is possible to do some very interesting things like generating C code on execution by adding additional code in make/sh (open up a loop????). No estoy seguro si esto es lo que se entiende o no…
    • posible»; pero no hay bucles en C programa tomx.c de 2000.
    • sí, sólo estaba diciendo que no era totalmente off-topic 😛

Dejar respuesta

Please enter your comment!
Please enter your name here