【Objective-C++】NSRangeを拡張する【Rangeラッパークラスの作り方】

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
広告