Is it possible to use for loop to reduce code in C?

3

I have the following function: A . Is it possible to use the B for loop to generate declarations as in the A function? What to use instead of printf to become a declaration?

printf("%s%i%s\n",ad1,i,ad2);

A

void functionX(unsigned int buf) {
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A15));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A14));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A13));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A12));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A11));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A10));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A9 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A8 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A7 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A6 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A5 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A4 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A3 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A2 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A1 ));
    H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A0 ));    
}

B

void functionX(unsigned int buf) {
    int i;
    char ad1[]="H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A";
    char ad2[]="));";

    for (i=15; i>=0; i--) {
      printf("%s%i%s\n",ad1,i,ad2);
    }
}
    
asked by anonymous 20.06.2017 / 08:58

3 answers

6

How about something like this:

void functionX( unsigned int buf )
{
    int i;

    int addr[16] = { EEPROM_A15, EEPROM_A14, EEPROM_A13, EEPROM_A12,
                   EEPROM_A11, EEPROM_A10, EEPROM_A9, EEPROM_A8,
                   EEPROM_A7, EEPROM_A6, EEPROM_A5, EEPROM_A4,
                   EEPROM_A3, EEPROM_A2, EEPROM_A1, EEPROM_A0 };

    for( i = 0; i < 16; i++ ) {
        H8_3687_pulse( EEPROM_MASK( buf, addr[i] ) );
    }

}
    
20.06.2017 / 14:28
3

While I usually follow Lacobus's response, this is a good place to remember that the tcc compiler has a dynamic-link library which allows the compilation of dynamic code:

#include "libtcc.h"

void (*)(unsigned int)
compilar_funcao(TCCState * s) {
    char buffer[1024];
    char * ptr = buffer;

    /* monta o texto do programa */
    ptr += sprintf(ptr,
        "#include \"header.h\"\n" // cabeçalho com as definições de EEPROM_MASK e EEPROM_A*
        "void f(unsigned int buf) {\n"
    );
    for (int i = 15; i >= 0; i --) {
        ptr += sprintf(ptr,
            "H8_3687_pulse(EEPROM_MASK(buf, EEPROM_A%d));\n",
            i
        );
    }
    ptr += sprintf(ptr, "}");
    /* fornecer os caminhos para inclusão de cabeçalhos */
    tcc_add_include_path(s, "./"); /* aqui o diretório atual */
    /* compilar para a memória */
    tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
    if (tcc_compile_string(buffer) == 1)
        return NULL;
    /* resolver as referências a símbolos externos */
    tcc_add_symbol(s, "H8_3687_pulse", H8_3687_pulse);
    /* fazer a relocação (trabalho do linker) */
    if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0)
        return NULL;
    /* retornar o símbolo global compilado (neste caso, chamado f */
    return tcc_get_symbol(s, "f");
}

At the start / end of the program you have to create the TCCState* to pass to the compilar_funcao() function above:

int
main(int argc, char ** argv) {
    TCCState * s = tcc_new();
    /* resto do programa... */
    tcc_delete(s);
    return 0;
}

Of course, in this specific case it is a tremendous overkill , but it is a good option if dynamic code generation can not be avoided ...

    
20.06.2017 / 20:34
3

You have a very special case there. I think EEPROM_A## is a family of macros. Macros work at the pre-processing level of the source file, a purely textual processing that occurs before calling the C compiler itself. I'll go into more detail about the preprocessor in this other answer .

In this case, there is not much to be done. At some point you need to access each of these macros ... What you can do is access these macros at an earlier time by populating a vector with those values.

If they are not macros, if they are variables, the language also does not provide any facility in this regard. C is a compiled language, and the name of the variables serves only as mnemonic so that the programmer (and the compiler too) can name a region of memory. After compiling, the name no longer exists and only the memory region exists, so there is not much to do about it.

In the case of variables, if they were compiled as if they were a vector, you can use a few pointers to do this in a loop. Recapping: Whether the compiled variables aligned as if they were a vector , something that depends on the library build from which you get those values. If you are compiling the library or if this is not explicitly written in its documentation, then we can assume that the following method is not reliable.

int i;
int *eeprom_a0_pt;

eeprom_a0_pt = &EEPROM_A0;

for (i = 15; i >= 0; i--) {
    H8_3687_pulse(EEPROM_MASK(buf, *(eeprom_a0_pt + i) ));
}

But you can only do this if and only if it is guaranteed that the variables have been compiled inline as if they were a vector.

I looked for some alternative using the GCC extension of variable macros, but could not find anything that could do this processing.

  

The section below was written before I became aware of the tcc, serving then for the traditional C without external libraries; for more details, see the answer from the Wtrmute

But why all this? Simple, by C language design.

As I explained above, the name of the variables gets lost in the compilation. If macro, text substitution occurs before compilation.

To do this take the output of printf and execute it correctly, you would need to evaluate ( evaluate in English) the expression. Making this evaluation is the equivalent of calling a compiler for that code snippet, also known as calling the eval function available in some programming languages.

The practice of dynamic code generation to be evaluated in run time is something typical of Lisp and Bash, being not very common in C.

    
20.06.2017 / 12:57