Using NSProcessInfo from C++

2020-04-14 12:23发布

问题:

Is it possible to use [[NSProcessInfo processInfo] operatingSystemVersion] from C++ or C and how do I do that?

回答1:

In XCode and Objective-C, files with extension .mm are treated as "mixed" in the sense that one single file can contain C++/C notion as well as Objective-C code, and that even C++/C function implementations can access Objective-C classes and send messages to them.

Such a .mm-files can then provide a trampoline function, i.e. a C-style function that is exposed and can then be used in pure C++/C-files (extension .cpp, where Objective-C code must not occur).

Let's consider the following setting:

  1. A mixed file ProcessInfoProvider.mm, which calls [[NSProcessInfo processInfo] operatingSystemVersion] and provides the result in a C-style function std::string getProcessInfoVersion_C(),
  2. A Pure cpp-file and a corresponding hpp-file UseProcessInfoInC.cpp/hpp defining a class MyCppProcessInfo, which provides a printVersion()-member function and which uses getProcessInfoVersion_C,
  3. Some other file that instantiates MyCppProcessInfo and calls member function printVersion()

Let's start with ProcessInfoProvider.mm:

#import <Foundation/Foundation.h>
#import <Foundation/NSProcessInfo.h>
#include <string.h>
#include <iostream>

std::string getProcessInfoVersion_C (void) {
    NSProcessInfo *processInfo = [[NSProcessInfo alloc] init];

    // check avaiability of the property operatingSystemVersion (10.10+) at runtime
    if ([processInfo respondsToSelector:@selector(operatingSystemVersion)])
    {
        NSOperatingSystemVersion versionObj = [processInfo operatingSystemVersion];

        char version[500];
        snprintf(version, 500, "Version %ld.%ld.%ld",
                 (long)versionObj.majorVersion,
                 (long)versionObj.minorVersion,
                 (long)versionObj.patchVersion);

        return version;
    }
    else
    {
        // Implement fallback for OSX 10.9 and below
        return "?";
    }
}

Note that getProcessInfoVersion_C is a C-style function but contains objective-C code in it's implementation.

Then, pure C++ files UseProcessInfoInC.cpp/hpp implement class MyCppProcessInfo:

// UseProcessInfoInC.hpp:
#ifndef UseProcessInfoInC_hpp
#define UseProcessInfoInC_hpp

class MyCppProcessInfo {
public:
    void printVersion(void);
};

#endif /* UseProcessInfoInC_hpp */

and

// UseProcessInfoInC.cpp:
#include "UseProcessInfoInC.hpp"
#include <iostream>

extern std::string getProcessInfoVersion_C (void);

void MyCppProcessInfo::printVersion(void)
{
    std::string version = getProcessInfoVersion_C();

    std::cout << version;
}

Note that these two files do not contain any Objective-C-stuff. Finally, let's try out MyCppProcessInfo, e.g. in a function main():

// File main.mm
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

#include "UseProcessInfoInC.hpp"

int main(int argc, char * argv[]) {
    @autoreleasepool {

        MyCppProcessInfo pi;
        pi.printVersion();

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}