非Objective-Cオブジェクトを引数として渡す

NSObjectのperformSelectorは、Objective-Cのオブジェクトしか渡せないので、int型の値を渡すだけでもたいへんです。NSObjectを継承したInteger型みたいなのを作るか、NSValueを使っているひとが多いと思います。


今回は、intでもstd::vectorでも、C++のオブジェクトをなんでも渡せる、performSelectorの代わりとなる関数を作りました。こんな感じです:

#import <Foundation/Foundation.h>
#import <boost/mpl/identity.hpp>

#import <iostream>
#import <vector>
#import <boost/range/algorithm/for_each.hpp>
#import <boost/lambda/lambda.hpp>
#import <boost/assign/list_of.hpp>

template <class A1>
void invokeSelector(id target, SEL action, typename boost::mpl::identity<A1>::type a1)
{
    typedef void(*Action)(id, SEL, A1);
    Action func = (Action)[target methodForSelector:action];
    (*func)(target, action, a1);
}

template <class A1, class A2>
void invokeSelector(id target, SEL action,
                    typename boost::mpl::identity<A1>::type a1,
                    typename boost::mpl::identity<A2>::type a2)
{
    typedef void(*Action)(id, SEL, A1, A2);
    Action func = (Action)[target methodForSelector:action];
    (*func)(target, action, a1, a2);
}

template <class A1, class A2, class A3>
void invokeSelector(id target, SEL action,
                    typename boost::mpl::identity<A1>::type a1,
                    typename boost::mpl::identity<A2>::type a2,
                    typename boost::mpl::identity<A3>::type a3)
{
    typedef void(*Action)(id, SEL, A1, A2, A3);
    Action func = (Action)[target methodForSelector:action];
    (*func)(target, action, a1, a2, a3);
}

@interface Foo : NSObject

- (void)doSomethingI:(int)x;
- (void)doSomethingV:(const std::vector<int>&)x;

@end

@implementation Foo

- (void)doSomethingI:(int)x {
    std::cout << x << std::endl;
}

- (void)doSomethingV:(const std::vector<int>&)x {
    boost::for_each(x, std::cout << boost::lambda::_1 << ' ');
}

@end


@interface Bar : NSObject

- (void)main;

@end

@implementation Bar

- (void)main {
    Foo* foo = [[Foo alloc] init];

    invokeSelector<int>(foo, @selector(doSomethingI:), 5);
    invokeSelector<const std::vector<int>&>(foo, @selector(doSomethingV:), boost::assign::list_of(3)(1)(4));

    [foo release];
}

@end



int main()
{
    Bar* bar = [[Bar alloc] init];
    [bar main];
    [bar release];
}
5
3 1 4 

いつ頃からかはわかりませんが、最近のXcode(GCC)では、関数テンプレートの中でObjective-Cの構文を使えるようになったので、とても簡単に作れました。
methodForSelectorでセレクターの関数ポインタが得られるので、それに引数を渡してるだけです。


これでObjective-C++での開発がかなり楽になります。