Skip to content

Commit c29a953

Browse files
committed
Formal protocol conformance in BoxUtil
1 parent 27e09b8 commit c29a953

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55

66
## Unreleased
77

8+
### Added
9+
- Boxed values now actually formally conform to relevant protocols (as reported by `confromsToProtocol:`)
10+
811
### Fixed
912
- Added `<exception>` header include to XCTestUtil.h that is now required on newer Xcodes
1013
- Making `BoxUtil.h` not rely on `std::filesystem` which is not available on older Apple

include/objc-helpers/BoxUtil.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ class __attribute__((visibility("hidden"))) BoxMaker {
167167
std::string className = objcData.modulePrefix + "Boxed["s + tid.name() + ']';
168168
Class cls = objc_allocateClassPair(objcData.NSObjectClass, className.c_str(), 0);
169169
classData.cls = cls;
170+
171+
if (!class_addProtocol(cls, @protocol(BoxedValue)))
172+
@throw [NSException exceptionWithName:NSGenericException reason:@"class_addProtocol(@protocol(NSCopying)) failed" userInfo:nullptr];
170173

171174
if (!class_addIvar(cls, "_value", sizeof(T), alignof(T), @encode(T)))
172175
@throw [NSException exceptionWithName:NSGenericException reason:@"class_addIvar(_value) failed" userInfo:nullptr];
@@ -185,6 +188,9 @@ class __attribute__((visibility("hidden"))) BoxMaker {
185188
if constexpr (std::is_copy_constructible_v<T>) {
186189
if (!class_addMethod(cls, objcData.copyWithZoneSel, IMP(copyWithZone), "@@:@"))
187190
@throw [NSException exceptionWithName:NSGenericException reason:@"class_addMethod(copyWithZone) failed" userInfo:nullptr];
191+
192+
if (!class_addProtocol(cls, @protocol(NSCopying)))
193+
@throw [NSException exceptionWithName:NSGenericException reason:@"class_addProtocol(@protocol(NSCopying)) failed" userInfo:nullptr];
188194
}
189195

190196
if (!class_addMethod(cls, objcData.isEqualSel, IMP(isEqual), "@@:@"))
@@ -196,6 +202,8 @@ class __attribute__((visibility("hidden"))) BoxMaker {
196202
if constexpr (std::totally_ordered<T>) {
197203
if (!class_addMethod(cls, objcData.compareSel, IMP(compare), (@encode(NSComparisonResult) + "@:@"s).c_str()))
198204
@throw [NSException exceptionWithName:NSGenericException reason:@"class_addMethod(compare) failed" userInfo:nullptr];
205+
if (!class_addProtocol(cls, @protocol(BoxedComparable)))
206+
@throw [NSException exceptionWithName:NSGenericException reason:@"class_addProtocol(@protocol(BoxedComparable)) failed" userInfo:nullptr];
199207
}
200208

201209
objc_registerClassPair(cls);

test/BoxUtilTests.mm

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
CHECK([obj.description isEqualToString:@"42"]);
1313
CHECK(obj.hash == std::hash<int>()(42));
1414

15+
CHECK([obj conformsToProtocol:@protocol(NSCopying)]);
16+
CHECK([obj conformsToProtocol:@protocol(BoxedValue)]);
17+
CHECK([obj conformsToProtocol:@protocol(BoxedComparable)]);
18+
CHECK([obj conformsToProtocol:@protocol(NSObject)]);
19+
1520
@try {
1621
boxedValue<long>(obj);
1722
FAIL("able to unbox wrong type");
@@ -71,6 +76,12 @@
7176

7277
auto obj = box(std::string("abc"));
7378
static_assert(std::is_same_v<decltype(obj), NSObject<BoxedValue, BoxedComparable, NSCopying> *>);
79+
80+
CHECK([obj conformsToProtocol:@protocol(NSCopying)]);
81+
CHECK([obj conformsToProtocol:@protocol(BoxedValue)]);
82+
CHECK([obj conformsToProtocol:@protocol(BoxedComparable)]);
83+
CHECK([obj conformsToProtocol:@protocol(NSObject)]);
84+
7485
CHECK(boxedValue<std::string>(obj) == str);
7586
CHECK([obj.description isEqualToString:@"abc"]);
7687
CHECK(obj.hash == std::hash<std::string>()(str));
@@ -93,6 +104,12 @@
93104

94105
auto obj = box(std::make_unique<int>(5));
95106
static_assert(std::is_same_v<decltype(obj), NSObject<BoxedValue, BoxedComparable> *>);
107+
108+
CHECK(![obj conformsToProtocol:@protocol(NSCopying)]);
109+
CHECK([obj conformsToProtocol:@protocol(BoxedValue)]);
110+
CHECK([obj conformsToProtocol:@protocol(BoxedComparable)]);
111+
CHECK([obj conformsToProtocol:@protocol(NSObject)]);
112+
96113
CHECK(*boxedValue<std::unique_ptr<int>>(obj) == 5);
97114

98115
@try {
@@ -143,6 +160,11 @@
143160

144161
static_assert(std::is_same_v<decltype(obj1), NSObject<BoxedValue, NSCopying> *>);
145162

163+
CHECK([obj1 conformsToProtocol:@protocol(NSCopying)]);
164+
CHECK([obj1 conformsToProtocol:@protocol(BoxedValue)]);
165+
CHECK(![obj1 conformsToProtocol:@protocol(BoxedComparable)]);
166+
CHECK([obj1 conformsToProtocol:@protocol(NSObject)]);
167+
146168
CHECK(boxedValue<foo>(obj1).i == 4);
147169
CHECK(boxedValue<foo>(obj2).c == 'b');
148170
CHECK([obj1 isEqual:obj2]);

0 commit comments

Comments
 (0)