Determining OS at Runtime
-
I need to support 10.4 in my application, but it uses some Carbon APIs
that are deprecated in 10.5 and I am using some new 10.5 APIs that
require the 10.5 SDK.
I am sure I have seen this before, but I have been unable to find it
in the Archive. How do I determine at runtime which OS version I am
running on?
Rich Collyer -
On Wed, Jul 1, 2009 at 7:24 PM, iseecolors<iseecolors...> wrote:
> I need to support 10.4 in my application, but it uses some Carbon APIs that
> are deprecated in 10.5 and I am using some new 10.5 APIs that require the
> 10.5 SDK.
>
> I am sure I have seen this before, but I have been unable to find it in the
> Archive. How do I determine at runtime which OS version I am running on?
Just check for the presence of the function you want to call. In
Xcode, set your deployment target to 10.4, so that Leopard-only
symbols will be weak-linked. Then just check the symbol for NULL
before calling it:
if (SomeLeopardFunction != NULL) {
SomeLeopardFunction();
} else {
TigerFunction();
}
sherm--
--
Cocoa programming in Perl: http://camelbones.sourceforge.net -
On Jul 1, 2009, at 5:22 PM, Sherm Pendley wrote:
> On Wed, Jul 1, 2009 at 7:24 PM,
> iseecolors<iseecolors...> wrote:
>> I need to support 10.4 in my application, but it uses some Carbon
>> APIs that
>> are deprecated in 10.5 and I am using some new 10.5 APIs that
>> require the
>> 10.5 SDK.
>>
>> I am sure I have seen this before, but I have been unable to find
>> it in the
>> Archive. How do I determine at runtime which OS version I am
>> running on?
>
> Just check for the presence of the function you want to call. In
> Xcode, set your deployment target to 10.4, so that Leopard-only
> symbols will be weak-linked. Then just check the symbol for NULL
> before calling it:
>
> if (SomeLeopardFunction != NULL) {
> SomeLeopardFunction();
> } else {
> TigerFunction();
> }
If you want to make sure that you don't include any "old" code in
your executable when you decide to make 10.5 (for example) your base
OS version, you could arrange your code like this:
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
if (SomeLeopardFunction == NULL)
TigerFunction();
else
#endif
SomeLeopardFunction();
...then the compiler will take care of sorting out the details for
you. If you use that sequence in several places, it might even be
worthwhile to create an inline wrapper function in a header file so
the OS-specific details are kept in one place:
inline void SomeFunction()
{
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
if (SomeLeopardFunction == NULL)
TigerFunction();
else
#endif
SomeLeopardFunction();
}
steve -
One way that I've seen used is to do this:
BOOL isLeopardOrBetter = YES;
Class gcClass = NSClassFromString(@"NSGarbageCollector");
if (gcClass == nil) { isLeopardOrBetter = NO; }
Cheers,
Dave -
On Jul 2, 2009, at 4:29 PM, Steve Christensen wrote:
> On Jul 1, 2009, at 5:22 PM, Sherm Pendley wrote:
>
>> On Wed, Jul 1, 2009 at 7:24 PM,
>> iseecolors<iseecolors...> wrote:
>>> I need to support 10.4 in my application, but it uses some Carbon
>>> APIs that
>>> are deprecated in 10.5 and I am using some new 10.5 APIs that
>>> require the
>>> 10.5 SDK.
>>>
>>> I am sure I have seen this before, but I have been unable to find
>>> it in the
>>> Archive. How do I determine at runtime which OS version I am
>>> running on?
>>
>> Just check for the presence of the function you want to call. In
>> Xcode, set your deployment target to 10.4, so that Leopard-only
>> symbols will be weak-linked. Then just check the symbol for NULL
>> before calling it:
>>
>> if (SomeLeopardFunction != NULL) {
>> SomeLeopardFunction();
>> } else {
>> TigerFunction();
>> }
>
> If you want to make sure that you don't include any "old" code in
> your executable when you decide to make 10.5 (for example) your
> base OS version, you could arrange your code like this:
>
> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
> if (SomeLeopardFunction == NULL)
> TigerFunction();
> else
> #endif
> SomeLeopardFunction();
>
> ...then the compiler will take care of sorting out the details for
> you. If you use that sequence in several places, it might even be
> worthwhile to create an inline wrapper function in a header file so
> the OS-specific details are kept in one place:
>
> inline void SomeFunction()
> {
> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
> if (SomeLeopardFunction == NULL)
> TigerFunction();
> else
> #endif
> SomeLeopardFunction();
> }
After I sent the message, I realized that a better situation for your
case would be to use
#ifndef NSAppKitVersionNumber10_5
#define NSAppKitVersionNumber10_5 949
#endif
if (NSAppKitVersionNumber < NSAppKitVersionNumber10_5)
...
since it will only use the Tiger method on Tiger.
steve -
On Thu, Jul 2, 2009 at 4:33 PM, Dave DeLong<davedelong...> wrote:
> One way that I've seen used is to do this:
>
> BOOL isLeopardOrBetter = YES;
> Class gcClass = NSClassFromString(@"NSGarbageCollector");
> if (gcClass == nil) { isLeopardOrBetter = NO; }
Don't do that. Check for the specific feature that you want to use.
I.e. this code is fine if you want to check for the presence of
NSGarbageCollector, however, that's exceeding unlikely, as you already
know whether or not your code supports GC at compile time.
--
Clark S. Cox III
<clarkcox3...> -
On 2 Jul 2009, at 16:29, Steve Christensen wrote:
> If you want to make sure that you don't include any "old" code in
> your executable when you decide to make 10.5 (for example) your base
> OS version, you could arrange your code like this:
>
> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
> if (SomeLeopardFunction == NULL)
> TigerFunction();
> else
> #endif
> SomeLeopardFunction();
Preprocessor directives take effect at compile time, not at runtime. -
On Jul 2, 2009, at 9:56 PM, Andrew Farmer wrote:
> On 2 Jul 2009, at 16:29, Steve Christensen wrote:
>> If you want to make sure that you don't include any "old" code in
>> your executable when you decide to make 10.5 (for example) your
>> base OS version, you could arrange your code like this:
>>
>> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
>> if (SomeLeopardFunction == NULL)
>> TigerFunction();
>> else
>> #endif
>> SomeLeopardFunction();
>
> Preprocessor directives take effect at compile time, not at runtime.
Yes, I'm aware of that. My point was that by writing the code in this
fashion, when you start building your software to -require- a minimum
of 10.5, any pre-10.5 code will be compiled out because then
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 so all you're
left with is the call to SomeLeopardFunction(). -
You might also just use Gestalt:
BOOL IsLeopardOrLater(void)
{
SInt32 vers;
Gestalt(gestaltSystemVersion,&vers);
return (vers >= 0x1050);
}
On Fri, Jul 3, 2009 at 6:59 AM, Steve Christensen<punster...> wrote:
> On Jul 2, 2009, at 9:56 PM, Andrew Farmer wrote:
>
>> On 2 Jul 2009, at 16:29, Steve Christensen wrote:
>>>
>>> If you want to make sure that you don't include any "old" code in your
>>> executable when you decide to make 10.5 (for example) your base OS version,
>>> you could arrange your code like this:
>>>
>>> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
>>> if (SomeLeopardFunction == NULL)
>>> TigerFunction();
>>> else
>>> #endif
>>> SomeLeopardFunction();
>>
>> Preprocessor directives take effect at compile time, not at runtime.
>
> Yes, I'm aware of that. My point was that by writing the code in this
> fashion, when you start building your software to -require- a minimum of
> 10.5, any pre-10.5 code will be compiled out because then
> MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 so all you're left
> with is the call to SomeLeopardFunction().
>
--
Mark Munz
unmarked software
http://www.unmarked.com/ -
For the case of a function, if you're deploying on 10.4 but using a
10.5 function, that function will be weak-linked so doing a runtime
check is both faster and more accurate in determining whether a
function exists or not. For a Cocoa class method, you could replace
the test with a call to -respondsToSelector: to determine if it
exists or not. In both cases you're taking a feature-based approach
rather than a "I know this feature showed up in this OS version"
approach.
And if you did actually need a IsLeopardOrLater function, it would be
better to cache the Gestalt result once since it won't change while
your app is running. Otherwise you're making a selector value lookup
each time.
BOOL IsLeopardOrLater(void)
{
static SInt32 sVersion = 0;
if (sVersion == 0)
Gestalt(gestaltSystemVersion, &sVersion);
return (sVersion >= 0x1050);
}
On Jul 3, 2009, at 9:00 AM, Mark Munz wrote:
> You might also just use Gestalt:
>
> BOOL IsLeopardOrLater(void)
> {
> SInt32 vers;
> Gestalt(gestaltSystemVersion,&vers);
> return (vers >= 0x1050);
> }
>
>
> On Fri, Jul 3, 2009 at 6:59 AM, Steve Christensen<punster...>
> wrote:
>> On Jul 2, 2009, at 9:56 PM, Andrew Farmer wrote:
>>
>>> On 2 Jul 2009, at 16:29, Steve Christensen wrote:
>>>>
>>>> If you want to make sure that you don't include any "old" code
>>>> in your
>>>> executable when you decide to make 10.5 (for example) your base
>>>> OS version,
>>>> you could arrange your code like this:
>>>>
>>>> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
>>>> if (SomeLeopardFunction == NULL)
>>>> TigerFunction();
>>>> else
>>>> #endif
>>>> SomeLeopardFunction();
>>>
>>> Preprocessor directives take effect at compile time, not at runtime.
>>
>> Yes, I'm aware of that. My point was that by writing the code in this
>> fashion, when you start building your software to -require- a
>> minimum of
>> 10.5, any pre-10.5 code will be compiled out because then
>> MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 so all
>> you're left
>> with is the call to SomeLeopardFunction().
-
On Jul 3, 2009, at 10:06 AM, Steve Christensen wrote:
> For the case of a function, if you're deploying on 10.4 but using a
> 10.5 function, that function will be weak-linked so doing a runtime
> check is both faster and more accurate in determining whether a
> function exists or not. For a Cocoa class method, you could replace
> the test with a call to -respondsToSelector: to determine if it
> exists or not. In both cases you're taking a feature-based approach
> rather than a "I know this feature showed up in this OS version"
> approach.
Just as a caution, you may run across functions that don't have the
appropriate weak attribute in the header, so you'll crash at launch
time on 10.4 (this bit me recently with CFStringTokenizer, rdar://problem/6781636
) So if you're checking function pointers against NULL, check the
header to make sure they have the correct accessibility macro.
--
Adam -
On Jul 3, 2009, at 1:06 PM, Steve Christensen wrote:
> For the case of a function, if you're deploying on 10.4 but using a
> 10.5 function, that function will be weak-linked so doing a runtime
> check is both faster and more accurate in determining whether a
> function exists or not.
Beware that there have been edge cases in the past where a non-NULL
function test will give you a false positive with disastrous results.
It goes like this:
if (FunctionName != NULL) {
FunctionName(x, y, z);
}
FunctionName was SPI on 10.x but (inadvertently) exported, but API on
10.y.
When running on 10.x, your NULL function pointer test will pass, but
this is SPI and isn't supported for use. The signature may have
changed, or the range of valid input values may be a subset of the
supported ones on 10.y.
This is not a theoretical problem, but one which has happened in the
past.
What is the take home lesson here? Test all branches of the code you
write.
Jim



