openFrameworks 0.84 iOS版 + 半C++11 + Xcode6

openFrameworks(以下oF)のiOS版をXcode6(6.1)で使おうとしたら全然すんなり行かなかったのでメモ。できるだけC++11で使いたいのですが、後述のとおりMacにおいてoFを完全C++11で使うことは難しく、現状できる半C++11状態での使い方をまとめます。まずMac/iOS版oFでビルド設定の類をいじる際に知っておきたい基本知識を先に挙げておきます。

-  XcodeでのoFアプリは2プロジェクト構成になっている(oF自体のプロジェクトと自分が作るアプリのプロジェクト)ので、設定によっては両プロジェクトの設定を書き換える/揃える必要がある
-  Xcodeにおいてビルドに関する設定は1プロジェクトにつき「project」と「target」という2段階がある。設定を変えたと思ってもビルド結果が変わらない気がしたときはこのあたりを確認すべし([stackoverflow参考](http://stackoverflow.com/questions/5881048/what-is-the-difference-between-target-and-project))

# 注意: Validate Project SettingsのPerform Changesはしない
XcodeでoFをビルドしていると、通るか通らないかに関わらず、oF側/アプリ側の両プロジェクトで「Validate Project Settings」というwarning表示が出ることがありますが、基本的にこれは放置の方がよいです。warning自体はXcode(というかMac/Apple)の推奨設定にプロジェクト設定を更新しませんか？という案内ですが、oFはoF側でビット幅など一部の設定を指定(限定)しているので、下手に変えるとビルドが通らなくなります。

# 修正1: Deployment Targetを上げる
[公式ページ](http://openframeworks.cc/download/)からダウンロードしたoF(iOS版)を解凍し、例えばExampleのaudioInputExampleのXcode projectを立ち上げビルドしてみると、早速通りません。

```
ld: -pie can only be used when targeting iOS 4.2 or later
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```

これは書いてあるとおりアプリ側のDeployment Targetがデフォルトで3.1になっているのが原因。適当に自分の都合のよいバージョンまで上げればOKです(私は7.0にしてます)。

# 作業2: undefined symbolを足す
Xcode6にするとリンク時に以下のように怒られます。

```
ld: warning: ignoring file ../../../libs/FreeImage/lib/ios/freeimage.a, missing required architecture i386 in file ../../../libs/FreeImage/lib/ios/freeimage.a (2 slices)
ld: warning: ignoring file ../../../libs/glu/lib/ios/glu-ios.a, missing required architecture i386 in file ../../../libs/glu/lib/ios/glu-ios.a (2 slices)
Undefined symbols for architecture i386:
  "_fwrite$UNIX2003", referenced from:
      _WriteProc(void*, unsigned int, unsigned int, void*) in freeimage.a(FreeImageIO.o-i386)
      LibRaw::dcraw_thumb_writer(char const*) in freeimage.a(libraw_cxx.o-i386)
      _opj_image_create in freeimage.a(image.o-i386)
      _png_default_write_data in freeimage.a(pngwio.o-i386)
      LibRaw::ppm_thumb() in freeimage.a(dcraw_common.o-i386)
      LibRaw::jpeg_thumb_writer(__sFILE*, char*, int) in freeimage.a(dcraw_common.o-i386)
      LibRaw::write_ppm_tiff() in freeimage.a(dcraw_common.o-i386)
      ...
  "_mktime$UNIX2003", referenced from:
      LibRaw::get_timestamp(int) in freeimage.a(dcraw_common.o-i386)
      LibRaw::parse_rollei() in freeimage.a(dcraw_common.o-i386)
      LibRaw::parse_riff() in freeimage.a(dcraw_common.o-i386)
  "_strerror$UNIX2003", referenced from:
      Iex::throwErrnoExc(std::string const&, int) in freeimage.a(IexThrowErrnoExc.o-i386)
  "_strtod$UNIX2003", referenced from:
      _png_handle_sCAL in freeimage.a(pngrutil.o-i386)
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```

これについては[ググればたくさん情報が出てくるとおり、](http://forum.openframeworks.cc/t/xcode6-beta-of-0-8-1-for-ios-8-0/16103/7) 当該関数の定義をどこかに作ってやればOKです。main.mmなどアプリ側のソースにアプリ毎に追加するか、addons/ofxiOS/src/app/ofAppiOSWindow.mmなどoF側のソースに共通設定として追加しましょう。後者なら一度やれば全アプリに適用でき、また暫定対応がアプリ側に残らなくて良いと思います。私は以下をofAppiOSWindow.mm冒頭に追加しました。

```cpp
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
extern "C"{
    size_t fwrite$UNIX2003( const void *a, size_t b, size_t c, FILE *d ){
        return fwrite(a, b, c, d);
    }
    char* strerror$UNIX2003( int errnum ){
        return strerror(errnum);
    }
    time_t mktime$UNIX2003(struct tm * a){
        return mktime(a);
    }
    double strtod$UNIX2003(const char * a, char ** b){
        return strtod(a, b);
    }
}
```

# 作業3: oFでできるだけのC++11機能を使う
一般にMac+clang+XcodeにおいてC++11の機能を有効にするには、build setting(1)C++ Language DialectをC++11に、(2)C++ Standard Libraryをlibc++に、すればよいのですが、oFを使うアプリでこれをやるとコンパイルエラーとリンクエラーの嵐になります。理由はoFが古いgccで作った(=libstdc++を使う)コンパイル済みライブラリとリンクする仕様だからであり、完全解決はoFから提供されるライブラリが新しくなるまでお預けです。このあたりの詳しい事情は[こちら](http://beatsync.net/main/log20131021.html)などを参照してください。

ではoFでできるだけC++11化することを考えましょう。その場合のXcodeのbuild settingは以下のようになり、gcc4.2時点でのC++11が有効になります。これはoF側のプロジェクトとアプリ側のプロジェクト両方で設定してください。

-  Language DialoctをGNU++11(-std=gnu-c++11)
-  C++ Standard Libraryをlibstdc++(GNU C++ standard library)

ただし、この設定にしてoFアプリのビルドを通すにはoF側のソースを数カ所直す必要があります。

1. ofType.h: 冒頭のinclude設定を以下のようにコメントアウトする(この修正は本記事で想定する環境以外では逆にビルドを通りにくくする修正ですので注意)

    ```cpp
    //#if (_MSC_VER)
    //#include <memory>
    //#else
    #include <tr1/memory>
    // import smart pointers utils into std
    namespace std {
    //#if __cplusplus<201103L
       using std::tr1::shared_ptr;
       using std::tr1::weak_ptr;
       using std::tr1::enable_shared_from_this;
    //#endif
       using std::tr1::static_pointer_cast;
       using std::tr1::dynamic_pointer_cast;
       using std::tr1::const_pointer_cast;
       using std::tr1::__dynamic_cast_tag;
    }
    //#endif
    ```

2. ofAppiOSWindow.mm: 116行のUIApplicationMainの第一引数をnilから0にする

    ```cpp
    UIApplicationMain(0, nil, nil, [NSString stringWithUTF8String:appDelegateClassName.c_str()]);
    ```

3. ofxiOSMapKit.mm: 114行の代入文にきちんとMKMapTypeへのキャストを明示する

    ```cpp
         mapView.mapType = (MKMapType)type;
    ```

ここまでがんばってやるとC++11の半分くらいの機能が使えるようになります。知っている範囲では以下のような機能です(デフォルトオプションのままで使えるものを含んでいるかも)。ちなみにinitializer_listとconstexprは使えませんでした。

-  tr1にあるヘッダの利用: memory, array, cstdint, type_traits, などなど(randomもつかえるが、実際にはまともに機能しない: [参考](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40331))
-  decltype
-  auto
-  range based for