From 3b80f614f0475577372122fa201878de7ba24b16 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Tue, 22 Oct 2024 12:18:16 +0200 Subject: [PATCH] update: forward declarations in c and objc (#4477) --- .../multiplatform-compatibility-guide.md | 88 +++++++++---------- docs/topics/native/native-c-interop.md | 48 +++++++++- docs/topics/native/native-objc-interop.md | 55 +++++++++++- 3 files changed, 145 insertions(+), 46 deletions(-) diff --git a/docs/topics/multiplatform/multiplatform-compatibility-guide.md b/docs/topics/multiplatform/multiplatform-compatibility-guide.md index 43386cc6822..384cbd062b0 100644 --- a/docs/topics/multiplatform/multiplatform-compatibility-guide.md +++ b/docs/topics/multiplatform/multiplatform-compatibility-guide.md @@ -914,8 +914,7 @@ Here's the planned deprecation cycle: **What's changed?** -The JetBrains team has revamped the approach to forward declarations in Kotlin to make their behavior more predictable -and prepare this functionality for the upcoming Kotlin 2.0 release. From now on: +The JetBrains team has revamped the approach to forward declarations in Kotlin to make their behavior more predictable: * You can only import forward declarations using the `cnames` or ` objcnames` packages. * You need to explicitly make a cast to and from the corresponding C and Objective-C forward declaration. @@ -927,48 +926,49 @@ and prepare this functionality for the upcoming Kotlin 2.0 release. From now on: Now, you can only use a special forward declaration package for that: `import cnames.structs.cstructName`. The same is true for `objcnames`. -* Consider two objcinterop libraries, one that uses `objcnames.protocols.ForwardDeclaredProtocolProtocol` and the other that has an actual definition: - - ```ObjC - // First objcinterop library - #import - - @protocol ForwardDeclaredProtocol; - - NSString* consumeProtocol(id s) { - return [NSString stringWithUTF8String:"Protocol"]; - } - ``` - - ```ObjC - // Second objcinterop library - // Header: - #import - @protocol ForwardDeclaredProtocol - @end - // Implementation: - @implementation ForwardDeclaredProtocolImpl : NSObject - @end; - - id produceProtocol() { - return [ForwardDeclaredProtocolImpl new]; - } - ``` - - Previously, it was possible to transfer objects between them seamlessly. Now, an explicit `as` cast is required - for the forward declaration: - - ```kotlin - // Kotlin code: - fun test() { - consumeProtocol(produceProtocol() as objcnames.protocols.ForwardDeclaredProtocolProtocol) - } - ``` - - > The casting to `objcnames.protocols.ForwardDeclaredProtocolProtocol` is only allowed from the corresponding real class. - > Otherwise, you'll get an error. - > - {style="note"} +* Consider two objcinterop libraries: one that uses `objcnames.protocols.ForwardDeclaredProtocolProtocol` and another + that has an actual definition: + + ```ObjC + // First objcinterop library + #import + + @protocol ForwardDeclaredProtocol; + + NSString* consumeProtocol(id s) { + return [NSString stringWithUTF8String:"Protocol"]; + } + ``` + + ```ObjC + // Second objcinterop library + // Header: + #import + @protocol ForwardDeclaredProtocol + @end + // Implementation: + @interface ForwardDeclaredProtocolImpl : NSObject + @end + + id produceProtocol() { + return [ForwardDeclaredProtocolImpl new]; + } + ``` + + Previously, it was possible to transfer objects between them seamlessly. Now, an explicit `as` cast is required + for the forward declaration: + + ```kotlin + // Kotlin code: + fun test() { + consumeProtocol(produceProtocol() as objcnames.protocols.ForwardDeclaredProtocolProtocol) + } + ``` + + > You can only cast to `objcnames.protocols.ForwardDeclaredProtocolProtocol` from the corresponding real class. + > Otherwise, you'll get an error. + > + {style="note"} **When do the changes take effect?** diff --git a/docs/topics/native/native-c-interop.md b/docs/topics/native/native-c-interop.md index b19fec45fd9..9cbf7074a9b 100644 --- a/docs/topics/native/native-c-interop.md +++ b/docs/topics/native/native-c-interop.md @@ -458,4 +458,50 @@ fun readData(fd: Int) { ``` Here we use service function `usePinned`, which pins an object, executes block and unpins it on normal and -exception paths. \ No newline at end of file +exception paths. + +### Forward declarations + +To import forward declarations, use the `cnames` package. For example, to import a `cstructName` forward declaration +declared in a C library with a `library.package`, use a special forward declaration package: +`import cnames.structs.cstructName`. + +Consider two cinterop libraries: one that has a forward declaration of a struct and another +with an actual implementation in another package: + +```C +// First C library +#include + +struct ForwardDeclaredStruct; + +void consumeStruct(struct ForwardDeclaredStruct* s) { + printf("Struct consumed\n"); +} +``` + +```C +// Second C library +// Header: +#include + +struct ForwardDeclaredStruct { + int data; +}; + +// Implementation: +struct ForwardDeclaredStruct* produceStruct() { + struct ForwardDeclaredStruct* s = malloc(sizeof(struct ForwardDeclaredStruct)); + s->data = 42; + return s; +} +``` + +To transfer objects between the two libraries, use an explicit `as` cast in you Kotlin code: + +```kotlin +// Kotlin code: +fun test() { + consumeStruct(produceStruct() as CPointer) +} +``` diff --git a/docs/topics/native/native-objc-interop.md b/docs/topics/native/native-objc-interop.md index b8252a0b6e6..b111214fcdc 100644 --- a/docs/topics/native/native-objc-interop.md +++ b/docs/topics/native/native-objc-interop.md @@ -232,7 +232,11 @@ A Swift/Objective-C initializer is imported to Kotlin as constructors or as fact The latter happens with initializers declared in the Objective-C category or as a Swift extension, because Kotlin has no concept of extension constructors. -Kotlin constructors are imported as initializers to Swift/Objective-C. +> Before importing Swift initializers to Kotlin, don't forget to annotate them with `@objc`. +> +{style="tip"} + +Kotlin constructors are imported as initializers to Swift/Objective-C. ### Setters @@ -573,6 +577,55 @@ binaries.framework { } ``` +### Forward declarations + +To import forward declarations, use the `objcnames.classes` and `objcnames.protocols` packages. For example, +to import a `objcprotocolName` forward declaration declared in an Objective-C library with a `library.package`, +use a special forward declaration package: `import objcnames.protocols.objcprotocolName`. + +Consider two objcinterop libraries: one that uses `objcnames.protocols.ForwardDeclaredProtocolProtocol` +and another with an actual implementation in another package: + +```ObjC +// First objcinterop library +#import + +@protocol ForwardDeclaredProtocol; + +NSString* consumeProtocol(id s) { + return [NSString stringWithUTF8String:"Protocol"]; +} +``` + +```ObjC +// Second objcinterop library +// Header: +#import +@protocol ForwardDeclaredProtocol +@end +// Implementation: +@interface ForwardDeclaredProtocolImpl : NSObject +@end + +id produceProtocol() { + return [ForwardDeclaredProtocolImpl new]; +} +``` + +To transfer objects between the two libraries, use an explicit `as` cast in you Kotlin code: + +```kotlin +// Kotlin code: +fun test() { + consumeProtocol(produceProtocol() as objcnames.protocols.ForwardDeclaredProtocolProtocol) +} +``` + +> Ypu can only cast to `objcnames.protocols.ForwardDeclaredProtocolProtocol` from the corresponding real class. +> Otherwise, you'll get an error. +> +{style="note"} + ## Casting between mapped types When writing Kotlin code, an object may need to be converted from a Kotlin type to the equivalent Swift/Objective-C type