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++?
ole
(Ole Begemann)
2
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
danhalliday
(Dan Halliday)
3
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