Skip to content

Commit c9f9117

Browse files
committed
Updating docs
1 parent caa568f commit c9f9117

2 files changed

Lines changed: 39 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
77

88
### Added
99
- Support for consuming this library via Xcode packages (e.g. SwiftPM)
10-
- Support for consuming this library via an XCFramework
10+
- Support for consuming this library via an XCFramework
11+
- Support for consuming this library via CMake
12+
13+
### Changed
14+
- Library versioning scheme has changed to have 3 fields (for compatibility with SwiftPM)
1115

1216
## [3.6] - 2026-05-26
1317

README.md

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ An ever-growing collection of utilities to make coding on Apple platforms in C++
1919

2020
## What's included? ##
2121

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

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

2632

2733
### 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:
4551
});
4652
```
4753
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
4955
expensive to copy - oh well...
5056
* Because of the above you cannot have move-only things in your block. Forget about using `std::unique_ptr` for example.
5157
@@ -91,10 +97,10 @@ dispatch_async(someQueue, makeBlock([ptr=std::move(ptr)]() {
9197
9298
```
9399

94-
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.
95101
You can also convert it to the block _pointer_ as needed, either using implicit conversion or a `.get()` member function.
96102

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
98104
necessary magic behind the scenes. For example:
99105

100106
```c++
@@ -128,7 +134,7 @@ Block_release(block);
128134
```
129135
130136
`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.
132138
133139
Here is the intended usage:
134140
@@ -143,7 +149,7 @@ dispatch_async(someQueue, [weakSelf = makeWeak(self)] () {
143149

144150
### Coroutines that execute on GCD dispatch queues ###
145151

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

148154
Working with coroutines is discussed in greater detail in [a separate doc](doc/CoDispatch.md).
149155

@@ -217,7 +223,7 @@ int main() {
217223
}
218224
```
219225

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).
221227

222228

223229
### 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
226232
some `NSObject * tag` in which you really want to put an `std::vector` or something similar. You can,
227233
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.
228234

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

231237
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
232238
name of the header and its APIs.
@@ -286,15 +292,15 @@ assert([box(5) compare:box(6)] == NSOrderingAscending);
286292

287293
### Comparators for Objective-C objects ###
288294

289-
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`.
290296

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

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`.
294300

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

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`.
298304

299305

300306
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
303309

304310
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`.
305311

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`.
307313

308314
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.
310316

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
313319
and patterned after `fmt::ptr`. Here is a short example of printing an `NSObject *` using all 3 equivalent methods:
314320

315321
```cpp
@@ -353,23 +359,23 @@ Header `NSStringUtil.h` provides `makeNSString` and `makeCFString` functions tha
353359

354360
and convert the input to `NSString`/`CFString`. They return `nil` on failure.
355361

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

358364
To convert in the opposite direction the header provides `makeStdString<Char>` overloads. These accept:
359365

360366
* `NSString *`/`CFStringRef`, optional start position (0 by default) and optional length (whole string by default)
361367
* A pair of `NSStringCharAccess` iterators
362368
* Any range of `NSStringCharAccess` iterators
363369

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

366372
This functionality is available in both Objective-C++ and plain C++.
367373

368374
### XCTest assertions for C++ objects ###
369375

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

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:
373379

374380
- `XCTAssertCppEqual`
375381
- `XCTAssertCppNotEqual`
@@ -378,7 +384,7 @@ While this is still safe, because nothing except the description is ever done wi
378384
- `XCTAssertCppLessThan`
379385
- `XCTAssertCppLessThanOrEqual`
380386

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:
382388

383389
- If there is an ADL call `testDescription(obj)` that produces `NSString *`, use that.
384390
- 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
389395

390396
## Linux notes ##
391397

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

396402
You must use:
397403
```
@@ -413,4 +419,5 @@ For `BlockUtil.h` link with:
413419
<!-- References -->
414420

415421
[libdispatch]: https://github.com/apple/swift-corelibs-libdispatch
416-
[gcc-blocks]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78352
422+
[gcc-blocks]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78352
423+
[releases]: https://github.com/gershnik/objc-helpers/releases

0 commit comments

Comments
 (0)