<概要>
Mac の開発業務では、C++ で記述されたライブラリを、 Objective-C 言語から利用したくなることが多々あります。
この時、通常は次の2つのアプローチのうち、どちらかを選びます。
1. 主開発言語を Objective-C ではなく Objective-C++ としてしまう。
2. 主開発言語はObjective-C のままで、C++ への橋渡しをするラッパー部分だけ Objective-C++ を使用する。
手法 1. は簡単に実現できますが、全体を Objective-C++ で開発することにしてしまうと、
2つのスタイルのオブジェクト指向言語が混ざり、設計が複雑、かつデバグが難しくなります(と思っています)。
手法 2. を実現するためには、多少複雑なトリックを必要とします。
やっとのことで、うまい手法を発見したので、ここに記しておきます。
<本文>
Objective-C++ ラッパーのヘッダファイルでトリッキーな #ifdef スイッチを用いて実現する。
次のような C++ クラスライブラリがあるものとする。
Wrapped.hpp
#ifndef __WRAPPED_H__
#define __WRAPPED_H__
class CWrapped
{
public:
CWrapped(int param1, int param2);
unsigned int doSomething();
}
#endif
Wrapped.cpp
#include "Wrapped.hpp"
CWrapped::CWrapped(int param1, int param2)
{
// do something
}
unsigned int CWrapped::doSomething()
{
// do something
return 0;
}
これを Objective-C++ の文法を用いてラップするわけであるが、そのラッパーを Objective-C から利用するためには、
少なくともそのヘッダファイルは C++ の文法を一切用いていない純粋な Objective-C のみの文法としなければならない。
特に、class キーワードが使えないため、以下のようなトリックを用いる。
Wrapper.h (インターフェース)
#import <Foundation/NSObject.h>
// declare CWrapped class for .mm (Obj-C++) files
#ifdef __cplusplus
class CWrapped;
#endif
// declare CWrapped class for .m (Obj-C) files (do not use the "class" keyword.)
#ifdef __OBJC__
#ifndef __cplusplus
typedef void CWrapped;
#endif
#endif
@interface Wrapper : NSObject {
CWrapped* mWrapped;
}
- (id)init:(int)param param2:(int)param2;
- (unsigned int)doSomething;
@end
Wrapper.mm (実装)
#import "Wrapper.h"
@implementation Wrapper
- (id)init:(int)param1 param2:(int)param2
{
if (self = [super init]) {
mWrapped = new CWrapped(param1, param2);
}
return self;
}
- (void)dealloc
{
delete mWrapped;
[super dealloc];
}
- (unsigned int)doSomething
{
return mWrapped->doSomething();
}
@end
Objective-C コードからは次のように利用する。
main.m
#import "Wrapper.h" // Objective-C から見ると class CWrapped ではなく、typedef void CWrapped なのでコンパイルが通る。
int main(int argc, char **argv)
{
Wrapper *wrapper = [[Wrapper alloc] init:10 param2:10];
unsigned int result = [wrapper doSomething];
[wrapper dealloc];
}
CWrapped は利用側 (main.m) から見ると void 型、ラッパー実装側 (Wrapper.mm) から見ると class CWrapped 型となる。
CWrapped のオブジェクトである mWrappedは、利用側 (main.m) から見てもラッパー実装側 (Wrapper.mm) から見ても、同じアドレスを指し、
特に、ラッパー実装側 (Wrapper.mm) では具体的な変数の型(クラス名)もわかるため、メンバメソッド等に通常通りアクセスでき、うまい。
参考文献:
[1] Mac Dev Center: The Objective-C Programming Language: Using C++ With Objective-C
Apple のドキュメント。この方法では利用側も Objective-C++ としなければならず、今回の目的にはそぐわない。
[2] Objective-C Wrappers for C++ Classes << Justin Souter Blog