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?
CodePudding user response:
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).
CodePudding user response:
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