Skip to content

Instantly share code, notes, and snippets.

@alexdrone
Created October 3, 2018 18:37
Show Gist options
  • Save alexdrone/abe0904f6f97b2155c4911e0d2c2aa60 to your computer and use it in GitHub Desktop.
Save alexdrone/abe0904f6f97b2155c4911e0d2c2aa60 to your computer and use it in GitHub Desktop.
ObjectiveC++ MACROS
#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