1
1
---
2
2
layout: page
3
- title: Safely Mixing Swift and C++
3
+ title: Safely Mixing Swift and C/C ++
4
4
official_url: https://swift.org/documentation/cxx-interop/safe-interop/
5
5
redirect_from:
6
6
- /documentation/cxx-interop/safe-interop.html
@@ -429,44 +429,44 @@ The most common bounds attribute is `__counted_by`. You can apply `__counted_by`
429
429
and return values to indicate the number of elements that the pointer points to, like this:
430
430
431
431
```c
432
- int calculate_sum(const int * __counted_by(len) values, int len);
432
+ int calculate_sum(const int * __counted_by(len) values [[clang::noescape]] , int len);
433
433
```
434
434
435
435
In this example the function signature on the C side hasn't changed, but the `__counted_by(len)`
436
436
annotation communicates that the `values` and `len` parameters are related - specifically, `values`
437
437
should point to a buffer of at least `len` `int` values. When you annotate a function with a bounds
438
438
attribute like this, the compiler will generate a bounds safe overload: in addition to the imported
439
439
`func calculate_sum(_ values: UnsafePointer<CInt>, _ len: CInt) -> CInt` signature, you will also
440
- get the an overload with the `func calculate_sum(_ values: UnsafeBufferPointer <CInt>) -> CInt` signature.
440
+ get the an overload with the `func calculate_sum(_ values: Span <CInt>) -> CInt` signature.
441
441
This signature is not only more ergonomic to work with - since the generated overload does the unpacking
442
442
of base pointer and count for you - but it's also bounds safe. For example, if your API contains parameters
443
443
that share a count, the bounds safe overload will check that they all correspond.
444
444
445
445
```c
446
- void sum_vectors(const int * __counted_by(len) a,
447
- const int * __counted_by(len) b,
448
- int * __counted_by(len) out,
446
+ void sum_vectors(const int * __counted_by(len) a [[clang::noescape]] ,
447
+ const int * __counted_by(len) b [[clang::noescape]] ,
448
+ int * __counted_by(len) out [[clang::noescape]] ,
449
449
int len);
450
450
```
451
451
This safe overload will trap if `a.count != b.count || b.count != out.count`:
452
452
```swift
453
- func sum_vectors(_ a: UnsafeBufferPointer <CInt>,
454
- _ b: UnsafeBufferPointer <CInt>,
455
- _ out: UnsafeMutableBufferPointer <CInt>)
453
+ func sum_vectors(_ a: Span <CInt>,
454
+ _ b: Span <CInt>,
455
+ _ out: MutableSpan <CInt>)
456
456
```
457
457
458
- If one of the `UnsafeBufferPointer ` parameters is larger than the others and you intentionally want to use
458
+ If one of the `Span ` parameters is larger than the others and you intentionally want to use
459
459
only a part of it, you can create a slice using `extracting(_:)`.
460
460
461
461
If your API has more complex bounds you can express those with an arithmetic expression, like so:
462
462
463
463
```c
464
- int transpose_matrix(int * __counted_by(columns * rows) values, int columns, int rows);
464
+ int transpose_matrix(int * __counted_by(columns * rows) values [[clang::noescape]] , int columns, int rows);
465
465
```
466
466
467
467
In this case the `columns` and `rows` parameters can't be elided:
468
468
```swift
469
- func transpose_matrix(_ values: UnsafeMutableBufferPointer <CInt>, _ columns: CInt, _ rows: CInt)
469
+ func transpose_matrix(_ values: MutableSpan <CInt>, _ columns: CInt, _ rows: CInt)
470
470
```
471
471
This is because there's no way to factor out `columns` and `rows` given only `values.count`.
472
472
Instead a bounds check is inserted to verify that `columns * rows == values.count`.
@@ -475,30 +475,38 @@ When your C/C++ API uses opaque pointers, void pointers or otherwise pointers to
475
475
the buffer size can't be described in terms of the number of elements. Instead you can annotate
476
476
these pointers with `__sized_by`. This bounds attribute behaves like `__counted_by`, but takes
477
477
a parameter describing the *number of bytes* in the buffer rather than the *number of elements*.
478
- Where `__counted_by` maps to `UnsafeBufferPointer <T>`, `__sized_by` will map to
479
- `UnsafeRawBufferPointer ` instead.
478
+ Where `__counted_by` maps to `Span <T>`, `__sized_by` will map to
479
+ `RawSpan ` instead.
480
480
481
481
You can access the `__counted_by` and `__sized_by` macro definitions by including the `ptrcheck.h` header.
482
+ For more information about these annotations, see Clang's [bounds safety documentation](https://clang.llvm.org/docs/BoundsSafety.html).
483
+ If the C code base is compiled with `-fbounds-safety` bounds safety is enforced on the C side as well -
484
+ otherwise it is only enforced at the interop boundary.
482
485
483
- #### Lifetime Safe Overloads for Pointers
486
+ #### Lifetime Annotations for Pointers
484
487
485
- Like with `std::span`, pointers with bounds annotations can also have their safe overloads map to
486
- `Span`/`MutableSpan`, when annotated with the appropriate lifetime annotations. Not all C versions
488
+ Like with `std::span`, pointers with bounds annotations can have their safe overloads map to
489
+ `Span`/`MutableSpan` when annotated with the appropriate lifetime annotations. Not all C versions
487
490
support the `[[clang::noescape]]` attribute syntax, so convenience macros for lifetime attributes
488
- using the GNU style syntax are available in the `lifetimebound.h` header.
491
+ using the GNU style syntax are available in the `lifetimebound.h` header. Unlike `std::span`, pointers
492
+ with bounds annotations also get a bounds safe overload when they lack lifetime annotations:
489
493
490
494
```c
491
495
#include <ptrcheck.h>
492
496
#include <lifetimebound.h>
493
- ````
497
+ ```
494
498
495
- | C API | Generated Swift overload |
496
- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
497
- | `void take_ptr(const int * __counted_by(len) x __noescape, int len);` | `func take_ptr(_ x: Span<Int32>)` |
498
- | `const int * __counted_by(len) change_ptr(const int * __counted_by(len) x __lifetimebound, int len);` | `@lifetime(x) func change_ptr(_ x: Span<Int32>) -> Span<Int32>` |
499
+ | C API | Generated Swift overload |
500
+ | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
501
+ | `void take_ptr_lifetime(const int * __counted_by(len) x __noescape, int len);` | `func take_ptr_lifetime(_ x: Span<Int32>)` |
502
+ | `const int * __counted_by(len) change_ptr_lifetime(const int * __counted_by(len) x __lifetimebound, int len);` | `@lifetime(x) func change_ptr_lifetime(_ x: Span<Int32>) -> Span<Int32>` |
503
+ | `void take_ptr(const int * __counted_by(len) x, int len);` | `func take_ptr(_ x: UnsafeBufferPointer<Int32>)` |
504
+ | `const int * __counted_by(len) change_ptr(const int * __counted_by(len) x, int len);` | `@lifetime(x) func change_ptr(_ x: UnsafeBufferPointer<Int32>) -> UnsafeBufferPointer<Int32>` |
499
505
500
- These overloads provide the same bounds safety as their `UnsafeBufferPointer` equvalents, but with
501
- added lifetime safety. If lifetime information is available the generated safe overload will always
506
+ The `UnsafeBufferPointer` overloads provide the same bounds safety as their `Span` equvalents
507
+ (NB: `UnsafeBufferPointer` is not bounds checked on memory access in release builds, but the generated
508
+ `UnsafeBufferPointer` overloads contain bounds checks in the same cases as the `Span` overloads, *even in release builds*),
509
+ but without lifetime safety. If lifetime information is available the generated safe overload will always
502
510
choose to use `Span` - no `UnsafeBufferPointer` overload will be generated in this case. This means
503
511
that existing callers are not affected by annotating an API with `__counted_by`, but callers using the
504
512
safe overload after adding `__counted_by` *will* be affected if `__noescape` is also added later on, or
0 commit comments