You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+34-27Lines changed: 34 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,9 +19,15 @@ An ever-growing collection of utilities to make coding on Apple platforms in C++
19
19
20
20
## What's included? ##
21
21
22
-
The library is a collection of mostly independent header files. There is nothing to link with. Simply add these headers to your include path and include them as needed.
22
+
The library is a collection of mostly independent header files. There is, generally, nothing to link with. You can integrate this library in multiple ways:
23
+
* Simply add the headers under the [include](include) directory to your include path and `#include` them as needed.
24
+
(For your convenience, the official [Releases][releases] have a .tar.gz containing the headers plus license/version info.)
25
+
* Via Xcode's "Add Package Dependencies". Point it to this repository URL. (And, no, you don't need to have any Swift in
26
+
your project for this to work).
27
+
* Using an XCFramework available from the [Releases][releases] page. Add it to your Xcode project.
28
+
* Via CMake, using all the usual methods: FetchContent, add_subdirectory, install locally etc.
23
29
24
-
The `sample` directory contains a sample that demonstrates the usage of main features.
30
+
The `sample` directory contains a sample that demonstrates the usage of the main features.
25
31
26
32
27
33
### Convert ANY C++ callable to a block ###
@@ -45,7 +51,7 @@ This works and works great, but there are a few things that don't:
45
51
});
46
52
```
47
53
Neither can you pass a block that captures anything mutable (like your lambda) - captured variables are all const.
48
-
* Your lambda captured variables are always *copied* into the block, not *moved*. If you have captures that are
54
+
* Your lambda's captured variables are always *copied* into the block, not *moved*. If you have captures that are
49
55
expensive to copy - oh well...
50
56
* Because of the above you cannot have move-only things in your block. Forget about using `std::unique_ptr` for example.
One important thing to keep in mind is that the object returned from `makeBlock`/`makeMutableBlock`**is the block**. It is NOT a block pointer (e.g. `Ret (^) (args)`) and it doesn't "store" the block pointer inside. The block's lifetime is this object's lifetime, and it ends when this object is destroyed. You can copy/move this object around and invoke it as any other C++ callable.
100
+
One important thing to keep in mind is that the object returned from `makeBlock`/`makeMutableBlock`**is the block**. It is NOT a block pointer (e.g. `Ret (^) (args)`) and it doesn't "store" the block pointer inside. The block's lifetime is this object's lifetime, and it ends when this object is destroyed. You can copy/move this object around and invoke it like any other C++ callable.
95
101
You can also convert it to the block _pointer_ as needed, either using implicit conversion or a `.get()` member function.
96
102
97
-
In Objective-C++ the block pointer lifetime **in a single scope** is unrelated to the block object's lifetime. The Objective-C++ ARC machinery will do the
103
+
In Objective-C++ the block pointer's lifetime **in a single scope** is unrelated to the block object's lifetime. The Objective-C++ ARC machinery will do the
98
104
necessary magic behind the scenes. For example:
99
105
100
106
```c++
@@ -128,7 +134,7 @@ Block_release(block);
128
134
```
129
135
130
136
`BlockUtil.h` also provides two helpers: `makeWeak` and `makeStrong` that simplify the "strongSelf"
131
-
casting dance around avoiding circular references when using blocks/lambdas.
137
+
casting dance for avoiding circular references when using blocks/lambdas.
### Coroutines that execute on GCD dispatch queues ###
145
151
146
-
Header `CoDispatch.h` allows you to use **asynchronous** C++ coroutines that execute on GCD dispatch queues. Yes, there is [this library](https://github.com/alibaba/coobjc) but it is big, targets Swift and Objective-C rather than C++/Objective-C++, and has a library to integrate with. It also has more features, of course. Here you get basic powerful C++ coroutine support in a single not very large (~800 loc) header.
152
+
Header `CoDispatch.h` allows you to use **asynchronous** C++ coroutines that execute on GCD dispatch queues. Yes, there is [this library](https://github.com/alibaba/coobjc) but it is big, targets Swift and Objective-C rather than C++/Objective-C++, and has a library to link with. It also has more features, of course. Here you get basic but powerful C++ coroutine support in a single not very large (~800 loc) header.
147
153
148
154
Working with coroutines is discussed in greater detail in [a separate doc](doc/CoDispatch.md).
149
155
@@ -217,7 +223,7 @@ int main() {
217
223
}
218
224
```
219
225
220
-
This facility can be used both from plain C++ (.cpp) and Objective-C++ (.mm) files. It is also available on Linux using [libdispatch][libdispatch] library (see [Linux notes](#linux-notes) below).
226
+
This facility can be used both from plain C++ (.cpp) and Objective-C++ (.mm) files. It is also available on Linux using the [libdispatch][libdispatch] library (see [Linux notes](#linux-notes) below).
221
227
222
228
223
229
### Boxing of any C++ objects in Objective-C ones ###
@@ -226,7 +232,7 @@ Sometimes you want to store a C++ object where an Objective-C object is expected
226
232
some `NSObject * tag` in which you really want to put an `std::vector` or something similar. You can,
227
233
of course, do that by creating a wrapper Objective-C class that stores `std::vector` but it is a huge annoyance. Yet another Objective-C class to write, so you need to make a new header and a .mm file. There is all the boilerplate code for `init` and for value access. And, after all this work, the result is going to be `std::vector`-specific. If you later need to wrap another C++ class you need yet another, almost identical wrapper.
228
234
229
-
For plain C structs Objective-C has a solution: `NSValue` that can store any C struct and let you retrieve it back later. Unfortunately in C++ this only works for "trivially copyable" types (which more or less correspond to "plain C structs"). Trying to stick anything else in `NSValue` will appear to work, but likely do very bad things - it simply copies object bytes into it and out! Whether the bytes copied out will work as the original object is undefined.
235
+
For plain C structs Objective-C has a solution: `NSValue` that can store any C struct and let you retrieve it back later. Unfortunately in C++ this only works for "trivially copyable" types (which more or less correspond to "plain C structs"). Trying to stick anything else in `NSValue` will appear to work, but will likely do very bad things - it simply copies object bytes into it and out! Whether the bytes copied out will work as the original object is undefined.
230
236
231
237
To solve this issue `BoxUtil.h` provides generic facilities for wrapping and unwrapping of C++ objects in `NSObject`-derived classes without writing any code. Such wrapping and unwrapping of native objects in objects of a higher-level language is usually called "boxing" and "unboxing", hence the
Header `NSObjectUtil.h` provides `NSObjectEqual` and `NSObjectHash` - functors that evaluate equality and hash code for any NSObject and allow them to be used as keys in `std::unordered_map` and `std::unordered_set` for example. These are implemented in terms of `isEqual` and `hash` methods of `NSObject`.
295
+
Header `NSObjectUtil.h` provides `NSObjectEqual` and `NSObjectHash` - functors that evaluate equality and hash code for any NSObject and allow them to be used as keys in `std::unordered_map` and `std::unordered_set` for example. These are implemented in terms of the `isEqual` and `hash` methods of `NSObject`.
290
296
291
-
Header `NSStringUtil.h` provides `NSStringLess` and `NSStringLocaleLess` comparators. These allow `NSString` objects to be used as keys in `std::map` or `std::set` as well as used in STL sorting and searching algorithms.
297
+
Header `NSStringUtil.h` provides `NSStringLess` and `NSStringLocaleLess` comparators. These allow `NSString` objects to be used as keys in `std::map` or `std::set` as well as in STL sorting and searching algorithms.
292
298
293
-
Additionally, it provides `NSStringEqual` comparator. This is more efficient than `NSObjectEqual` and is implemented in terms of `isEqualToString`.
299
+
Additionally, it provides an `NSStringEqual` comparator. This is more efficient than `NSObjectEqual` and is implemented in terms of `isEqualToString`.
294
300
295
-
Header `NSNumberUtil.h` provides `NSNumberLess` comparator. This allows `NSNumber` objects to be used as keys in `std::map` or `std::set` as well as used in STL sorting and searching algorithms.
301
+
Header `NSNumberUtil.h` provides an `NSNumberLess` comparator. This allows `NSNumber` objects to be used as keys in `std::map` or `std::set` as well as in STL sorting and searching algorithms.
296
302
297
-
Additionally, it provides `NSNumberEqual` comparator. This is more efficient than `NSObjectEqual` and is implemented in terms of `isEqualToNumber`.
303
+
Additionally, it provides an `NSNumberEqual` comparator. This is more efficient than `NSObjectEqual` and is implemented in terms of `isEqualToNumber`.
298
304
299
305
300
306
For all comparators, `nil`s are handled properly. A `nil` is equal to `nil` and is less than any non-`nil` object.
@@ -303,13 +309,13 @@ For all comparators, `nil`s are handled properly. A `nil` is equal to `nil` and
303
309
304
310
Header `NSObjectUtil.h` provides `operator<<` for any `NSObject` to print it to an `std::ostream`. This behaves similarly to the `%@` formatting flag by delegating either to `descriptionWithLocale:` or to `description`.
305
311
306
-
Header `NSStringUtil.h` provides additional `operator<<` to print an `NSString` to an `std::ostream`. This outputs `UTF8String`.
312
+
Header `NSStringUtil.h` provides an additional `operator<<` to print an `NSString` to an `std::ostream`. This outputs `UTF8String`.
307
313
308
314
Both headers also provide `std::formatter`s with the same functionality if `std::format` is available in the standard library and
309
-
`fmt::formatter` if a macro `NS_OBJECT_UTIL_USE_FMT` is defined. In the latter case, presence of `<fmt/format.h>` or `"fmt/format.h"` include file is required.
315
+
`fmt::formatter` if a macro `NS_OBJECT_UTIL_USE_FMT` is defined. In the latter case, the presence of `<fmt/format.h>` or `"fmt/format.h"` include file is required.
310
316
311
-
Note that since version 0.8 `fmt` library disallows formatting of any kind of naked pointers, whether they have custom formatter or not.
312
-
(See https://github.com/fmtlib/fmt/issues/4037). Thus, to format ObjC pointers you need to use `fmt::nsptr` wrapper provided by this library
317
+
Note that since version 0.8 the `fmt` library disallows formatting of any kind of naked pointers, whether they have a custom formatter or not.
318
+
(See https://github.com/fmtlib/fmt/issues/4037). Thus, to format ObjC pointers you need to use the `fmt::nsptr` wrapper provided by this library
313
319
and patterned after `fmt::ptr`. Here is a short example of printing an `NSObject *` using all 3 equivalent methods:
and convert the input to `NSString`/`CFString`. They return `nil` on failure.
355
361
356
-
Conversions from `char16_t` are exact and can only fail when out of memory. Conversions from other formats will fail also when encoding is invalid. Conversions from `char` assume UTF-8 and from `wchar_t`, UTF-32.
362
+
Conversions from `char16_t` are exact and can only fail when out of memory. Conversions from other formats will also fail when encoding is invalid. Conversions from `char` assume UTF-8 and from `wchar_t`, UTF-32.
357
363
358
364
To convert in the opposite direction the header provides `makeStdString<Char>` overloads. These accept:
359
365
360
366
*`NSString *`/`CFStringRef`, optional start position (0 by default) and optional length (whole string by default)
361
367
* A pair of `NSStringCharAccess` iterators
362
368
* Any range of `NSStringCharAccess` iterators
363
369
364
-
They return an `std::basic_string<Char>`. A `nil` input produces an empty string. Similar to above, conversions from `char16_t` are exact and conversions to other char types transcode from an appropriate UTF encoding. If the source `NSString *`/`CFStringRef` contains invalid UTF-16, the result is an empty string.
370
+
They return an `std::basic_string<Char>`. A `nil` input produces an empty string. Similar to the above, conversions from `char16_t` are exact and conversions to other char types transcode from an appropriate UTF encoding. If the source `NSString *`/`CFStringRef` contains invalid UTF-16, the result is an empty string.
365
371
366
372
This functionality is available in both Objective-C++ and plain C++.
367
373
368
374
### XCTest assertions for C++ objects ###
369
375
370
-
When using XCTest framework you might be tempted to use `XCTAssertEqual` and similar on C++ objects. While this works and is safe, you will quickly discover that when the tests fail you get a less-than-useful failure message that shows _raw bytes_ of the C++ object instead of any kind of logical description. This happens because in order to obtain the textual description of the value, `XCTAssertEqual` and friends stuff it into an `NSValue` and then query its description. And, as mentioned in [BoxUtil.h](#boxutilh) section, `NSValue` simply copies raw bytes of a C++ object.
376
+
When using the XCTest framework you might be tempted to use `XCTAssertEqual` and similar on C++ objects. While this works and is safe, you will quickly discover that when the tests fail you get a less-than-useful failure message that shows _raw bytes_ of the C++ object instead of any kind of logical description. This happens because in order to obtain the textual description of the value, `XCTAssertEqual` and friends stuff it into an `NSValue` and then query its description. And, as mentioned in the [BoxUtil.h](#boxing-of-any-c-objects-in-objective-c-ones) section, `NSValue` simply copies raw bytes of a C++ object.
371
377
372
-
While this is still safe, because nothing except the description is ever done with those bytes, the end result is hardly usable. To fix this `XCTestUtil.h` header provides the following replacement macros:
378
+
While this is still safe, because nothing except the description is ever done with those bytes, the end result is hardly usable. To fix this, the`XCTestUtil.h` header provides the following replacement macros:
373
379
374
380
-`XCTAssertCppEqual`
375
381
-`XCTAssertCppNotEqual`
@@ -378,7 +384,7 @@ While this is still safe, because nothing except the description is ever done wi
378
384
-`XCTAssertCppLessThan`
379
385
-`XCTAssertCppLessThanOrEqual`
380
386
381
-
These, in the case of failure, try to obtain description using the following methods:
387
+
These, in the case of failure, try to obtain a description using the following methods:
382
388
383
389
- If there is an ADL call `testDescription(obj)` that produces `NSString *`, use that.
384
390
- Otherwise, if there is an ADL call `to_string(obj)` in `using std::to_string` scope, use that.
@@ -389,9 +395,9 @@ Thus, if an object is printable using the typical means, those will be automatic
389
395
390
396
## Linux notes ##
391
397
392
-
`BlockUtil.h` and `CoDispatch.h` headers can also be used on Linux. Currently, this requires:
393
-
* Clang 16 or above (for blocks support). See [this issue][gcc-blocks] for status of blocks support in GCC
394
-
*[swift-corelibs-libdispatch][libdispatch] library. Note that **most likely you need to build it from source**. The versions available via various package managers (as of summer 2024) are very old and cannot be used.
398
+
The `BlockUtil.h` and `CoDispatch.h` headers can also be used on Linux. Currently, this requires:
399
+
* Clang 16 or above (for blocks support). See [this issue][gcc-blocks] for the status of blocks support in GCC
400
+
*The [swift-corelibs-libdispatch][libdispatch] library. Note that **most likely you need to build it from source**. The versions available via various package managers (as of summer 2024) are very old and cannot be used.
0 commit comments