2013-12-02 4 views
3

Я пишу математическую библиотеку в Objective-C (для удовольствия), и теперь мне нужно каким-то образом эмулировать перегрузку функций в стиле C++.Перегрузка метода эмуляции в Objective-C

Мой предназначен взгляд:

NSNumber *x = @(25); 
CMVector *v = [CMVector vectorWithElements:@12, @14, @18, nil]; 
CMMatrix *m = [CMMatrix matrixWithRows:@[@12, @13, @0 ], 
             @[@24, @26, @30], 
             @[@0, @0, @1 ], nil]; 
CMMatrix *i = [CMMatrix identityMatrixWithRank:3]; 

NSLog(@"%@ %@ %@ %@", [x multiply:v], [x multiply:m], [m multiply: i], [v multiply:x]); 

Есть ли способ реализации этого?

Вот возможный способ я придумал, используя класс для представления перегруженной функции и кодирования аргументов имен в имена методов класса, как:

@interface CMMultiply : NSProxy 

+ (NSNumber *)NSNumber:(NSNumber *)left NSNumber:(NSNumber *)right; 
+ (CMVector *)NSNumber:(NSNumber *)left CMVector:(CMVector *)right; 
+ (CMVector *)CMVector:(CMVector *)left NSNumber:(NSNumber *)right; 
+ (CMVector *)CMVector:(CMVector *)left CMVector:(CMVector *)right; 
+ (CMMatrix *)NSNumber:(NSNumber *)left CMMatrix:(CMMatrix *)right; 
+ (CMMatrix *)CMMatrix:(CMMatrix *)left NSNumber:(NSNumber *)right; 
+ (CMMatrix *)CMMatrix:(CMMatrix *)left CMMatrix:(CMMatrix *)right; 

@end 

и реализовать перегруженный метод, с помощью создания кодированного имени методы и назовите его.

Любые лучшие идеи?

EDIT

Вот моя перегружать логика:

@implementation NSObject (CMOverloading) 

- (NSString *)overloadingClassName 
{ 
    return NSStringFromClass([self class]); // Override this to handle class clusters 
} 

- (id)callOverloadedMethod:(Class)method withObject:(id)other 
{ 
    NSString *methodName = [NSString stringWithFormat:@"%@:%@:", [self overloadingClassName], [other overloadingClassName]]; 
    SEL selector = NSSelectorFromString(methodName); 
    return objc_msgSend(method, selector, self, other); 
} 

@end 

EDIT 2

Базовый класс для библиотеки "перегрузки" является NSProxy, так что пользователи библиотеки не могут создать экземпляр.

EDIT 3

Реализация упрощая макросы:

#define CMOverloadingMethod(_return, _left, _right) \ 
+ (_return *)_left:(_left *)left _right:(_right *)right 

#define CMOverloadedMethod(_type, _object) \ 
return [self callOverloadedMethod:[_type class] withObject:(_object)] 

Перегруженный метод все еще нужен (простой) единственную реализацию, здесь, прямо в NSObject:

- (id)multiply:(id)right 
{ 
    CMOverloadedMethod(CMMultiply, right); 
} 

и реализующий на NSNumber:

@implementation CMMultiply (NSNumber) 

CMOverloadingMethod(NSNumber, NSNumber, NSNumber) 
{ 
    return @([left doubleValue] * [right doubleValue]); 
} 

@end 
+1

Создание класса для каждой операции является сумасшедшим! Если вам действительно нужна перегрузка оператора, и вы должны это тщательно изучить, одним из способов было бы указать аргументы вашего метода как типа 'id'. Затем вы можете использовать 'isKindOfClass:', чтобы точно определить, что вам нужно делать с ними. – bdesham

+0

@bdesham Ну этот «один класс для каждой перегрузки» предназначен для использования с категориями классов, так что позже могут быть добавлены различные перегрузки. –

ответ

0

Вот окончательный проект, который я использовал, разрешая типы с использованием отражения. Я по-прежнему использую классы в качестве контейнеров с перегруженными функциями, в основном используя эти типы контейнеров в виде vtables:

#define CMOverloadingMethod(_return, _left, _right) \ 
+ (_return *)_left:(_left *)left _right:(_right *)right 

#define CMOverloadedMethod(_type, _object) \ 
return [self callOverloadedMethod:[_type class] withObject:(_object)] 

- (id)callOverloadedMethod:(Class)method withObject:(id)other 
{ 
    Class thisClass = [self class]; 
    Class nextClass = [other class]; 
    Class methodClass = object_getClass(method); 

    // Find the appropriate overload: 
    Class thisClass2 = Nil; 
    do 
    { 
     thisClass2 = thisClass; 

     Class otherClass = nextClass; 
     Class otherClass2 = Nil; 
     do 
     { 
      otherClass2 = otherClass; 
      SEL selector = NSSelectorFromString(MSSTR(@"%@:%@:", NSStringFromClass(thisClass), NSStringFromClass(otherClass))); 
      if (class_respondsToSelector(methodClass, selector)) 
       return objc_msgSend(method, selector, self, other); 
      else 
       otherClass = class_getSuperclass(otherClass); 
     } while (otherClass != otherClass2 && otherClass && otherClass2); 
     thisClass = class_getSuperclass(thisClass); 
    } while (thisClass != thisClass2 && thisClass && thisClass2); 

    [NSException raise:NSInvalidArgumentException format:@"Cannot resolve overloaded method in class %@ for objects typed %@ and %@.", NSStringFromClass(method), NSStringFromClass([self class]), NSStringFromClass([other class])]; 
    return nil; 
} 
2

Для достижения этого синтаксиса

NSLog(@"%@ %@ %@ %@", [x multiply:v], [x multiply:m], [m multiply: i], [v multiply:x]); 

вы должны реализовать метод умножения в каждом из ваших математических классов (вектор, матрица) и метод категории на NSNumber. Проблема в том, что вы также хотите иметь возможность передавать любой аргумент, и это не поддерживается в Objective-C (несколько методов с тем же именем, но с разными типами аргументов).

Вместо этого вы можете либо взять идентификатор в качестве аргумента и определить тип во время выполнения или вы должны быть более многословен:

NSLog(@"%@ %@ %@ %@", [x multiplyWithVector:v], 
         [x multiplyWithMatrix:m], 
         [m multiplyWithMatrix:i], 
         [v multiplyWithScalar:x]); 

Но это слишком сложным и, кроме того, реализует математическую библиотеку, как это будет довольно медленным.

Почему бы вам вместо этого не использовать простые структуры и функции? Это было сделано много раз, и это работает.

+0

Я сказал, для удовольствия. Для реальной сделки я предпочел бы перейти непосредственно к GMP/MPFR или тому подобное. –

1

Objective-C не использует виртуальную таблицу, такую ​​как C++. Это относится к методам, использующим поиск строк, поэтому вы не можете перегружать методы по параметрам.Лучше всего использовать id, чтобы разрешить обращение к любому типу параметра. Таким образом, ваш метод multiply будет выглядеть следующим образом:

+ (id)multiplyLeft:(id)left right:(id)right; 

Не хорошенькое решение.

Вы можете использовать категории, а затем вы бы решение, которое начинается так:

@interface NSObject (Math) 

- (instancetype)multiply:(id)right; 

@end 

@implementation NSObject (Math) 

- (instancetype)multiply:(id)right 
{ 
    // Feel free to throw an exception here. 
    return nil; 
} 

@end 

@implementation NSNumber (Math) 

- (instancetype)multiply:(id)right 
{ 
    if ([right isKindOfClass:[NSNumber class]]) 
    { 
     return [NSNumber numberWithInteger:[self integerValue] * [right integerValue]]; 
    } 
    else 
    { 
    // Feel free to throw an exception here. 
     return nil; 
    } 
} 

@end 

Опять же, не симпатичное решение, но он позволяет писать то, что вы после этого.

Удачи.

+0

Я на самом деле пытаюсь реализовать поиск на основе типа vtable поверх соответствия строк. –

0

Вы также можете использовать C++. Objective-C++ довольно мощный. Clang теперь поддерживает C++ 11. Это тот материал, который C++ делает очень хорошо, и смешивание кода двух языков довольно чисто и легко.

+0

Я нахожусь на той же странице, что и [Линус Торвальдс] (http://article.gmane.org/gmane.comp.version-control.git/57918) на C++, просто не нравится, ненавижу и ненавижу. Итак, нет, я буду придерживаться Objective-C или просто чистым C. –

+0

О, мальчик, если Линус говорит, что это плохо ... lol. Хорошо. Обязательно отправьте свое решение, когда закончите. Возможно, тогда я отправлю версию на C++. – Rob

+0

Это уже есть. –

Смежные вопросы