3

A #define's replacement list containing no spaces can be mapped to integers (IDs):

#define ID_double       1
#define ID_float        2
#define ID_long_double  3
#define ID_(x)          ID_##x
#define ID(x)           ID_(x)

#define T               double
T v;
int x = ID(T);          /* 1 */

Now consider:

#define T               long double

The code above won't compile:

<source>:3:25: error: 'ID_long' undeclared here (not in a function)

Question: is there a way to support spaces?

For example (somehow):

#define REPLACE_SPACES_TO_UNDERSCORES(x)     ??

#define ID(x)           ID_(REPLACE_SPACES_TO_UNDERSCORES(x))

#define T               long double
T v;
int x = ID(T);          /* 3 */
pmor
  • 5,392
  • 4
  • 17
  • 36
  • Maybe [`_Generic`](https://en.cppreference.com/w/c/language/generic) could be useful? – HolyBlackCat Mar 30 '22 at 18:16
  • 5
    Use a `typedef` statement to create an alias that doesn't contain spaces, ie `typedef long double long_double;` and then use the alias in the macros. – Remy Lebeau Mar 30 '22 at 18:16
  • @RemyLebeau Indeed: besides `#define T long_double` there is a need to `typedef long double long_double;`. Thanks! – pmor Mar 30 '22 at 18:24
  • @RemyLebeau However, you cannot (?) `typedef long double long_double;` via compiler option, while you can `#define T long_double` via compiler option. Now consider that the code is read-only. Then how to insert an extra `typedef` in there? – pmor Mar 30 '22 at 18:29
  • @pmor "*the code is read-only*" - if you can't modify the code, then you are SOL. AFAIK, there is no macro solution to this problem, and even if there were, it seems you can't modify the code to utilize it anyway – Remy Lebeau Mar 30 '22 at 18:31
  • `Now consider that the code is read-only. Then how to insert an extra typedef` Soooooo copy the code? Or create a separate header, add `typedef long doubel long_double;` as the first line, and add `#include ` on the second line. – KamilCuk Mar 30 '22 at 18:37
  • @KamilCuk OK. I was thinking of using only compiler options (e.g. `-D`) to parametrize the code. I.e. no modifications of the code itself. – pmor Mar 30 '22 at 18:57
  • 1
    GCC knows the option `-include` to add a file as it is included by `#include`. – the busybee Mar 30 '22 at 19:51

1 Answers1

1

The same idea I had in Replace spaces with underscores in a macro? can also be used here, and the dictionary will be much more realistical in size. In the following code on the end, ID(T) is replaced by 3.

// dictionary
#define WORD_long     long,
#define WORD_double   double,

// ---------------------------------------------

// the classics
#define COMMA(...)  ,
#define FIRST(a, ...)  a

// apply function f for each argument recursively with tail
#define FOREACHTAIL_1(f,a)      f(a,)
#define FOREACHTAIL_2(f,a,...)  f(a,FOREACHTAIL_1(f,__VA_ARGS__)) 
#define FOREACHTAIL_3(f,a,...)  f(a,FOREACHTAIL_2(f,__VA_ARGS__)) 
#define FOREACHTAIL_4(f,a,...)  f(a,FOREACHTAIL_3(f,__VA_ARGS__)) 
#define FOREACHTAIL_N(_4,_3,_2,_1,N,...)  \
        FOREACHTAIL_##N
#define FOREACHTAIL(f,...) \
        FOREACHTAIL_N(__VA_ARGS__,4,3,2,1)(f,__VA_ARGS__)

// if there are two arguments, expand to true. Otherwise false.
#define IFTWO_N(_0,_1,N,...)     N
#define IFTWO(true, false, ...)  IFTWO_N(__VA_ARGS__, true, false)

// If empty, expand to true, otherwise false.
// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define IFEMPTY(true, false, ...)  IFTWO(true, false, COMMA __VA_ARGS__ ())

// Join arguments with `_`.
#define JOIN_U(a, b)      a##_##b
#define JOIN_TWO_IN(a,b)  IFEMPTY(FIRST, JOIN_U, b)(a, b)
#define JOIN_TWO(a,b)     JOIN_TWO_IN(a,b)
#define JOIN(...)         FOREACHTAIL(JOIN_TWO, __VA_ARGS__)

// Append WORD_ to each argument and join arguments with spaces.
#define WORD_             /* the last one expands to empty */
#define WORDS_TWO(a, b)   WORD_##a b
#define WORDS(...)        FOREACHTAIL(WORDS_TWO, __VA_ARGS__)

#define REPLACE_SPACES_TO_UNDERSCORES(a)  JOIN(WORDS(WORDS(WORDS(WORDS(WORDS(a))))))

// --------------------------------------------

#define ID_double       1
#define ID_float        2
#define ID_long_double  3
#define ID_IN2(x)       ID_##x
#define ID_IN(x)        ID_IN2(x)
#define ID(x)           ID_IN(REPLACE_SPACES_TO_UNDERSCORES(x))

int main() {
    #define T               long double
    T v;
    int x = ID(T);          /* 3 */
}
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • A bit off-topic: do you think that it is possible to implement floating-point arith. (e.g. IEEE 754) with the preprocessor using metalanguages / DSLs such as [Order](https://github.com/rofl0r/order-pp)? The input would have to be encoded somehow using simpler tokens (i.e. you cannot read a token e.g. 3.14). However, the final output could be formatted to a floating constant (C11, 6.4.4.2 Floating constant). Yes, floating-point computation with the preprocessor would likely be very slow. However, here performance is no object. Any comments / thoughts? – pmor Aug 16 '22 at 16:46
  • Example: `DIV(FP(1),FP(3,6))` (i.e. 1/3.6) evaluates to `0.27` (prec. is two digits after decimal point) or to `0.2` (prec. is one digit after decimal point). Have a look at this [example](https://github.com/rofl0r/order-pp/blob/master/example/fibonacci.c). – pmor Aug 16 '22 at 16:46
  • `it is possible` Yes. `thoughts?` I believe this is not worth the time, just use a different preprocessor that can do the arithmetic, and I would spend my time on something else. `Example: DIV(FP(1),FP(3,6))` I did https://godbolt.org/z/xeY343b5s – KamilCuk Aug 16 '22 at 20:39
  • Btw, GCC produces `warning: ISO C99 requires at least one argument for the "..." in a variadic macro`. It can be fixed by adding comma after `_1`: `FP_N(__VA_ARGS__,_2,_1,)(__VA_ARGS__)`. – pmor Aug 17 '22 at 12:26