Pitch: @objc attribute for top-level functions


(Charles Srstka) #1

MOTIVATION:

Sometimes, it’s necessary to write a top-level C function in order to interact with some C-based code. This can come up, for example, when making a new port for a cross-platform app that implements OS-dependent functionality via C functions. More urgently, though, it also pops up in certain Apple APIs, such as the standard QuickLook plug-in template. In order to write a QuickLook plug-in, developers are required to write implementations for certain predefined C functions, such as GeneratePreviewForURL(), GenerateThumbnailForURL(), CancelPreviewGeneration(), and CancelThumbnailGeneration(). Unfortunately, this API contract cannot be met in Swift. Currently, Swift can only expose class members to C/Objective-C, which means that implementing a QuickLook plug-in involves a rather awkward series of hoops to jump through:

GeneratePreview.swift:

class QLGlue: NSObject {
  @objc static func generatePreview(_: UnsafeMutableRawPointer, request preview: QLPreviewRequest, url: URL, contentTypeUTI: String, options: [String : Any]) -> OSStatus {
    // generate the preview
  }
}

GeneratePreview.m:

#import “MyProject-Swift.h"

OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef request, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) {
    return [QLGlue generatePreview:thisInterface request:request url:(__bridge NSURL *)url contentTypeUTI:(__bridge NSString *)contentTypeUTI options:(__bridge NSDictionary *)options];
}

PROPOSED SOLUTION:

Allow the @objc on top-level functions. This will cause the functions to be exposed as C functions, satisfying contracts such as Apple’s QuickLook plug-in contract, and allowing developers to just write:

@objc func generatePreview(_: UnsafeMutableRawPointer, _ preview: QLPreviewRequest, _ url: CFURL, _ contentTypeUTI: CFString, _ options: CFDictionary) -> OSStatus {
  // generate the preview
}

This would eliminate an entire source file of glue code.

IMPACT ON EXISTING CODE:

None, since @objc is not currently allowed on top-level functions. This is purely additive.

Charles


(Joe Groff) #2

This would definitely be useful, even beyond ObjC interop. There is in fact an unfinished implementation of a `@_cdecl("symbolName")` attribute that you can use to export a global function as a C-compatible symbol. What's missing (IIRC) is bridging header support to expose the declaration back to C, compiler checking for symbol name collisions, and decoupling the validation from ObjC interop for non-Apple platforms—and, of course, a formal evolution proposal to make it an official part of the language. I don't think we want to call this @objc, since it isn't inherently tied to ObjC, and IMO it makes sense to require the exported symbol name to be required, since top-level unmangled symbols are a crowded namespace, and it would be good to ensure people think about the C symbol name they export.

-Joe

···

On Mar 31, 2017, at 8:00 AM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

MOTIVATION:

Sometimes, it’s necessary to write a top-level C function in order to interact with some C-based code. This can come up, for example, when making a new port for a cross-platform app that implements OS-dependent functionality via C functions. More urgently, though, it also pops up in certain Apple APIs, such as the standard QuickLook plug-in template. In order to write a QuickLook plug-in, developers are required to write implementations for certain predefined C functions, such as GeneratePreviewForURL(), GenerateThumbnailForURL(), CancelPreviewGeneration(), and CancelThumbnailGeneration(). Unfortunately, this API contract cannot be met in Swift. Currently, Swift can only expose class members to C/Objective-C, which means that implementing a QuickLook plug-in involves a rather awkward series of hoops to jump through:

GeneratePreview.swift:

class QLGlue: NSObject {
  @objc static func generatePreview(_: UnsafeMutableRawPointer, request preview: QLPreviewRequest, url: URL, contentTypeUTI: String, options: [String : Any]) -> OSStatus {
    // generate the preview
  }
}

GeneratePreview.m:

#import “MyProject-Swift.h"

OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef request, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) {
   return [QLGlue generatePreview:thisInterface request:request url:(__bridge NSURL *)url contentTypeUTI:(__bridge NSString *)contentTypeUTI options:(__bridge NSDictionary *)options];
}

PROPOSED SOLUTION:

Allow the @objc on top-level functions. This will cause the functions to be exposed as C functions, satisfying contracts such as Apple’s QuickLook plug-in contract, and allowing developers to just write:

@objc func generatePreview(_: UnsafeMutableRawPointer, _ preview: QLPreviewRequest, _ url: CFURL, _ contentTypeUTI: CFString, _ options: CFDictionary) -> OSStatus {
  // generate the preview
}

This would eliminate an entire source file of glue code.

IMPACT ON EXISTING CODE:

None, since @objc is not currently allowed on top-level functions. This is purely additive.


(Charlie Monroe) #3

+1, missed this on a number of occasions.

···

On Mar 31, 2017, at 5:00 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

MOTIVATION:

Sometimes, it’s necessary to write a top-level C function in order to interact with some C-based code. This can come up, for example, when making a new port for a cross-platform app that implements OS-dependent functionality via C functions. More urgently, though, it also pops up in certain Apple APIs, such as the standard QuickLook plug-in template. In order to write a QuickLook plug-in, developers are required to write implementations for certain predefined C functions, such as GeneratePreviewForURL(), GenerateThumbnailForURL(), CancelPreviewGeneration(), and CancelThumbnailGeneration(). Unfortunately, this API contract cannot be met in Swift. Currently, Swift can only expose class members to C/Objective-C, which means that implementing a QuickLook plug-in involves a rather awkward series of hoops to jump through:

GeneratePreview.swift:

class QLGlue: NSObject {
  @objc static func generatePreview(_: UnsafeMutableRawPointer, request preview: QLPreviewRequest, url: URL, contentTypeUTI: String, options: [String : Any]) -> OSStatus {
    // generate the preview
  }
}

GeneratePreview.m:

#import “MyProject-Swift.h"

OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef request, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) {
   return [QLGlue generatePreview:thisInterface request:request url:(__bridge NSURL *)url contentTypeUTI:(__bridge NSString *)contentTypeUTI options:(__bridge NSDictionary *)options];
}

PROPOSED SOLUTION:

Allow the @objc on top-level functions. This will cause the functions to be exposed as C functions, satisfying contracts such as Apple’s QuickLook plug-in contract, and allowing developers to just write:

@objc func generatePreview(_: UnsafeMutableRawPointer, _ preview: QLPreviewRequest, _ url: CFURL, _ contentTypeUTI: CFString, _ options: CFDictionary) -> OSStatus {
  // generate the preview
}

This would eliminate an entire source file of glue code.

IMPACT ON EXISTING CODE:

None, since @objc is not currently allowed on top-level functions. This is purely additive.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jean-Daniel) #4

We should also think about a way to specify the symbol visibility. It may be useful to export a global function to interact with C code, but that don’t mean we want to export it and make it visible outside of the module.
Maybe it is enough to say that ‘internal' symbols should be exported as « private extern » and ‘public' symbol should be exported a « global », but I think it is worse mentioning it in a formal proposal.

···

Le 1 avr. 2017 à 00:53, Joe Groff via swift-evolution <swift-evolution@swift.org> a écrit :

On Mar 31, 2017, at 8:00 AM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

MOTIVATION:

Sometimes, it’s necessary to write a top-level C function in order to interact with some C-based code. This can come up, for example, when making a new port for a cross-platform app that implements OS-dependent functionality via C functions. More urgently, though, it also pops up in certain Apple APIs, such as the standard QuickLook plug-in template. In order to write a QuickLook plug-in, developers are required to write implementations for certain predefined C functions, such as GeneratePreviewForURL(), GenerateThumbnailForURL(), CancelPreviewGeneration(), and CancelThumbnailGeneration(). Unfortunately, this API contract cannot be met in Swift. Currently, Swift can only expose class members to C/Objective-C, which means that implementing a QuickLook plug-in involves a rather awkward series of hoops to jump through:

GeneratePreview.swift:

class QLGlue: NSObject {
  @objc static func generatePreview(_: UnsafeMutableRawPointer, request preview: QLPreviewRequest, url: URL, contentTypeUTI: String, options: [String : Any]) -> OSStatus {
    // generate the preview
  }
}

GeneratePreview.m:

#import “MyProject-Swift.h"

OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef request, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) {
  return [QLGlue generatePreview:thisInterface request:request url:(__bridge NSURL *)url contentTypeUTI:(__bridge NSString *)contentTypeUTI options:(__bridge NSDictionary *)options];
}

PROPOSED SOLUTION:

Allow the @objc on top-level functions. This will cause the functions to be exposed as C functions, satisfying contracts such as Apple’s QuickLook plug-in contract, and allowing developers to just write:

@objc func generatePreview(_: UnsafeMutableRawPointer, _ preview: QLPreviewRequest, _ url: CFURL, _ contentTypeUTI: CFString, _ options: CFDictionary) -> OSStatus {
  // generate the preview
}

This would eliminate an entire source file of glue code.

IMPACT ON EXISTING CODE:

None, since @objc is not currently allowed on top-level functions. This is purely additive.

This would definitely be useful, even beyond ObjC interop. There is in fact an unfinished implementation of a `@_cdecl("symbolName")` attribute that you can use to export a global function as a C-compatible symbol. What's missing (IIRC) is bridging header support to expose the declaration back to C, compiler checking for symbol name collisions, and decoupling the validation from ObjC interop for non-Apple platforms—and, of course, a formal evolution proposal to make it an official part of the language. I don't think we want to call this @objc, since it isn't inherently tied to ObjC, and IMO it makes sense to require the exported symbol name to be required, since top-level unmangled symbols are a crowded namespace, and it would be good to ensure people think about the C symbol name they export.

-Joe


(Joe Groff) #5

It seems to me like the symbol visibility ought to follow the same rules as for Swift symbols.

-Joe

···

On Apr 1, 2017, at 6:48 AM, Jean-Daniel <mailing@xenonium.com> wrote:

Le 1 avr. 2017 à 00:53, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

On Mar 31, 2017, at 8:00 AM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

MOTIVATION:

Sometimes, it’s necessary to write a top-level C function in order to interact with some C-based code. This can come up, for example, when making a new port for a cross-platform app that implements OS-dependent functionality via C functions. More urgently, though, it also pops up in certain Apple APIs, such as the standard QuickLook plug-in template. In order to write a QuickLook plug-in, developers are required to write implementations for certain predefined C functions, such as GeneratePreviewForURL(), GenerateThumbnailForURL(), CancelPreviewGeneration(), and CancelThumbnailGeneration(). Unfortunately, this API contract cannot be met in Swift. Currently, Swift can only expose class members to C/Objective-C, which means that implementing a QuickLook plug-in involves a rather awkward series of hoops to jump through:

GeneratePreview.swift:

class QLGlue: NSObject {
  @objc static func generatePreview(_: UnsafeMutableRawPointer, request preview: QLPreviewRequest, url: URL, contentTypeUTI: String, options: [String : Any]) -> OSStatus {
    // generate the preview
  }
}

GeneratePreview.m:

#import “MyProject-Swift.h"

OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef request, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) {
  return [QLGlue generatePreview:thisInterface request:request url:(__bridge NSURL *)url contentTypeUTI:(__bridge NSString *)contentTypeUTI options:(__bridge NSDictionary *)options];
}

PROPOSED SOLUTION:

Allow the @objc on top-level functions. This will cause the functions to be exposed as C functions, satisfying contracts such as Apple’s QuickLook plug-in contract, and allowing developers to just write:

@objc func generatePreview(_: UnsafeMutableRawPointer, _ preview: QLPreviewRequest, _ url: CFURL, _ contentTypeUTI: CFString, _ options: CFDictionary) -> OSStatus {
  // generate the preview
}

This would eliminate an entire source file of glue code.

IMPACT ON EXISTING CODE:

None, since @objc is not currently allowed on top-level functions. This is purely additive.

This would definitely be useful, even beyond ObjC interop. There is in fact an unfinished implementation of a `@_cdecl("symbolName")` attribute that you can use to export a global function as a C-compatible symbol. What's missing (IIRC) is bridging header support to expose the declaration back to C, compiler checking for symbol name collisions, and decoupling the validation from ObjC interop for non-Apple platforms—and, of course, a formal evolution proposal to make it an official part of the language. I don't think we want to call this @objc, since it isn't inherently tied to ObjC, and IMO it makes sense to require the exported symbol name to be required, since top-level unmangled symbols are a crowded namespace, and it would be good to ensure people think about the C symbol name they export.

-Joe

We should also think about a way to specify the symbol visibility. It may be useful to export a global function to interact with C code, but that don’t mean we want to export it and make it visible outside of the module.
Maybe it is enough to say that ‘internal' symbols should be exported as « private extern » and ‘public' symbol should be exported a « global », but I think it is worse mentioning it in a formal proposal.


(Jordan Rose) #6

Note that we don't have a good way to do this with a single generated header. But we could just limit printing to public symbols, and force people to redeclare the signature on the C side otherwise for now.

Also missing: proper validation of what can be exposed to C on a platform without ObjC or bridging. We have a lot of tests for what is and isn't valid @objc, but very few for C, and there will be divergence in some places.

Jordan

···

On Apr 3, 2017, at 09:04, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 1, 2017, at 6:48 AM, Jean-Daniel <mailing@xenonium.com <mailto:mailing@xenonium.com>> wrote:

Le 1 avr. 2017 à 00:53, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

On Mar 31, 2017, at 8:00 AM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

MOTIVATION:

Sometimes, it’s necessary to write a top-level C function in order to interact with some C-based code. This can come up, for example, when making a new port for a cross-platform app that implements OS-dependent functionality via C functions. More urgently, though, it also pops up in certain Apple APIs, such as the standard QuickLook plug-in template. In order to write a QuickLook plug-in, developers are required to write implementations for certain predefined C functions, such as GeneratePreviewForURL(), GenerateThumbnailForURL(), CancelPreviewGeneration(), and CancelThumbnailGeneration(). Unfortunately, this API contract cannot be met in Swift. Currently, Swift can only expose class members to C/Objective-C, which means that implementing a QuickLook plug-in involves a rather awkward series of hoops to jump through:

GeneratePreview.swift:

class QLGlue: NSObject {
  @objc static func generatePreview(_: UnsafeMutableRawPointer, request preview: QLPreviewRequest, url: URL, contentTypeUTI: String, options: [String : Any]) -> OSStatus {
    // generate the preview
  }
}

GeneratePreview.m:

#import “MyProject-Swift.h"

OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef request, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) {
  return [QLGlue generatePreview:thisInterface request:request url:(__bridge NSURL *)url contentTypeUTI:(__bridge NSString *)contentTypeUTI options:(__bridge NSDictionary *)options];
}

PROPOSED SOLUTION:

Allow the @objc on top-level functions. This will cause the functions to be exposed as C functions, satisfying contracts such as Apple’s QuickLook plug-in contract, and allowing developers to just write:

@objc func generatePreview(_: UnsafeMutableRawPointer, _ preview: QLPreviewRequest, _ url: CFURL, _ contentTypeUTI: CFString, _ options: CFDictionary) -> OSStatus {
  // generate the preview
}

This would eliminate an entire source file of glue code.

IMPACT ON EXISTING CODE:

None, since @objc is not currently allowed on top-level functions. This is purely additive.

This would definitely be useful, even beyond ObjC interop. There is in fact an unfinished implementation of a `@_cdecl("symbolName")` attribute that you can use to export a global function as a C-compatible symbol. What's missing (IIRC) is bridging header support to expose the declaration back to C, compiler checking for symbol name collisions, and decoupling the validation from ObjC interop for non-Apple platforms—and, of course, a formal evolution proposal to make it an official part of the language. I don't think we want to call this @objc, since it isn't inherently tied to ObjC, and IMO it makes sense to require the exported symbol name to be required, since top-level unmangled symbols are a crowded namespace, and it would be good to ensure people think about the C symbol name they export.

-Joe

We should also think about a way to specify the symbol visibility. It may be useful to export a global function to interact with C code, but that don’t mean we want to export it and make it visible outside of the module.
Maybe it is enough to say that ‘internal' symbols should be exported as « private extern » and ‘public' symbol should be exported a « global », but I think it is worse mentioning it in a formal proposal.

It seems to me like the symbol visibility ought to follow the same rules as for Swift symbols.