NSRangeをより便利に扱うためのテクニックを紹介します。Objective-C++を活用して、次のような処理を実現します。
NSString* s = @"NSString";
Range r = {0, 2};// NSMakeRange(0, 2);
[s substringWithRange:r]; // "NS"
[s substringWithRange:r.unionRange({2, 2})]; // "NSSt"
Range g = {0, s.length};
[s substringToIndex:g.max()]; // "NSString"
Objective-C++ではC++の言語機能を活用することで、Swiftと同等の機能性を実現することが可能となっています。
Rangeクラスの実装
C++の仕組みを活用して、次のような定義を行います。
struct Range {
NSUInteger location, length; /// decltype(NSRange::location) location;
Range(NSRange range) : location(range.location), length(range.length) {}
Range(NSUInteger location, NSUInteger length) : location(location), length(length) {}
Range(NSString *string) { *this = NSRangeFromString(string); }
operator NSRange() { return NSMakeRange(location, length); }
operator NSValue*() { return [NSValue valueWithRange:*this]; }
operator NSString*() { return NSStringFromRange(*this); }
NSUInteger max() { return NSMaxRange(*this); }
Range unionRange(Range range) { return NSUnionRange(*this, range); }
Range intersectionRange(Range range) { return NSIntersectionRange(*this, range); }
BOOL contains(NSUInteger location) { return NSLocationInRange(location, *this); }
BOOL equals(Range range) { return NSEqualRanges(*this, range); }
bool notFound() { return location == NSNotFound; }
bool found() { return location != NSNotFound; }
BOOL operator==(Range range) { return equals(range); }
BOOL operator!=(Range range) { return !equals(range); }
Range& operator++() { ++location; return *this; }
Range& operator--() { --location; return *this; }
Range operator++(int) { auto r = *this; location++; return r; }
Range operator--(int) { auto r = *this; location--; return r; }
NSUInteger operator*() { return location; }
// operator bool() { return location != NSNotFound; }
// Range operator+(NSUInteger i) { auto r = *this; r.location += i; return r; }
// Range operator-(NSUInteger i) { auto r = *this; r.location -= i; return r; }
// bool operator~() { return NSNotFound == location; }
// NSUInteger operator+() { return length; }
// NSUInteger operator()() { return max(); }
// Range advance(NSUInteger n) { location += n; return *this; }
};
Rangeクラスの使用
以下のコードはRangeクラスの使用例です。上記の定義によって、メンバ関数(メソッド)やRange ⇔ NSRange
間の相互変換が実現されています。
@interface TestRange : NSObject @end @implementation TestRange
+ (void)printNSRange:(NSRange)range { puts(NSStringFromRange(range).UTF8String); }
+ (void)printRange:(Range)range { puts(NSStringFromRange(range).UTF8String); }
+ (void)load {
/// constructors ///
NSRange nsRange = NSMakeRange(0, 2);
Range myRange = NSMakeRange(0, 2);
Range b = nsRange;
NSRange c = myRange;
/// assignment operators ///
Range a = {0, 2};
a = {0, 2};
a = Range{0, 2};
a = Range(0, 2);
a = NSMakeRange(0, 2);
a = nsRange;
/// conversions ///
NSString *string = a;
a = string;
NSValue *value = a;
a = value.rangeValue;
/// methods ///
assert( a.max() == 2 );
assert( a.unionRange({2, 2}).length == 4 );
assert( a.contains(1) == YES );
assert( a.contains(9) == NO );
assert( a.equals(b) );
assert(!a.equals({0, 9}) );
assert( a == b ); // Range == Range
assert( a == c ); // Range == NSRange
assert( a == Range(0, 2) );
assert( a == NSMakeRange(0, 2) );
assert( a != NSMakeRange(1, 2) );
assert( a == nsRange );
assert( a == myRange );
assert( *a == b.location );
assert( ++a == Range(1, 2) );
assert( --a == Range(0, 2) );
assert( *a == 0 );
assert( (a + 3) == Range( 3, 2) );
assert( (a - 1) == Range(-1, 2) );
/// operator +, - ///
// assert( (a + 3) == Range( 3, 2) );
// assert( (a - 1) == Range(-1, 2) );
/// operator bool ///
// {
// if (a) assert("yes")
// if (!a == a.notFound()) assert("yes");
// NSString *s = @"a";
// if (Range r = [s rangeOfString:@"a"]) assert([@"a" isEqualToString:[s substringWithRange:r]]);
// if (Range r = [s rangeOfString:@"b"]) ; else assert(r.notFound());
// }
/// converting constructors ///
[TestRange printNSRange:myRange]; // "{0, 2}"
[TestRange printNSRange:nsRange]; // "{0, 2}"
[TestRange printRange:myRange]; // "{0, 2}"
[TestRange printRange:nsRange]; // "{0, 2}"
/// others ///
NSString *s = @"123456";
Range range = {3, 3};
printf("%s", [s substringWithRange:range].UTF8String); // "456"
printf("%s", [s substringWithRange:Range(1, 2)].UTF8String); // "23"
}
@end