Skip to content

Commit 0308404

Browse files
committed
[interop][SwiftToCxx] update reverse interop user guide for new enum design
1 parent eb7a23f commit 0308404

File tree

1 file changed

+78
-22
lines changed

1 file changed

+78
-22
lines changed

docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ int main() {
424424

425425
## Using Swift Enumeration Types
426426

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.
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 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.
428428

429429
For example, given the following enum:
430430

@@ -444,32 +444,44 @@ The following interface will be generated:
444444
// "Navigation-Swift.h" - C++ interface for Swift's Navigation module.
445445
class CompassDirection {
446446
public:
447-
static const CompassDirection north;
448-
static const CompassDirection south;
449-
static const CompassDirection east;
450-
static const CompassDirection west;
447+
static const struct { ... } north;
448+
static const struct { ... } south;
449+
static const struct { ... } east;
450+
static const struct { ... } west;
451+
private:
452+
// type representation details.
453+
...
451454
};
452455
```
453456
457+
This will let you construct enumeration values from C++ using the C++ call operator on the case:
458+
459+
```c++
460+
#include "Navigation-Swift.h"
461+
462+
void testConstructEnumValue() {
463+
auto direction = CompassDirection::north();
464+
}
465+
```
466+
454467
### Matching Swift Enumeration Values with a C++ Switch Statement
455468

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:
469+
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:
457470

458471
```c++
459472
#include "Navigation-Swift.h"
460473
using namespace Navigation;
461474

462475
CompassDirection getOpposite(CompassDirection cd) {
463476
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;
477+
case CompassDirection::north:
478+
return CompassDirection::south();
479+
case CompassDirection::south:
480+
return CompassDirection::north();
481+
case CompassDirection::east:
482+
return CompassDirection::west();
483+
case CompassDirection::west:
484+
return CompassDirection::east();
473485
}
474486
}
475487
```
@@ -533,6 +545,9 @@ Will get a C++ interface that resembles this class:
533545
class Barcode {
534546
public:
535547
Barcode() = delete;
548+
549+
static const struct { ... } qrCode;
550+
static const struct { ... } upc;
536551

537552
bool isUpc() const;
538553

@@ -547,29 +562,70 @@ public:
547562

548563
// Extracts an associated value from Barcode.qrCode enum case
549564
swift::String getQrCode() const;
550-
551-
static Barcode initUpc(swift::Int, swift::Int, swift::Int, swift::Int);
552-
static Barcode initQrCode(swift::String);
553565
};
554566
```
555567
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:
568+
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:
557569
558570
```c++
559571
#include "Store-Swift.h"
560572
using namespace Store;
561573
562574
Barcode normalizeBarcode(Barcode barcode) {
563-
if (barcode.isQrCode()) {
575+
switch (barcode) {
576+
case Barcode::qrCode: {
564577
auto qrCode = barcode.getQrCode();
565578
swift::Array<swift::Int> loadedBarcode = loadQrCode(qrCode);
566-
return Barcode::initUpc(loadedBarcode[0], loadedBarcode[1], loadedBarcode[2], loadedBarcode[3]);
579+
return Barcode::upc(loadedBarcode[0], loadedBarcode[1], loadedBarcode[2], loadedBarcode[3]);
567580
}
581+
case Barcode::upc:
582+
return barcode;
583+
}
584+
}
585+
```
568586

569-
return barcode;
587+
The use of a `get` associated value accessor for an invalid enum case for the given
588+
enum value will abort the program.
589+
590+
### Resilient Enums
591+
592+
A resilient Swift enumeration value could represent a case that's unknown to the client.
593+
Swift forces the client to check if the value is `@uknown default` when switching over
594+
the enumeration to account for that. C++ follows a similar principle,
595+
by exposing an `unknown_default` case that can then be matched in a switch.
596+
597+
For example, given the following resilient enumeration:
598+
599+
```swift
600+
// Swift module 'DateTime'
601+
enum DateFormatStyle {
602+
case medium
603+
case full
604+
}
605+
```
606+
607+
In C++, you need do an exhaustive switch over all cases and the unknown default
608+
case to avoid any compiler warnings:
609+
610+
```c++
611+
using namespace DateTime;
612+
void test(const DateFormatStyle &style) {
613+
switch (style) {
614+
case DateFormatStyle::medium:
615+
...
616+
break;
617+
case DateFormatStyle::full:
618+
...
619+
break;
620+
case DateFormatStyle::unknown_default: // just like Swift's @unknown default
621+
// Some case value added in a future version of enum.
622+
break;
623+
}
570624
}
571625
```
572626
627+
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++.
628+
573629
## Using Swift Class Types
574630
575631
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)