Johann Horvat
Johann Horvat

Reputation: 1325

C preprocessor macro expansion - use calculations

I have something like, where I try to use macro expansion with a calculation resulting into a string of chars using C:

#define S_O(N, X1, Y1, X2, Y2, RI, GI, BI, RO, GO, BO) \
    "S" #N*2-1 "H;" \
    "S" #N*2-1 "Z;" \
    "S" #N*2-1 "M2;" \
    "S" #N*2-1 "PA" #X1 "," #Y1 ";" \
    "S" #N*2-1 "PB" #X2 "," #Y2 ";" \
    "S" #N*2-1 "W2,2;" \
    "S" #N*2-1 "R" #RO ";" \
    "S" #N*2-1 "G" #GO ";" \
    "S" #N*2-1 "B" #BO ";" \
    "S" #N*2-1 "A0;" \
    "S" #N*2-1 "BD0;" \
    "S" #N*2-1 "S;" \
    "S" #N*2-1 "S;" \
    "S" #N*2 "H;" \
    "S" #N*2 "Z;" \
    "S" #N*2 "M2;" \
    "S" #N*2 "PA" #X1 "," #Y1 ";" \
    "S" #N*2 "PB" #X2 "," #Y2 ";" \
    "S" #N*2 "W2,2;" \
    "S" #N*2 "R" #RI ";" \
    "S" #N*2 "G" #GI ";" \
    "S" #N*2 "B" #BI ";" \
    "S" #N*2 "A0;" \
    "S" #N*2 "BD0;" \
    "S" #N*2 "S;" \
    "S" #N*2 "S;"

/* later in the code is used like this: */
char buffer[1024]={0};
snprintf(1024, S_O(1, 10, 20, 1880, 1060, 0, 0, 0, 255, 255, 255));
/* the output should be:
 * "S1H;S1Z;" etc.
 */

Obviously, this doesn't compile... How may I achieve my goal to avoid a function encapsulating this?

Upvotes: 0

Views: 257

Answers (2)

tstanisl
tstanisl

Reputation: 14107

It's doable.

You can initialize a string as a normal array. It allows to do the computation over individual characters.

char x[] = "hello";
char y[] = { 'h', 'e', 'l', 'l', 'o', 0 };

Each digit can be computed as '0' + (value / (10**position)) % 10.

Exemplary code that computes strings at compilation time for 2*x-1 and y*y where x == 7 and y == 11.

#include <stdio.h>

#define D(X) ('0' + (X) % 10)
#define S3(X) D((X)/100), D((X)/10), D(X)
#define S_O(X, Y) { 'S', S3(2*X-1), 'H', ';', 'S', S3(Y*Y), 'A', ';', 0 }

int main() {
    char buf[] = S_O(7, 11);
    puts(buf);
}

It prints:

S013H;S121A;

Still not perfect, because it adds those extra heading zeros but it may be good enough for OP's application.

Upvotes: 3

KamilCuk
KamilCuk

Reputation: 140960

I try to use macro expansion with a calculation resulting into a string

It is not possible to use just macro expansion to convert a result of calculation to a string. It is not (*feasibly) possible to use C preprocessor to convert the result of a calculation to a string.

How may I achieve my goal to avoid a function encapsulating this?

Strongly prefer in such cases to use a better and more powerful pre-processor than C preprocessor, like M4, Jinja2, PHP, or generate the source code.


*feasibly: To convert the result of a calculation to a string in C preprocessor, you would have to enumerate all possible cases one by one and prepare a string for it.

// calculate.h

#if N*2 == 2
#define RESULT_OF_N_MUL_2 "2"
#elif N*2 == 4
#define RESULT_OF_N_MUL_2 "4"
// etc. for __any__ number you want to handle
#endif

#if N*2-1 == 1
#define RESULT_OF_N_MUL_2_MINUS_1 "1"
#elif N*2-1 == 3
#define RESULT_OF_N_MUL_2_MINUS_1 "3"
// etc. for __any__ number you want to handle
#endif

#define S_O() \
   "S" RESULT_OF_N_MUL_2_MINUS_1 "H;" \
   "S" RESULT_OF_N_MUL_2 "H;"

#undef N

// main.c
#define N 10
#include "calculate.h"
int main() {
   puts(S_O());
}

If you are open to such code, then you could use boost/preprocessor and apply stringify operation after the expansion, which works suprisingly good:

#include <boost/preprocessor.hpp>
#include <boost/preprocessor/arithmetic.hpp>
#include <boost/preprocessor/stringize.hpp>
    
#define S_O(N) \
    "S" BOOST_PP_STRINGIZE(BOOST_PP_SUB(BOOST_PP_MUL(N, 2), 1)) "H;"

int main() {
    puts(S_O(5)); // outputs "S9H;"
}

Behind the scenes, let's say BOOST_PP_* enumerate all possible cases of addition one by one.

Upvotes: 2

Related Questions