我正在Objective-C中编写一个数学库(为了好玩),现在我需要一些方法来模拟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
并通过创建编码的方法名称来实现重载方法并调用它。
有更好的想法吗?
修改
这是我的重载逻辑:
@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
编辑2
“重载库”的基类是NSProxy
,因此库的用户无法实例化它。
编辑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
答案 0 :(得分:2)
实现此语法
NSLog(@"%@ %@ %@ %@", [x multiply:v], [x multiply:m], [m multiply: i], [v multiply:x]);
你必须在每个数学类(向量,矩阵)和NSNumber上的类别方法中实现乘法。问题是您还希望能够传递任何参数,而Objective-C(具有相同名称但参数类型不同的多个方法)不支持此参数。
相反,您可以将id作为参数并在运行时计算出类型,或者您必须更详细:
NSLog(@"%@ %@ %@ %@", [x multiplyWithVector:v],
[x multiplyWithMatrix:m],
[m multiplyWithMatrix:i],
[v multiplyWithScalar:x]);
但是这太复杂了,而且实现这样的数学库会很慢。
为什么不使用简单的结构和函数呢?这已经做了很多次,它的工作原理。
答案 1 :(得分:1)
Objective-C不使用像C ++这样的vtable。它指的是使用字符串查找的方法,因此您不能通过参数重载方法。那么,您最好的选择是使用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
同样,这不是一个漂亮的解决方案,但它允许你写下你想要的东西。
祝你好运。答案 2 :(得分:0)
您也可以使用C ++。 Objective-C ++非常强大。 Clang现在支持C ++ 11.这是C ++做得很好的东西,并且混合两种语言的代码非常干净和简单。
答案 3 :(得分:0)
这是我使用的最终设计,使用反射解析类型。我仍然使用类作为重载函数的容器,基本上使用这些容器类型作为vtable:
#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;
}