Skip to content

Commit 0cc2ee1

Browse files
authored
Merge pull request #60524 from hyp/eng/enum-redesign-docs
[interop][SwiftToCxx] update reverse interop user guide for new enum design
2 parents 4245bd5 + f212db5 commit 0cc2ee1

File tree

1 file changed

+83
-24
lines changed

1 file changed

+83
-24
lines changed

docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
A Swift library author might want to expose their interface to C++, to allow a C++ codebase to interoperate with the Swift library. This document describes how this can be accomplished, by first describing how Swift can expose its interface to C++, and then going into the details on how to use Swift APIs from C++.
44

5-
**NOTE:** This is a work-in-progress, living guide document for how Swift APIs can be imported and used from C++.
5+
6+
**NOTE:** This is a work-in-progress, living guide document for how Swift APIs can be imported and used from C++. This document reflects the current state of the experimental design, and it will evolve over time
7+
as this feature will go through Swift's evolution process. This document does not specify the final target
8+
design for the Swift to C++ interoperability layer.
69

710
**NOTE:** This document does not go over the following Swift language features yet:
811

@@ -71,7 +74,7 @@ Fundamental primitive types have a C++ fundamental type that represents them in
7174
|--- |--- |--- |--- |--- |
7275
|Void (or no return) |void | | | |
7376
|Int |swift::Int |ptrdiff_t |long or long long (windows) |YES |
74-
|UInt |size_t | |unsigned long or unsigned long long (windows) |YES |
77+
|UInt |swift::UInt | size_t |unsigned long or unsigned long long (windows) |YES |
7578
|Float |float | | | |
7679
|Double |double | | | |
7780
| | | | | |
@@ -424,7 +427,7 @@ int main() {
424427

425428
## Using Swift Enumeration Types
426429

427-
A Swift enumeration is imported as class in C++. That allows C++ to invoke methods and access properties that the enumeration provides. Each enumeration case that doesn’t have associated value is exposed as a static variable in the structure.
430+
A Swift enumeration is imported as class in C++. That allows C++ to invoke methods and access properties that the enumeration provides. Each enumeration case is represented by a static variable that can be used in a switch to match the case of the enum, and to construct new enums values as well.
428431

429432
For example, given the following enum:
430433

@@ -444,32 +447,44 @@ The following interface will be generated:
444447
// "Navigation-Swift.h" - C++ interface for Swift's Navigation module.
445448
class CompassDirection {
446449
public:
447-
static const CompassDirection north;
448-
static const CompassDirection south;
449-
static const CompassDirection east;
450-
static const CompassDirection west;
450+
static const struct { ... } north;
451+
static const struct { ... } south;
452+
static const struct { ... } east;
453+
static const struct { ... } west;
454+
private:
455+
// type representation details.
456+
...
451457
};
452458
```
453459
460+
This will let you construct enumeration values from C++ using the C++ call operator on the case:
461+
462+
```c++
463+
#include "Navigation-Swift.h"
464+
465+
void testConstructEnumValue() {
466+
auto direction = CompassDirection::north();
467+
}
468+
```
469+
454470
### Matching Swift Enumeration Values with a C++ Switch Statement
455471

456-
Swift’s enumerations can not be used directly in a switch, as C++ does not allow a `switch` to operate on C++ classes. However, For Swift enumerations that have an underlying integer representation, the generated C++ interface provides a convenience C++ enum called `cases` inside of the generated C++ class that represents the enumeration. This C++ enum can then be used in a switch, as the class that represents the enumeration implicitly converts to it. The `cases` C++ enum allows us to switch over the `CompassDirection` class from the example above in the following manner:
472+
The C++ values that correspond to Swift enumeration case values can be used directly inside the switch statement. The generated C++ interface provides a convenience C++ enum called cases inside of the generated C++ class that represents the enumeration that the switch actually operates over. This C++ enum can then be used in a switch, as the class that represents the enumeration implicitly converts to it, and so do the C++ case values. This allows us to switch over the CompassDirection class from the example above in the following manner:
457473

458474
```c++
459475
#include "Navigation-Swift.h"
460476
using namespace Navigation;
461477

462478
CompassDirection getOpposite(CompassDirection cd) {
463479
switch (cd) { // implicit conversion to CompassDirection::cases
464-
using enum CompassDirection::cases; // allow name lookup to find enum cases.
465-
case north:
466-
return CompassDirection::south;
467-
case south:
468-
return CompassDirection::north;
469-
case east:
470-
return CompassDirection::west;
471-
case west:
472-
return CompassDirection::east;
480+
case CompassDirection::north:
481+
return CompassDirection::south();
482+
case CompassDirection::south:
483+
return CompassDirection::north();
484+
case CompassDirection::east:
485+
return CompassDirection::west();
486+
case CompassDirection::west:
487+
return CompassDirection::east();
473488
}
474489
}
475490
```
@@ -533,6 +548,9 @@ Will get a C++ interface that resembles this class:
533548
class Barcode {
534549
public:
535550
Barcode() = delete;
551+
552+
static const struct { ... } qrCode;
553+
static const struct { ... } upc;
536554

537555
bool isUpc() const;
538556

@@ -547,29 +565,70 @@ public:
547565

548566
// Extracts an associated value from Barcode.qrCode enum case
549567
swift::String getQrCode() const;
550-
551-
static Barcode initUpc(swift::Int, swift::Int, swift::Int, swift::Int);
552-
static Barcode initQrCode(swift::String);
553568
};
554569
```
555570
556-
The C++ user of this enumeration can then use it by checking the type of the value and getting the associated value using the `is` and `get` member functions:
571+
The C++ user of this enumeration can then use it by checking the type of the value in a switch and getting the associated value using the get member functions:
557572
558573
```c++
559574
#include "Store-Swift.h"
560575
using namespace Store;
561576
562577
Barcode normalizeBarcode(Barcode barcode) {
563-
if (barcode.isQrCode()) {
578+
switch (barcode) {
579+
case Barcode::qrCode: {
564580
auto qrCode = barcode.getQrCode();
565581
swift::Array<swift::Int> loadedBarcode = loadQrCode(qrCode);
566-
return Barcode::initUpc(loadedBarcode[0], loadedBarcode[1], loadedBarcode[2], loadedBarcode[3]);
582+
return Barcode::upc(loadedBarcode[0], loadedBarcode[1], loadedBarcode[2], loadedBarcode[3]);
567583
}
584+
case Barcode::upc:
585+
return barcode;
586+
}
587+
}
588+
```
568589

569-
return barcode;
590+
The use of a `get` associated value accessor for an invalid enum case for the given
591+
enum value will abort the program.
592+
593+
### Resilient Enums
594+
595+
A resilient Swift enumeration value could represent a case that's unknown to the client.
596+
Swift forces the client to check if the value is `@uknown default` when switching over
597+
the enumeration to account for that. C++ follows a similar principle,
598+
by exposing an `unknown_default` case that can then be matched in a switch.
599+
600+
For example, given the following resilient enumeration:
601+
602+
```swift
603+
// Swift module 'DateTime'
604+
enum DateFormatStyle {
605+
case medium
606+
case full
607+
}
608+
```
609+
610+
In C++, you need do an exhaustive switch over all cases and the unknown default
611+
case to avoid any compiler warnings:
612+
613+
```c++
614+
using namespace DateTime;
615+
void test(const DateFormatStyle &style) {
616+
switch (style) {
617+
case DateFormatStyle::medium:
618+
...
619+
break;
620+
case DateFormatStyle::full:
621+
...
622+
break;
623+
case DateFormatStyle::unknown_default: // just like Swift's @unknown default
624+
// Some case value added in a future version of enum.
625+
break;
626+
}
570627
}
571628
```
572629
630+
The `unknown_default` case value is not a constructible case and you will get a compiler error if you try to construct it in C++.
631+
573632
## Using Swift Class Types
574633
575634
Swift class types that are usable from C++ are available in their corresponding module namespace. They’re bridged over as a C++ class that stores a referenced counted pointer inside of it. Its initializers, methods and properties are exposed as members of the C++ class.

0 commit comments

Comments
 (0)