[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Version-checking macro

Kenneth Brody

7/8/2011 6:42:00 PM

This is one of those "it works on my compiler, but I want to verify that
it's 'legal and defined' as far as C is concerned" types of things.

I am writing a macro which I will be using as a version check, as in "test
if the version is at least X.Y.Z".

#define IF_VERSION(major,minor) \
( ( major > VER_MAJOR ) \
|| \
( major == VER_MAJOR && minor >= VER_MINOR ) \
)

Elsewhere, there will be #define's for VER_MAJOR and VER_MINOR. For example:

#define VER_MAJOR 10.5
#define VER_MINOR 6

This would mean version "10.5.6".

(Yes, I know this should really probably be MAJOR.MINOR.PATCH, but it's
already set up with this type of version defines.)

I would like to be able to use something like:

#if IF_VERSION(10.5,4)
.... code that requires version 10.5.4 or later ...
#else
.... Pre-10.5.4 version of the code ...
#endif


My concern is the expansion of the macro into a boolean expression which
needs to be evaluated as part of the #if directive. Yes, I know it "should"
be fine, but knowing all the obscure macro definitions that go by in clc,
where you need 3 nested macros to ensure that the expansion is done the way
you want, something in the back of my mind says "I wonder if..."


Thanks.

--
Kenneth Brody
9 Answers

Eric Sosman

7/9/2011 12:36:00 AM

0

On 7/8/2011 2:42 PM, Kenneth Brody wrote:
> This is one of those "it works on my compiler, but I want to verify that
> it's 'legal and defined' as far as C is concerned" types of things.
>
> I am writing a macro which I will be using as a version check, as in
> "test if the version is at least X.Y.Z".
>
> #define IF_VERSION(major,minor) \
> ( ( major > VER_MAJOR ) \
> || \
> ( major == VER_MAJOR && minor >= VER_MINOR ) \
> )
>
> Elsewhere, there will be #define's for VER_MAJOR and VER_MINOR. For
> example:
>
> #define VER_MAJOR 10.5
> #define VER_MINOR 6
>
> This would mean version "10.5.6".
>
> (Yes, I know this should really probably be MAJOR.MINOR.PATCH, but it's
> already set up with this type of version defines.)
>
> I would like to be able to use something like:
>
> #if IF_VERSION(10.5,4)
> ... code that requires version 10.5.4 or later ...
> #else
> ... Pre-10.5.4 version of the code ...
> #endif
>
>
> My concern is the expansion of the macro into a boolean expression which
> needs to be evaluated as part of the #if directive. Yes, I know it
> "should" be fine, but knowing all the obscure macro definitions that go
> by in clc, where you need 3 nested macros to ensure that the expansion
> is done the way you want, something in the back of my mind says "I
> wonder if..."

The back of your mind has good instincts. The problem here is
that "The expression that controls conditional inclusion shall be an
integer constant expression..." (6.10.1p1), and `10.5' ain't an integer
constant expression. To put it another way: The preprocessor doesn't
grok floating-point.

--
Eric Sosman
esosman@ieee-dot-org.invalid

Gene

7/9/2011 2:06:00 AM

0

On Jul 8, 2:42 pm, Kenneth Brody <kenbr...@spamcop.net> wrote:
> This is one of those "it works on my compiler, but I want to verify that
> it's 'legal and defined' as far as C is concerned" types of things.
>
> I am writing a macro which I will be using as a version check, as in "test
> if the version is at least X.Y.Z".
>
> #define IF_VERSION(major,minor) \
>          ( ( major > VER_MAJOR ) \
>            || \
>            ( major == VER_MAJOR && minor >= VER_MINOR ) \
>          )
>
> Elsewhere, there will be #define's for VER_MAJOR and VER_MINOR.  For example:
>
> #define VER_MAJOR 10.5
> #define VER_MINOR 6
>
> This would mean version "10.5.6".
>
> (Yes, I know this should really probably be MAJOR.MINOR.PATCH, but it's
> already set up with this type of version defines.)
>
> I would like to be able to use something like:
>
> #if IF_VERSION(10.5,4)
> ... code that requires version 10.5.4 or later ...
> #else
> ... Pre-10.5.4 version of the code ...
> #endif
>
> My concern is the expansion of the macro into a boolean expression which
> needs to be evaluated as part of the #if directive.  Yes, I know it "should"
> be fine, but knowing all the obscure macro definitions that go by in clc,
> where you need 3 nested macros to ensure that the expansion is done the way
> you want, something in the back of my mind says "I wonder if..."
>
> Thanks.
>
> --
> Kenneth Brody

The floating point comparison won't work. I've used something lke:

// Convert version parts to a single integer.
// You can also use hexadecimal digits and shift rather than multiply.
// Either way a printed encoding (in decimal or hex) is still
readable.
#define TO_VERSION(Major, Minor, Sub) (((Major) * 1000 + Minor) * 1000
+ (Sub))

#define VERSION TO_VERSION(10, 5, 6)

#if VERSION >= TO_VERSION(10, 5, 4)
.... code for case where version is at least 10.5.4.
#else
.... older version code
#endif

Greg A. Woods

7/9/2011 5:52:00 AM

0

Kenneth Brody <kenbrody@spamcop.net> writes:
>
> I am writing a macro which I will be using as a version check, as in
> "test if the version is at least X.Y.Z".
>
> #define IF_VERSION(major,minor) \

You might want to look at NetBSD's <sys/param.h>, for the definition of
the __NetBSD_Prereq__() macro and related things, and perhaps read some
of the threads on the NeBSD mailing lists discussing its creation too.

I can post pointers if your google-fu fails. :-)

Also note though that you will not likely be able to use that style of
macro like this:

#if (defined(IF_VERSION) && IF_VERSION(1,2))

Dunno if that'd be a concern for you or not.

If you do need to test for the presence of the macro before you try to
use it then you'll have to use something like __NetBSD_Version__ where
you combine all the numbers with a well known formula instead, or just
go with individual integer values like the __GCC_*__ macros and test
each separately, as needed:

#define __GNUC__ 4
#define __GNUC_MINOR__ 1
#define __GNUC_PATCHLEVEL__ 2


--
Greg A. Woods
RoboHack

<woods@robohack.ca> living on the edge http://www.ro...

gordonb.hlq8b

7/9/2011 2:16:00 PM

0

> This is one of those "it works on my compiler, but I want to verify that
> it's 'legal and defined' as far as C is concerned" types of things.
>
> I am writing a macro which I will be using as a version check, as in "test
> if the version is at least X.Y.Z".

One of the important things to do in this situation is to define
an ordering for version numbers. Yours is one of the more unusual
ones, if I take the implementation attempt to be the definition of
the ordering. (The preprocessor can't compare floating-point
numbers, as others have indicated, but it seems you tried to do
this, so I'm taking that as the intended comparison).

It seems that by your definition, 10.02.0 < 10.1.0, but 10.37.02 >
10.37.1 (if it's in the second position, "02" compares less than
"1", but if it's in the third position, the opposite holds). And,
it seems that if a version number component has a leading 0, it's
octal, and if it has a leading 0x, it's hexadecimal, because of the
way C defines integer constants.

According to one method of comparing version numbers (not necessarily
the best or most popular one), these numbers are all valid,
all different, and listed in order:

10
10.0
10.0.0
10.0.00
10.0.01
10.0.02
10.0.1
10.0.10
10.0.2
10.00.0
10.00.1
10.00.2
10.02
10.02.1
10.1.1
10.1.2
11.3.7

Rules:
- Component numbers are separated by periods.
- the first component number is treated as an integer, and compare in order.
(9 < 10). Leading zeros are not allowed. Omitting the first component
is not allowed.
- null components (which have 0 characters) compare earlier than
non-null components. (10 < 10.0) If a component is null, all
subsequent ones must be null (10..03 is not allowed).
- subsequent component numbers are treated as decimal fractions, and compare
in order. (10.10 < 10.2 and 10.82.03 < 10.82.1)
- two subsequent component numbers that differ only in one having
trailing zero(s) have the longer one compare greater. (10.00 < 10.000)
- two version numbers with the same string representation are equal,
otherwise they are not.

Kenneth Brody

7/11/2011 4:01:00 PM

0

On 7/8/2011 8:35 PM, Eric Sosman wrote:
> On 7/8/2011 2:42 PM, Kenneth Brody wrote:
>> This is one of those "it works on my compiler, but I want to verify that
>> it's 'legal and defined' as far as C is concerned" types of things.
[...]
> The back of your mind has good instincts. The problem here is
> that "The expression that controls conditional inclusion shall be an
> integer constant expression..." (6.10.1p1), and `10.5' ain't an integer
> constant expression. To put it another way: The preprocessor doesn't
> grok floating-point.

Thanks to all who pointed that out, and the pointers to other ways to handle
this.

Fortunately, there's already another #define which represents the value in
hex. For example, version 4.6 would be #defined as "0x0406". Using my
original macro:

#define IF_VERSION(major,minor) \
( ( major > VER_MAJOR ) \
|| \
( major == VER_MAJOR && minor >= VER_MINOR ) \
)

but with the "hexified" version, would this work:

#if IF_VERSION(0x0406,8)
....
#else
....
#endif

Now, too bad there isn't any way that I know of to return the equivalent of
printf("0x%02x%02x",x,y) within a macro.

Maybe I'll just make sure that the header which defines the version number
also includes two #defines with the X.Y value split into 2 integers?
(Fortunately, I'm in the position to add that requirement on this project.)
Then I'll just pass three values, rather than two.

Again, thanks for the replies.

--
Kenneth Brody

Kenneth Brody

7/11/2011 7:01:00 PM

0

On 7/11/2011 12:01 PM, Kenneth Brody wrote:
[...]

Okay, "final" version, put up for comments/criticisms...

Given the already-existing "MY_VERSION" which is the "hexified" version
number (ie: 4.6 would be specified as "0x0406") and MY_REVNUM is the patch
level. (Names changed to protect the innocent.)

#define MY_VERSION_MAJOR (MY_VERSION / 256)
#define MY_VERSION_MINOR (MY_VERSION % 256)
#define MY_VERSION_PATCH (MY_REVNUM)
#define IS_MY_VERSION(major,minor,patch) \
( \
( major > MY_VERSION_MAJOR ) \
|| \
( major == MY_VERSION_MAJOR && minor > MY_VERSION_MINOR ) \
|| \
( major == MY_VERSION_MAJOR && minor == MY_VERSION_MINOR && patch >=
MY_VERSION_PATCH ) \
)

I can then use, for example:

#if IS_MY_VERSION(5,0,0)
.... code for 5.0.00 and later ...
#elif IS_MY_VERSION(4,6,3)
.... code for 4.6.03 through pre-5.0.00 ...
#else
.... pre-4.6.03 code ...
#endif

--
Kenneth Brody

Eric Sosman

7/12/2011 1:33:00 AM

0

On 7/11/2011 3:01 PM, Kenneth Brody wrote:
> On 7/11/2011 12:01 PM, Kenneth Brody wrote:
> [...]
>
> Okay, "final" version, put up for comments/criticisms...
>
> Given the already-existing "MY_VERSION" which is the "hexified" version
> number (ie: 4.6 would be specified as "0x0406") and MY_REVNUM is the
> patch level. (Names changed to protect the innocent.)
>
> #define MY_VERSION_MAJOR (MY_VERSION / 256)
> #define MY_VERSION_MINOR (MY_VERSION % 256)
> #define MY_VERSION_PATCH (MY_REVNUM)
> #define IS_MY_VERSION(major,minor,patch) \
> ( \
> ( major > MY_VERSION_MAJOR ) \
> || \
> ( major == MY_VERSION_MAJOR && minor > MY_VERSION_MINOR ) \
> || \
> ( major == MY_VERSION_MAJOR && minor == MY_VERSION_MINOR && patch >=
> MY_VERSION_PATCH ) \
> )
>
> I can then use, for example:
>
> #if IS_MY_VERSION(5,0,0)
> ... code for 5.0.00 and later ...
> #elif IS_MY_VERSION(4,6,3)
> ... code for 4.6.03 through pre-5.0.00 ...
> #else
> ... pre-4.6.03 code ...
> #endif

Nothing seems obviously wrong, but why bother separating the
pieces of MY_VERSION only to glue them back together?

--
Eric Sosman
esosman@ieee-dot-org.invalid

Gene

7/12/2011 1:55:00 AM

0

On Jul 11, 3:01 pm, Kenneth Brody <kenbr...@spamcop.net> wrote:
> On 7/11/2011 12:01 PM, Kenneth Brody wrote:
> [...]
>
> Okay, "final" version, put up for comments/criticisms...
>
> Given the already-existing "MY_VERSION" which is the "hexified" version
> number (ie: 4.6 would be specified as "0x0406") and MY_REVNUM is the patch
> level.  (Names changed to protect the innocent.)
>
> #define MY_VERSION_MAJOR    (MY_VERSION / 256)
> #define MY_VERSION_MINOR    (MY_VERSION % 256)
> #define MY_VERSION_PATCH    (MY_REVNUM)
> #define IS_MY_VERSION(major,minor,patch)        \
>             (                                   \
>                 ( major > MY_VERSION_MAJOR ) \
>                 ||                              \
>                 ( major == MY_VERSION_MAJOR && minor > MY_VERSION_MINOR ) \
>                 ||                              \
>                 ( major == MY_VERSION_MAJOR && minor == MY_VERSION_MINOR && patch >=
> MY_VERSION_PATCH ) \
>             )
>
> I can then use, for example:
>
> #if IS_MY_VERSION(5,0,0)
> ... code for 5.0.00 and later ...
> #elif IS_MY_VERSION(4,6,3)
> ... code for 4.6.03 through pre-5.0.00 ...
> #else
> ... pre-4.6.03 code ...
> #endif

Okay, but isn't it more readable to allow general comparisons of
version numbers? Your method implies the direction of comparison, so
at least it should be called IS_MY_VERSION_AT_LEAST().

Or you can just shift the patch level into the existing hex encoding
and use integer comparison as version comparison:

#define MAJOR_VERSION 4
#define MINOR_VERSION 6
#define PATCH_VERSION 0

// Encodings of version information.

#define ENCODE(A, B) (((A) << 4) | (B))

#define VERSION_ENCODING(Major, Minor) \
ENCODE(Major, Minor)

#define VERSION_ENCODING_WITH_PATCH(Major, Minor, Patch) \
ENCODE(VERSION_ENCODING(Major, Minor), Patch)

// Strings denoting version informatino.
#define STRINGIFY(X) #X
#define STRING_ENCODE(A, B) \
STRINGIFY(A) "." STRINGIFY(B)

#define VERSION_STRING(Major, Minor) \
STRING_ENCODE(Major, Minor)

#define VERSION_STRING_WITH_PATCH(Major, Minor, Patch)\
VERSION_STRING(Major, Minor) "." STRINGIFY(Patch)

// Current version encodings and strings.
#define MY_VERSION VERSION_ENCODING(MAJOR_VERSION, MINOR_VERSION)
#define MY_VERSION_STRING VERSION_STRING(MAJOR_VERSION, MINOR_VERSION)

#define MY_VERSION_WITH_PATCH \
VERSION_ENCODING_WITH_PATCH(MAJOR_VERSION, MINOR_VERSION,
PATCH_VERSION)
#define MY_VERSION_STRING_WITH_PATCH \
VERSION_STRING_WITH_PATCH(MAJOR_VERSION, MINOR_VERSION,
PATCH_VERSION)

// Check including patch level.
#if MY_VERSION_WITH_PATCH >= VERSION_ENCODING_WITH_PATCH(5, 0, 0)
.... yada yada
#endif

// Check with no patch level
#if MY_VERSION >= VERSION_ENCODING(6, 9)
.... yada yada
#endif

I've added version string expansions here for fun.

Kenneth Brody

7/15/2011 4:29:00 PM

0

On 7/11/2011 9:32 PM, Eric Sosman wrote:
> On 7/11/2011 3:01 PM, Kenneth Brody wrote:
>> On 7/11/2011 12:01 PM, Kenneth Brody wrote:
>> [...]
>>
>> Okay, "final" version, put up for comments/criticisms...
>>
>> Given the already-existing "MY_VERSION" which is the "hexified" version
>> number (ie: 4.6 would be specified as "0x0406") and MY_REVNUM is the
>> patch level. (Names changed to protect the innocent.)
>>
>> #define MY_VERSION_MAJOR (MY_VERSION / 256)
>> #define MY_VERSION_MINOR (MY_VERSION % 256)
>> #define MY_VERSION_PATCH (MY_REVNUM)
>> #define IS_MY_VERSION(major,minor,patch) \
>> ( \
>> ( major > MY_VERSION_MAJOR ) \
>> || \
>> ( major == MY_VERSION_MAJOR && minor > MY_VERSION_MINOR ) \
>> || \
>> ( major == MY_VERSION_MAJOR && minor == MY_VERSION_MINOR && patch >=
>> MY_VERSION_PATCH ) \
>> )
>>
>> I can then use, for example:
>>
>> #if IS_MY_VERSION(5,0,0)
>> ... code for 5.0.00 and later ...
>> #elif IS_MY_VERSION(4,6,3)
>> ... code for 4.6.03 through pre-5.0.00 ...
>> #else
>> ... pre-4.6.03 code ...
>> #endif
>
> Nothing seems obviously wrong, but why bother separating the
> pieces of MY_VERSION only to glue them back together?

Because I thought it looked better to use IS_MY_VERSION(4,6,3) than
something like IS_MY_VERSION(0x0406,3).

However, I now see that I could have simply combined (major*256+minor)
rather than split MY_VERSION. Thanks.

--
Kenneth Brody