Objective-C++11
この記事は Mac Dev JP Advent Calendar の参加記事 (6 日目) です。
はじめに
この記事では、 Objective-C++ で C++11 を使用する例について紹介します。
Objective-C++ とは
Objective-C++ は、プログラミング言語 Objective-C と C++ とを多重継承したような言語です。お互いの文法がかち合わないので、混在することができます。極稀に文法がコンフリクトしますが、個人的にはそれで困ったことはありません。文法的にほぼ直交し、お互いのオブジェクトを単なるポインタ値とみなしているため、混在させることができるのです。 Objective-C と C++ の機能をオーバーラップさせるようなことはできません。例えば、 Objective-C のクラスを C++ で継承したりするようなことはできません。 Objective-C の ARC を C++ のオブジェクトに適用するといったこともできません。ソースの拡張子は mm が一般的に使われます。
Objective-C++ を利用することで、 C++ の資産を使いつつ Cocoa アプリケーションを開発する、といったことが可能になります。あとは「C++ のヘッダファイルオンリーなライブラリを作りたいのにどうしても Cocoa 使わなきゃいけなくて、でも Cocoa は Objective-C から触らないといけなくて死ぬ」という場合も、 Objective-C++ は有用です。
問題点は、コンパイル時間が遅くなることです。ただでさえ複雑な文法を持つ Objective-C と C++ が混ざることで、コンパイル速度はかなり遅くなります。
Objective-C++ の例
#import <Cocoa/Cocoa.h> @interface Bar : NSObject @end @implementation Bar - (void)hello { NSLog(@"Hello!"); } @end class Foo { private: Bar* bar_; public: Foo(Bar* bar) : bar_(bar) { } void hello() { [this->bar_ hello]; } }; int main() { Bar* bar = [[Bar alloc] init]; Foo foo(bar); foo.hello(); }
Objective-C の世界と C++ の世界とが見事に入り交じっていることがわかりますね。これを test.mm という名前で保存したとして、次のようにすればコンパイルされ、 test という名前の実行ファイルができます:
clang++ -x objective-c++ -stdlib=libc++ -framework Cocoa -o test test.mm
C++11 とは
C++11 は、 2011 年に ISO で承認された C++ の最新標準規格です。 ISO に承認される前までは C++0x と呼ばれていました。 C++03 に対し大幅な機能追加が行われています。
宣伝
Ebiten (海老天) という 2D ゲームライブラリを作っています。 C++ ヘッダファイルオンリーなライブラリを目指すため、本稿のテーマである Objective-C++11 を活用しています。 API やコンセプトが毎日のように変わるので実用段階には程遠いです。
準備
最新の clang++ が必要になりますが、これは最新の Xcode 4.2 に含まれています。 App Store で落としましょう。でかいので落とすのに毎度とても時間がかかります。ほんとうに何とかして欲しいですね…。ちなみに gcc では、 Objective-C++ は扱えますが、 Objective-C++ と C++11 とを両立させることができません。
clang++ オプション
clang++ に、以下のオプションが必要です:
-x objective-c++ # 言語の指定 -std=c++0x # C++11 での指定。近い将来には -std=c++11 になるはず。というかなっている? -stdlib=libc++ # 標準ライブラリの指定
標準ライブラリの指定はなんで要るのかよく分からないのですが、指定しないと動きません。
実際にやってみた
型推論
例えば
NSApplication* app = [NSApplication sharedApplication]; HogehogeController* controller = [[HogehogeController alloc] init]; [app setDelegate:controller]; [app run];
と書いていたのを
auto app = [NSApplication sharedApplication]; auto controller = [[HogehogeController alloc] init]; [app setDelegate:controller]; [app run];
と書けるようになります。長い型名も書かなくて済みます。 id 型と違って、静的に型が定まるものなので、コンパイル時の検査がより強力になります。 C++11 の機能の中で、 Objective-C にも適用でき、かつ実用的である唯一の例だと思います。
範囲 for
// clang++ 3.0 では initializer list が未実装なので、仕方なく要素一個一個追加している std::vector<std::string> cppArray; cppArray.emplace_back("Foo"); cppArray.emplace_back("Bar"); cppArray.emplace_back("Baz"); // C++11 の範囲 for for (auto str : cppArray) { cppResult += str; } NSLog(@"%@", [[NSString alloc] initWithCString:cppResult.c_str() encoding:NSUTF8StringEncoding]); NSString* objcResult = @""; NSArray* objcArray = [NSArray arrayWithObjects:@"Foo", @"Bar", @"Baz", nil]; // Objective-C の範囲 for for (NSString* str in objcArray) { objcResult = [objcResult stringByAppendingString:str]; } NSLog(@"%@", objcResult);
二種類の for が混在できます。細かい文法が違います (コロンと in)。とても不気味ですね。
NSArray に C++11 の範囲 for を適用させることは不可能ではないと思いますが、ほとんど意味が無いと思います。やるとしたら std::begin/end の NSArray* による特殊化を行うことになるでしょう。
その他
追加された標準ライブラリ (thread など) も問題なく使用出来ました。
nil の代わりに nullptr を書いたりも出来ました。ただメリットは皆無であり、お互い同じ値である保証は全く無いので、辞めたほうが良いでしょう。
clang++ には C++11 のフル機能が実装されているわけではありません。詳しくは Clang - C++ and C++'11 Status を参照しましょう。例えば 2011 年 12 月 6 日 (日本時間) 現在では、 Initializer List やラムダ式などが使えません。
まとめ
本記事では、 Objective-C++ で C++11 の機能を問題なく使えるよ、ということを示しました。「C++11 の新機能で Objective-C で使うとかっこいい機能」というのが思ったよりもなかったのが残念です。 Objective-C で型推論出来てすげーと思って書き始めたものの、他に思ったより面白い例がなく、何が嬉しいのかよく分からない記事になってしまいました。
追伸
はてなブログってスーパー pre 記法に色つけられないのかな?