Macros #if directive with non integer definition - c

I'm having a 20yo legacy code (pure C) which defines:
#define X float
before first function and before another function:
#undefine X
#define X double
I'm writing a code which is supposed to work with both definitions (and it'll be copied into both functions automatically). But it's impossible to make such code in a few cases. For these cases I need to detect if my X is float or double and use #if #else #endif.
The #if (X == float) is resolved as 0 == 0 and won't work.
In theory I can grab into legacy code and modify these definition to make my life easier, but I wonder if there is any macro magic which would allow me to workaround this without touching the legacy? Something with conversion X to string may be? Any ideas?

Concatenate with prefix that expands to something you can control.
#define PREFIX_float 0
#define PREFIX_double 1
#define CONCAT(a, b) a##b
#define XCONCAT(a, b) CONCAT(a, b)
#if XCONCAT(PREFIX_, X) == PREFIX_float

Related

Asking a predefined value as a float(or string) on an `#if` macro

I want to condition in an #if on the float(or string) value of a predefined variable.
As I saw if the value of the predefined variable is a whole number the #if is working good. But, if it is a float or a string, the if is not working as expected.
For example:
#define _VER_ = 103
#if _VER_ == 103
//Do somthing
#endif
The above code is working as expected, the #if becomes active/not active according to the _VER_ value. But, if the _VER_ value will be set to 1.0.3 or to "1.0.3" the #if is not working as expected.
For example:
#define _VER_ = 1.0.3
#if _VER_ == 1.0.3
//Do somthing
#endif
Or this:
#define _VER_ = "1.0.3"
#if _VER_ == "1.0.3"
//Do somthing
#endif
Both code example are not working as expected, the #if stays inactive regardless with the _VER_ value.
How to make this working properly?
The pre-processor is not able to do string comparisons at all, and floating point values in #if directives are not supported by the standard either. But even if they were comparing floating point is always critical due to rounding issues (keep in mind that you cannot even represent many of such simple numbers like 0.1 exactly as they are periodic in binary), see e.g. here or here, thus you cannot compare floating points safely either (most likely that's why they are not supported...).
What you could do, though, is having three distinct integral values:
#define MAJOR 1
#define MINOR 0
#define REVISION 3
Now you can do comparisons on these single values individually like:
#if MAJOR > 1 || MAJOR == 1 && MINOR > 0
// anything after 1.0.x
What you could do as well is combining these values into a single integer value by shifting them appropriately, e. g.
#define VERSION MAKE_VERSION(MAJOR, MINOR, REVISION)
#define MAKE_VERSION(X, Y, Z) MAKE_VERSION_(X, Y, Z)
// intermediate step necessary to resolve the version parts correctly
#define MAKE_VERSION_(X, Y, Z) (X ## lu << 16 | Y ## lu << 8 | Z ## lu)
Adding lu suffixes: long is guaranteed large enough by the standard such that shifting by 16 bit will fit into the type (which is not guaranteed for int!). You might experiment with other offsets to make the result fit into int and then leaving out the suffix (e. g. shifting by 8 or 12 instead of 16) or to allow more sub-versions (e. g. by shifting by 20 instead of 16).
Multiplying by 10000 and 100 would do the trick as well and result in more human readable version numbers (in decimal notation at least).
Finally you could create a version string by another macro:
#define VERSION_STRING MAKE_VERSION(MAJOR, MINOR, REVISION)
#define MAKE_VERSION(X, Y, Z) MAKE_VERSION_(X, Y, Z)
// intermediate step needed for correct stringification
#define MAKE_VERSION_(X, Y, Z) #X "." #Y "." #Z
Side note:
#define _VER_ = 103
Is plain wrong for two reasons:
The pre-processor does nothing else than pure text processing, it is not aware of any C code. The assignment sign (=) will be included in text replacement anywhere you use it (so #if _VER_ == 103 will be expanded to #if = 103 == 103).
Identifiers starting with an underscore followed by a capital character are reserved, using them results in undefined behaviour (note: identifiers with two subsequent underscores – anywhere – as well).
You can split the version up in the way that makes most sense anyway, namely:
#define VERS_MAJOR 1
#define VERS_MINOR 0
#define VERS_PATCH 3
And then do pre-processor comparisons such as:
#if (VERS_MAJOR == 1) && (VERS_MINOR == 0)
...
If you need to get 1.0.3 as output then convert it to a string literal:
#define STR(x) #x
#define STR_VERSION(a,b,c) STR(a) "." STR(b) "." STR(c)
#define VERSION STR_VERSION(VERS_MAJOR,VERS_MINOR,VERS_PATCH)
...
puts(VERSION); // prints 1.0.3

The C define with any value for non zero

In C I have a define like:
#define VALUE 5
But the user can set a function too:
#define VALUE get_value()
For historical reasons
#define VALUE 0
means "the value is not set, use the default"
The question is how to write an #if that decides if VALUE is 0.
#if VALUE == 0
...
#else
...
#endif
gives "missing binary operator before token "("" error with GCC.
EDIT:
To make the use case clearer:
#if VALUE == 0
set_default_value();
#else
set_value(VALUE)
#endif
So I don't need VALUE to be evaluated in the #if just see if it's literally '0' or not.
You can use preprocessing pattern matching.
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define GLUE(A,B) GLUE_I(A, B)
#define GLUE_I(A,B) A##B
#define ZERO_TEST(X_) SECOND(GLUE(ZERO_TEST_AGAINST_,X_),0)
#define ZERO_TEST_AGAINST_0 ,1
The key construct here is the SECOND macro, which indirectly expands to its second argument. For pattern matching, you would use this by carefully constructing a first argument; since SECOND normally expands to its second argument, whatever you construct is normally ignored. But since SECOND expands to its second argument indirectly, you can cherry pick a particular pattern by having the first argument expand in particular cases with a comma, which would shove in a new second argument.
In this case we have an indirect paste at the end of ZERO_TEST_AGAINST_, and we're looking for the result of that to be ZERO_TEST_AGAINST_0.
To use this:
#if ZERO_TEST(VALUE)
set_default_value();
#else
set_value(VALUE)
#endif
Demo
http://coliru.stacked-crooked.com/a/2a6afc189637cfd3
Caveat
This fits your spec precisely as given; the indirect paste would not work with this form if you have parenthetical definitions:
#define VALUE (5)
...or:
#define VALUE (get_value() << 2) | 1
...since ZERO_TEST_AGAINST_ and ( do not join to make a valid token.
A generic solution is likely impossible, but you can hack something together:
#define CAT(x, ...) CAT_(x, __VA_ARGS__)
#define CAT_(x, ...) x##__VA_ARGS__
#define CHECK_VALUE_CHECK_0 )(
#define CHECK_VALUE_FALSE(...) CHECK_VALUE_TRUE
#define CHECK_VALUE_TRUE() 1
#define CHECK_VALUE CHECK_VALUE_(CAT(CHECK_VALUE_CHECK_, VALUE))
#define CHECK_VALUE_(...) CHECK_VALUE_FALSE(__VA_ARGS__)
#if CHECK_VALUE
#error Value is 0.
#else
#error Value is not 0.
#endif
Now, if VALUE is defined to 0, the macro CHECK_VALUE will expand to 1. Otherwise it will expand to CHECK_VALUE_TRUE, which as an unknown identifier, is considered falsey by #if.
This solution is hacky:
If VALUE starts with 0,, its causes a hard error.
If VALUE starts with something other than a letter or digit or _ (e.g. (), it causes a hard error.
...
A preprocessor cannot evaluate C-Functions, such as get_value(). It can only handle static data, as its replaced with code prior to compilation, so your statements are not executed at runtime.
if (VALUE == 0) in your C-Code would get replaced for example with if (get_value() == 0) prior to compilation. The Preprocessor is not able to evaluate the return value of get_value() (even if it would always return 0).
A #define VALUE 5 line makes the C preprocessor replace the string VALUE with the string 5 wherever it sees it (outside "strings", that is). The resulting program will not even contain VALUE anymore, and that is what the compiler proper sees(1).
Do an experiment: Get your program (cut it down to a few lines), and run:
cc -E proggie.c > proggie.i
Under Unixy systems you'll get a file proggie.i that contains preprocessed C, which is what the compiler proper sees. No VALUE in sight, and trying to set VALUE in the source ends up as trying to set 0, which obviously won't work.
(1) Historically, C compilers ran a chain of programs over the source (then memories where measured in KiB, not GiB), the first --preprocessor-- was usually called cpp; today's compilers usually don't have a separate preprocessor anymore. But conceptually the first step is still preprocessing the code (handle #include, #if and #defined macros).
I think you're misunderstand how the historical 'the value is not set' works.
In the C preprocessor, whenever you have an #if directive any identifier in the expression that is not an macro (and is not the special function defined) will be replaced by the constant 0 prior to evaluating the #if. So with your example
#define VALUE get_value()
#if VALUE == 0
what happens is the VALUE macro is expanded, then, since there is no get_value macro defined, it is replaced by 0, leaving you with
#if 0() == 0
which gives you the syntax error you see.

Proper usage of #if directive in C? [duplicate]

This question already has answers here:
How to compare strings in C conditional preprocessor-directives
(14 answers)
Closed 2 years ago.
The following structure for C preprocessor directive #if works well.
#define C 1
#if C==1
...
#endif
But I'd like to use something like this:
#define REAL double
#if REAL==double
...
#elif REAL==float
...
#else
assert(0);
#endif
It's not valid. Is there a solution?
Is there a solution?
There is a whole world of ways you can combine macro expansion with conditional compilation. It's unclear which of them you would consider a solution, but here's a possibility:
#define doubledouble 1
#define floatfloat 1
#define concat_literal(x,y) x ## y
#define realtype(t1,t2) concat_literal(t1,t2)
// ...
#define REAL float
#if realtype(REAL, double)
// REAL is double
#elif realtype(REAL, float)
// REAL is float
#else
// REAL is something else
#endif
The realtype macro works by macro-expanding its arguments and concatenating them together. On rescan, if the result is doubledouble or floatfloat then that is expanded further to 1. If it is an identifier that is not defined as a macro name, then the #if treats it as 0.
It's not foolproof, of course. It does not work if REAL has a multi-token expansion, such as long double. There's also a minor risk of collision with other defined macro names.

Macro to replace undefined tokens with some predefined value

Macro to replace undefined tokens with some predefined value.
I would like to have some EXPAND(x) macro that will expand to x if x is defined and to, for example, -1 if it is not, so that this code:
#define M1 1
#define M2 2
#undef M3
#define M4 (2*2)
printf("%i %i %i %i", EXPAND(M1), EXPAND(M2), EXPAND(M3), EXPAND(M4));
would print:
1 2 -1 4
Is that possible in C? Only thing that I can think of is to stringify x and then parse it with constexpr function, but that will work only in simple cases.
The only way to do this would be to use some header file that knows all possible macros and checks if they are defined. If not, replace them with -1.
#ifndef M1
#define M1 -1
#endif
#ifndef M2
#define M2 -1
#endif
...
You would then include this header after the file containing the macros/after the macro defines.
Though please note that this is a bad design, full of tight coupling and other incredibly bad ideas. There's certainly better ways to solve the actual problem, like ensuring that the code won't compile if a certain macro is missing.

Simple tuple operation

Say I have a tuple:
#define T (a, b)
How can I extract first and second element of the tuple in gcc, without use of any external libraries?
I found one way of doing this. I'm not sure if this would work in anything else than gcc.
#define first_(x, y) x
#define first(t) first_ t
#define second_(x, y) y
#define second(t) second_ t
#define T (a, b)
first(T) // expands to a
second(T) // expands to b

Resources