Access a Package’s `Bundle.module` From C/C++

Apologies if this is straightforward. I have a package that contains C/C++ code and I want to load some resources from inside it. I’ve got it working by cobbling together the following monstrosity, where I hard-code the path to the target’s bundle.

But what I really want is to get that dynamically as you would using Bundle.module in Swift. Is there a sensible way to do this using CoreFoundation APIs?

#include <CoreFoundation/CFBundle.h>

std::string model_path() {
    CFURLRef url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
    char buffer[PATH_MAX];

    if (CFURLGetFileSystemRepresentation(url, true, (UInt8 *)buffer, PATH_MAX)) {
        if (url != NULL) {
            CFRelease(url);
        }

        std::string path = buffer;
        path += "/MyPackageName_MyTargetName.bundle/models/some_model.onnx";
        return path;
    }
}

I guess to put it another way, what’s the shortest way to write Bundle.module.path(forResource: "some_model", ofType: "onnx") in C/C++?

I haven't tried this, but I'm almost certain SwiftPM only generates a resource bundle accessor for Swift targets and for Clang targets that contain Objective-C.

From SE-0271:

For Objective-C, the build system will add a preprocessor define called SWIFTPM_MODULE_BUNDLE which can be used to access the bundle from any .m file.

And here's the relevant SwiftPM source code:

    /// Generate the resource bundle accessor, if appropriate.
    private func generateResourceAccessor() throws {
        // Only generate access when we have a bundle and ObjC files.
        guard let bundlePath = self.bundlePath, clangTarget.sources.containsObjcFiles else { return }
        …
1 Like

Thanks a lot @ole! Sure enough I can see the preprocessor define from Objective-C files in the target. For my use case this works fine, and I can just add a little Objective-C++ helper to my target.

#import <Foundation/Foundation.h>

__BEGIN_DECLS

NSBundle* Package_Target_SWIFTPM_MODULE_BUNDLE(void);

#define SWIFTPM_MODULE_BUNDLE Package_Target_SWIFTPM_MODULE_BUNDLE()

__END_DECLS

Here’s a quick first pass at a helper for anyone looking to get package resource paths from C++. Corrections welcome — I’ve no idea if the memory management and string encoding is right here.

PathForResource.h

#include <string>

std::string PathForResource(std::string name, std::string extension);

PathForResource.mm (must have mm extension)

#import "PathForResource.h"

std::string PathForResource(std::string name, std::string extension) {
    NSString *objcName = [NSString stringWithCString:name.c_str() encoding:NSASCIIStringEncoding];
    NSString *objcExtension = [NSString stringWithCString:extension.c_str() encoding:NSASCIIStringEncoding];
    NSString *objcPath = [SWIFTPM_MODULE_BUNDLE pathForResource:objcName ofType:objcExtension];

    return std::string([objcPath cStringUsingEncoding:NSASCIIStringEncoding]);
}
1 Like