Created
October 3, 2018 18:37
-
-
Save alexdrone/abe0904f6f97b2155c4911e0d2c2aa60 to your computer and use it in GitHub Desktop.
ObjectiveC++ MACROS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef objcxx_h | |
#define objcxx_h | |
#import <Foundation/Foundation.h> | |
NS_ASSUME_NONNULL_BEGIN | |
// #pragma mark - Type inference and dynamic casts | |
// Type inference for local variables. | |
#if defined(__cplusplus) | |
#else | |
#define auto __auto_type | |
#define nullptr nil | |
#endif | |
// Equivalent to swift nil coalescing operator '??'. | |
#if defined(__cplusplus) | |
template <typename T> | |
static inline T *_Nonnull _objcxx_nil_coalescing(T *_Nullable value, T *_Nonnull defaultValue) { | |
return value != nil ? value : defaultValue; | |
} | |
#define objcxx_nil_coalescing(VALUE, DEFAULT) _objcxx_nil_coalescing(VALUE, DEFAULT) | |
#else | |
#define objcxx_nil_coalescing(VALUE, DEFAULT) (VALUE != nil ? VALUE : DEFAULT) | |
#endif | |
/// Mirrors Swift's 'as?' operator. | |
#if defined(__cplusplus) | |
template <typename T> | |
static inline T *_Nullable _objcxx_dynamic_cast( | |
__unsafe_unretained id _Nullable obj, | |
bool assert = false) { | |
if ([(id)obj isKindOfClass:[T class]]) return obj; | |
if (assert) NSCAssert(false, @"failed to cast %@ to %@", obj, [T class]); | |
return nullptr; | |
} | |
template <typename T> | |
static inline T *_Nonnull _objcxx_dynamic_cast_or_assert(__unsafe_unretained id _Nullable obj) { | |
return (T * _Nonnull) _objcxx_dynamic_cast<T>(obj, true); | |
} | |
#define objcxx_dynamic_cast(TYPE, VALUE) _objcxx_dynamic_cast<TYPE>(VALUE) | |
#define objcxx_dynamic_cast_or_assert(TYPE, VALUE) _objcxx_dynamic_cast_or_assert<TYPE>(VALUE) | |
#else | |
static inline id _objcxx_dynamic_cast(__unsafe_unretained id obj, Class type, BOOL assert) { | |
if ([(id)obj isKindOfClass:type]) return obj; | |
if (assert) NSCAssert(false, @"failed to cast %@ to %@", obj, type); | |
return nullptr; | |
} | |
#define objcxx_dynamic_cast(TYPE, VALUE) \ | |
((TYPE * _Nullable) _objcxx_dynamic_cast(VALUE, TYPE.class, false)) | |
#define objcxx_dynamic_cast_or_assert(TYPE, VALUE) \ | |
((TYPE * _Nonnull) objcxxDynamicCast(VALUE, TYPE.class, true)) | |
#endif | |
// #pragma mark - Weakify | |
#define objcxx_weakname_(VAR) VAR##_weak_ | |
#define objcxx_weakify(VAR) __weak __typeof__(VAR) objcxx_weakname_(VAR) = (VAR) | |
#define objcxx_strongify(VAR) \ | |
_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wshadow\"") \ | |
__strong __typeof__(VAR) VAR = objcxx_weakname_(VAR); \ | |
_Pragma("clang diagnostic pop") | |
#define objcxx_strongify_and_return_if_nil(VAR) \ | |
objcxx_strongify(VAR); \ | |
if (!(VAR)) { \ | |
return; \ | |
} | |
// Safe keypath litterals. | |
#if DEBUG | |
#define objcxx_keyPath(o, p) ((void)(NO && ((void)o.p, NO)), @ #p) | |
#else | |
#define objcxx_keyPath(o, p) @ #p | |
#endif | |
// #pragma mark - Misc | |
// Equivalent to Swift's @noescape. | |
#define objcxx_noescape __attribute__((noescape)) | |
// Ensure the caller method is being invoked on the main thread. | |
#define objcxx_assert_on_main_thread() \ | |
NSAssert(NSThread.isMainThread, @"%@ called off the main thread.", NSStringFromSelector(_cmd)) | |
#define objcxx_clamp(x, low, high) \ | |
({ \ | |
__typeof__(x) __x = (x); \ | |
__typeof__(low) __low = (low); \ | |
__typeof__(high) __high = (high); \ | |
__x > __high ? __high : (__x < __low ? __low : __x); \ | |
}) | |
// #pragma mark - Boxable structs | |
// Ensure the struct can be boxed in a NSValue by using the @ symbol. | |
#define objcxx_boxable __attribute__((objc_boxable)) | |
typedef struct objcxx_boxable CGPoint CGPoint; | |
typedef struct objcxx_boxable CGSize CGSize; | |
typedef struct objcxx_boxable CGRect CGRect; | |
typedef struct objcxx_boxable CGVector CGVector; | |
typedef struct objcxx_boxable UIEdgeInsets UIEdgeInsets; | |
typedef struct objcxx_boxable _NSRange NSRange; | |
// #pragma mark - Generics | |
@protocol objcxx_FastEnumeration <NSFastEnumeration> | |
- (id)objcxx_enumeratedType; | |
@end | |
// Usage: objcxx_foreach (s, strings) { ... } | |
// For each loops using type inference. | |
#define objcxx_foreach(element, collection) \ | |
for (typeof((collection).objcxx_enumeratedType) element in (collection)) | |
@interface NSArray <ElementType>(objcxx_FastEnumeration) <objcxx_FastEnumeration> | |
- (ElementType)objcxx_enumeratedType; | |
@end | |
@interface NSSet <ElementType>(objcxx_FastEnumeration) <objcxx_FastEnumeration> | |
- (ElementType)objcxx_enumeratedType; | |
@end | |
@interface NSDictionary <KeyType, ValueType>(objcxx_FastEnumeration) <objcxx_FastEnumeration> | |
- (KeyType)objcxx_enumeratedType; | |
@end | |
/// This overrides the NSObject declaration of copy with specialized ones that retain | |
// the generic type. | |
// This is pure compiler sugar and will create additional warnings for type mismatches. | |
// @note id-casted objects will create a warning when copy is called on them as there are multiple | |
// declarations available. Either cast to specific type or to NSObject to work around this. | |
@interface NSArray <ElementType>(objcxx_SafeCopy) | |
// Same as `copy` but retains the generic type. | |
- (NSArray<ElementType> *)copy; | |
// Same as `mutableCopy` but retains the generic type. | |
- (NSMutableArray<ElementType> *)mutableCopy; | |
@end | |
@interface NSSet <ElementType>(objcxx_SafeCopy) | |
// Same as `copy` but retains the generic type. | |
- (NSSet<ElementType> *)copy; | |
// Same as `mutableCopy` but retains the generic type. | |
- (NSMutableSet<ElementType> *)mutableCopy; | |
@end | |
@interface NSDictionary <KeyType, ValueType>(objcxx_SafeCopy) | |
// Same as `copy` but retains the generic type. | |
- (NSDictionary<KeyType, ValueType> *)copy; | |
// Same as `mutableCopy` but retains the generic type. | |
- (NSMutableDictionary<KeyType, ValueType> *)mutableCopy; | |
@end | |
@interface NSOrderedSet <ElementType>(objcxx_SafeCopy) | |
// Same as `copy` but retains the generic type. | |
- (NSOrderedSet<ElementType> *)copy; | |
// Same as `mutableCopy` but retains the generic type. | |
- (NSMutableOrderedSet<ElementType> *)mutableCopy; | |
@end | |
@interface NSHashTable <ElementType>(objcxx_SafeCopy) | |
// Same as `copy` but retains the generic type. | |
- (NSHashTable<ElementType> *)copy; | |
@end | |
@interface NSMapTable <KeyType, ValueType>(objcxx_SafeCopy) | |
// Same as `copy` but retains the generic type. | |
- (NSMapTable<KeyType, ValueType> *)copy; | |
@end | |
#endif //objcxx_h | |
// #pragma mark - ARC Structs | |
/** Usage: | |
objcxx_struct_def { NSUInteger count; NSString *name; } MyAction1; | |
objcxx_struct_def { NSNumber *number; } MyAction2; | |
MyAction1 arg = { | |
.count = 0, | |
.name = @"" | |
}; | |
objcxx_boxed_struct *userInfo = objcxx_boxed_struct_make(arg); | |
objcxx_struct_get(MyAction1, arg2, userInfo); | |
*/ | |
#define objcxx_struct_check(TYPE, ACTION) \ | |
[ACTION.identifier isEqualToString: @ #TYPE] \ | |
#define objcxx_struct_def \ | |
typedef struct __attribute__((objc_boxable)) \ | |
#define _objcxx_boxed_struct_make(TYPE, ARGS) \ | |
[[objcxx_boxed_struct alloc] initWithIdentifier:@ # TYPE \ | |
andData:@(ARGS)]; \ | |
#define objcxx_boxed_struct_make(STRUCT) \ | |
_objcxx_boxed_struct_make(__typeof(STRUCT), STRUCT) \ | |
#define objcxx_struct_get(TYPE, VAR, WRAPPER) \ | |
TYPE VAR; \ | |
assert(objcxx_struct_check(TYPE, WRAPPER)); \ | |
[WRAPPER.boxedStruct getValue:&VAR]; \ | |
/// Wraps a dispatch action. | |
@interface objcxx_boxed_struct: NSObject | |
/// The struct identifier (automatically populated by @c objcxx_boxed_struct_make). | |
@property(nonatomic, readonly) NSString *identifier; | |
/// The boxed struct. | |
/// @note Use The @c objcxx_struct_check macro to check the action type and | |
/// @c objcxx_struct_get to unbox the arguments. | |
@property(nonatomic, readonly) NSValue *boxedStruct; | |
/// Don't use the initializer directly. | |
/// Use @c objcxx_boxed_struct_make to build a boxed struct. | |
- (instancetype)initWithIdentifier:(NSString *)identifier andData:(NSValue *)boxedStruct; | |
@end | |
@implementation objcxx_boxed_struct | |
- (instancetype)initWithIdentifier:(NSString *)identifier andData:(NSValue *)boxedStruct { | |
if (self = [super init]) { | |
_identifier = identifier; | |
_boxedStruct = boxedStruct; | |
} | |
return self; | |
} | |
@end | |
NS_ASSUME_NONNULL_END | |
// #pragma mark - NSArray to std::vector and viceversa | |
#if defined(__cplusplus) | |
#include <vector> | |
template <typename T> | |
static inline NSArray *objcxx_array_with_vector( | |
const std::vector<T> &vector, | |
id (^block)(const T &value)) { | |
NSMutableArray *result = [NSMutableArray arrayWithCapacity:vector.size()]; | |
for (const T &value : vector) { | |
[result addObject:block(value)]; | |
} | |
return result; | |
} | |
template <typename T> | |
static inline std::vector<T> objcxx_vector_with_elements( | |
id<NSFastEnumeration> array, | |
T (^_Nullable block)(id value)) { | |
std::vector<T> result; | |
for (id value in array) { | |
result.push_back(block(value)); | |
} | |
return result; | |
} | |
#endif //defined(__cplusplus) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment