Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update: forward declarations in c and objc #4477

Merged
merged 4 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 44 additions & 43 deletions docs/topics/multiplatform/multiplatform-compatibility-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +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:
and prepare this functionality for the Kotlin 2.0.0 release. From now on:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this line about Kotlin 2.0.0? In my opinion, the following line is more than enough:

"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.
Expand All @@ -926,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 <Foundation/Foundation.h>

@protocol ForwardDeclaredProtocol;

NSString* consumeProtocol(id<ForwardDeclaredProtocol> s) {
return [NSString stringWithUTF8String:"Protocol"];
}
```

```ObjC
// Second objcinterop library
// Header:
#import <Foundation/Foundation.h>
@protocol ForwardDeclaredProtocol
@end
// Implementation:
@implementation ForwardDeclaredProtocolImpl : NSObject
@end;

id<ForwardDeclaredProtocol> 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 the other
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Consider two objcinterop libraries, one that uses `objcnames.protocols.ForwardDeclaredProtocolProtocol` and the other
* Consider two objcinterop libraries: one that uses `objcnames.protocols.ForwardDeclaredProtocolProtocol` and another

that has an actual definition:

```ObjC
// First objcinterop library
#import <Foundation/Foundation.h>

@protocol ForwardDeclaredProtocol;

NSString* consumeProtocol(id<ForwardDeclaredProtocol> s) {
return [NSString stringWithUTF8String:"Protocol"];
}
```

```ObjC
// Second objcinterop library
// Header:
#import <Foundation/Foundation.h>
@protocol ForwardDeclaredProtocol
@end
// Implementation:
@implementation ForwardDeclaredProtocolImpl : NSObject
@end;

id<ForwardDeclaredProtocol> 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is better to get rid of the passive voice here:

You can only cast to objcnames.protocols.ForwardDeclaredProtocolProtocol from the corresponding real class. Otherwise, you'll get an error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you'd like to keep the original sentence, the first "The" article is not needed.

> Otherwise, you'll get an error.
>
{style="note"}

**When do the changes take effect?**

Expand Down
53 changes: 52 additions & 1 deletion docs/topics/native/native-c-interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -646,4 +646,55 @@ fun readData(fd: Int) {
```

Here we use service function `usePinned`, which pins an object, executes block and unpins it on normal and
exception paths.
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`.

SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
To transfer objects between libraries, you need to make an explicit `as` cast to and from the corresponding C
forward declaration. Consider two cinterop libraries, one that has a forward declaration of a struct and the other
with an actual implementation:

```C
// First C library
#include <stdio.h>

typedef struct ForwardDeclaredStruct ForwardDeclaredStruct;

void consumeStruct(ForwardDeclaredStruct* s) {
printf("Struct consumed\n");
}
```

```C
// Second C library
// Header:
#include <stdlib.h>

typedef struct {
int data;
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
} ForwardDeclaredStruct;

// Implementation:
ForwardDeclaredStruct* produceStruct() {
ForwardDeclaredStruct* s = malloc(sizeof(ForwardDeclaredStruct));
s->data = 42;
return s;
}
```

To transfer objects between them, make the `as` cast in you Kotlin code:

```kotlin
// Kotlin code:
fun test() {
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
consumeStruct(produceStruct() as cnames.structs.ForwardDeclaredStruct)
}
```

SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
> Casting is only allowed from the corresponding real class. Otherwise, you'll get an error.
>
{type="note"}
56 changes: 55 additions & 1 deletion docs/topics/native/native-objc-interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,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.
> Remember to expose Swift methods with `@objc` before importing Swift initializers to Kotlin.
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
>
{type="tip"}

Kotlin constructors are imported as initializers to Swift/Objective-C.

### Setters

Expand Down Expand Up @@ -569,6 +573,56 @@ binaries.framework {
}
```

### Forward declarations

To import forward declarations, use the `objcnames` package. For example, to import a `objcstructName` forward declaration
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
declared in an Objective-C library with a `library.package`, use a special forward declaration package:
`import objcnames.structs.objcstructName`.

To transfer objects between libraries, you need to make an explicit `as` cast to and from the corresponding Objective-C
forward declaration. Consider two objcinterop libraries, one that uses `objcnames.protocols.ForwardDeclaredProtocolProtocol`
and the other that has an actual definition:

```ObjC
// First objcinterop library
#import <Foundation/Foundation.h>

@protocol ForwardDeclaredProtocol;

NSString* consumeProtocol(id<ForwardDeclaredProtocol> s) {
return [NSString stringWithUTF8String:"Protocol"];
}
```

```ObjC
// Second objcinterop library
// Header:
#import <Foundation/Foundation.h>
@protocol ForwardDeclaredProtocol
@end
// Implementation:
@implementation ForwardDeclaredProtocolImpl : NSObject
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
@end;
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved

id<ForwardDeclaredProtocol> produceProtocol() {
return [ForwardDeclaredProtocolImpl new];
}
```

To transfer objects between them, make the `as` cast in you Kotlin code:

```kotlin
// Kotlin code:
fun test() {
consumeStruct(produceStruct() as cnames.structs.ForwardDeclaredStruct)
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
}
```

> The casting to `objcnames.protocols.ForwardDeclaredProtocolProtocol` is only allowed from the corresponding real class.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the suggestion above.

> Otherwise, you'll get an error.
>
{type="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
Expand Down
Loading