Announce

PukiWiki contents have been moved into SONOTS Plugin (20070703)

ObjectiveCWrapper

Objective-C Wrappers for C++ Classes

概要

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