2014/12/20 CMakeで同一プロジェクト内にC++11コードとObjective-Cコードを混在させる
最近、僕が趣味でも仕事でもかかわってる大半のプロジェクトのビルド環境をCMake化することができた。ただ、その途中で、ひとつだけ、ネットで答えを探してもストレートな答えが見つからなかったのが、どうやってObjective-CコードとC++11を使ったC++コードを混在させるか、ということだった。(解決策を編み出すまでに数時間考えた。)
本質的に、Objective-Cは、C言語から、言語の美しさを捨てて、オブジェクト指向っぽくしようとして失敗した言語だ。だから、使わないで済むなら使わないに越したことは無い。
が、Mac
OSXにプログラムを対応させる場合、不幸なことに、Objective-Cを使わざるを得ない。幸運なことに、Objective-Cの関数は、Cから呼び出すことができて、Objective-CからCの関数を呼び出すことができる。C++からも、extern
"C"を使うことで、行き来が可能だ。だから、Objective-C依存部分をとことんまで最小化することで、Mac
OSX用の実用的なプログラムを書くことができる。
Objective-Cの利用を最小化するためには、プラットフォーム依存機能をカバーするライブラリを書いて、ライブラリ内にObjective-Cコードを封じ込めて、外部から見えなくすればいい。しかし、そのようなライブラリ、例えば、ウィンドウを一枚開いてキーボードやマウスからの入力を受け付けるようなライブラリでは、C++のソースコードと、Objective-Cのソースコードを混在させなくてはならない。
そのC++のソースコードで、C++11の機能を使おうとすると、問題が発生する。GCCやClangを使ってC++11のコードをコンパイルするためには、-std=c++0xオプションを追加する必要がある。CMakeLists.txtの中では、
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=c++0x)
と書けばいい。ところが、cmakeにはバグがあった。このCMAKE_CXX_FLAGSがObjective-Cコードのコンパイルでも使われてしまう。Objective-Cの標準にc++0xというのは存在しないので、コンパイラがエラーを出して止まってしまう。
上に書いた通り、Objective-CはC言語の劣化版なので、C++よりCに似ている。だから、渡すなら、CMAKE_C_FLAGSを渡すべきだ。ところが、このバグのため、CMAKE_CXX_FLAGSが渡ってしまう。
他のほとんどのことは、大体困ったらGoogleが答えを教えてくれたのだが、この問題だけは、どうしても答えが見つからなかった。Objective-Cだけ別ライブラリにしようかとまで思ったところで、ついに解決策を発見。CMakeでは、各ソースごとのオプションを指定できるので、ソースファイルが、
set(SRCS
library_source.cpp
objectivec_part.m
)
こんな感じで定義してあって、C++にだけ適用されるべきフラグ(Objective-Cに適用されると困るフラグ)を、
set(YS_CXX_FLAGS "-std=c++0x")
こんな感じで定義してあるとすると、
if(YS_CXX_FLAGS)
foreach(SRC ${SRCS})
if(${SRC} MATCHES .cpp$)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/${SRC}
PROPERTIES
COMPILE_FLAGS
"${YS_CXX_FLAGS}")
endif()
endforeach(SRC)
endif()
このループで、ファイル末尾が.cppで終わるファイル全部についてYS_CXX_FLAGSが適用される。この方法で、僕が嫌々、仕方なく書いたObjective-Cのソースファイルも含めることができた。
|