2013年12月30日月曜日

C++11 と obj-c

別に obj-c を使ってるわけでもなんでもないのですけど、前回の記事で cocoa のコードをつらつら書いてるときに”拡張子を .mm にして clang に食わすとその中身は C++ と obj-c が混在しまくってても OK"というのを知りました。
オブジェクトの互換性はなく、 obj-c のクラスを C++ で継承したりその逆はできないみたいですが、それ以外のことは一通りできており、 C++ で設計、記述したクラスに実に自然に mac の GUI を被せることができて結構感動したものです。
もうこれでくだらない glue を沢山書かなくていいんだ!

それはそれで大変素晴らしいことなのですが、それでは C++11 ではどうなのかというところが気になりました。
気になる事は試してみましょう。

まず、 clang で C++11 を有効にするには -std=c++11 をつけてコンパイルします。
更に、 C++11 の新しい標準ライブラリを使うには -stdlib=libc++ をつけます。
libstdc++ とかではありません。

cmake でいうとこうです。
   set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++")
ただ一つ問題があります。
cmake の project に .m が混在しているとこれはエラーになってしまいます。
.mm だけならば問題ありません。 project を別けるかしましょう。
無論、自分で makefile を書くならば suffix ルールを別けるだけで済みます。
まぁ、全部 .mm にしちゃいましょう。

これで準備は完了です。
界面に関わらないことであれば、概ね C++11 の機能が使えるようです。

クロージャ

C++ のコンテキストでクロージャを呼び出すことは難なくできました。
これを obj-c のメソッドに渡すことを考えてみましょう。
Test::foobar というクロージャを受け取って呼び出す obj-c のクラスを考えます。

Test* test = [Test alloc];
const char* msg = "Hello";
[test foobar:[msg](){printf ("%s\n", msg);}];
太字がクロージャであります。
Test の実装はこんな感じ。
@interface Test : NSObject
@end

@implementation Test
- (void) foobar:(std::function< void(void) >)closure
{
  printf("call begin\n");
  closure();
  printf("call end\n");
}
@end
結果は以下の通り。

call begin
Hello
call end
問題なく受け渡すことができました。

__attribute__

クロージャが使えるならかなり色々なことができそうです。
界面に関わるうち、構文上どうにもならなそうなもの以外(例えば obj-c メソッドのシグネチャでrvalue 制約や型推論を使う等)は一通り使えそうです。

構文上厳しそうなものといえば [[]] による __attribute__ です。
GNU 拡張の __attribute__ ディレクティブを、 clang は理解しますが、折角仕様に盛り込まれたのだからそっちを使ったほうがいいです(もっとも、 no_inline だけは効いてくれたことがないですが)。
でもこいつは [] で括る obj-c の文法と相性が悪そうです。
    float vec4 [[align(4)]] [1];
    float vec8 [[align(8)]] [1];
    float vec16 [[align(16)]] [1];
    printf("vec4:%p vec8:%p vec16:%p\n", vec4, vec8, vec16);
一応コンパイルは通りました。ですが実行結果は以下の通り。
vec4:0x7fff5fbfee78 vec8:0x7fff5fbfee74 vec16:0x7fff5fbfee70
手元の clang では残念ながら [[align(x)]] 自体が動いていないようです。
gcc-4.9 では大丈夫だったように思うのですが。

まぁ、使えるんじゃないですかね?(鼻をほじりながら)

0 件のコメント:

コメントを投稿