ArduinoでC++11
ずいぶんご無沙汰していました。スタッフの高木です。
久しぶりついでに、これまでとは方針を転換して、これからはマニアックな技術情報もこのブログで書いていきたいと思います。今回は Arduino についてです。Arduinoの話題だというと、電子工作の話か? と思う方もいらっしゃるかもしれません。しかし、クローバーフィールドはソフトウェアの会社ですので、題材が Arduino だとしてもソフトウェアの話をします。
最近では Arduino Zero Pro が発売され、Arduino Due をはじめとするARMを用いたボードが増えつつあります。しかし、「Arduino といえばやっぱりAVRでしょう!」ということで、当面は Arduino Uno、Arduino Mega 2560、Arduino Leonardo といったAVR系のボードの話題をしていくことにします。
今回は初回ですので、軽くC++の話をすることにします。Arduino IDEでは、基本的にはArduino言語というプログラミング言語を使います。けれどもこの Arduino言語、実態はほとんどがC++です。Arduino言語で書かれたスケッチは、簡単な前処理を行ってC++のコードに変換されます。その後、C++としてコンパイルされるわけです。スケッチの拡張子は .ino ですが、.cpp にすれば C++ を、.c にすれば C も使うことができます。せっかくですので、C++はC++でもC++11を扱うことにしたいと思います。
ちょっと前までは、Arduino IDEでは C++11 を扱うのはまともな方法では無理でした。というのは、バージョン 1.0.x 系の Arduino IDEに付属する GCC のバージョンは 4.3.2 で、このバージョンでは C++11 にはほとんど対応していませんでした。ところが、最近では ARM系のボードと Arduino IDEが統合され、最新のバージョンである 1.6.1 では GCC 4.8.1 が付属しています。GCC 4.8.1 といえば、C++11 の機能は一通り備わったバージョンです。C++14 には対応していませんが、まあいいでしょう。
GCC で C++11 を使うには、コンパイルオプションに -std=c++11 または -std=gnu++11 を指定しなければなりません。1.0.x 系の Arduino IDEでは、コンパイルオプションをユーザーが変える方法はありませんでした(Javaのソースコードを改変して、コンパイルし直すなら可能でしたが…)。しかし、最新バージョンであればそんな心配は不要です。スケッチを格納するフォルダに、platform.local.txt という名前のテキストファイルを作成し、次のように書けばコンパイルオプションを追加することができます。
compiler.cpp.extra_flags=-std=gnu++11
簡単ですね。これだけで、コンパイルオプションに -std=gnu++11 が追加され、C++11 の機能を使うことができるようになります。
ただ、手放しで喜ぶことはできません。Arduino は OS 無しで動く、いわゆるフリースタンディング環境です。フリースタンディング環境であれば、気の利いたライブラリはほとんどありません。コンテナもアルゴリズムもストリームも、本当に何も無いのです。OS があろうが無かろうが関係ないと思われる <type_traits> のようなヘッダもありません。また、これは C++11 であろうが無かろうが同じですが、例外処理は使えません。Arduino IDEでは、コンパイル時に -fno-exceptions オプションが追加されるからです。この対応そのものは完全に妥当なものだと思います。Arduino にも、StandardCplusplus というライブラリがありますが、例外処理があることを前提として設計された既存のライブラリを強引に使おうとしても、例外が発生すれば abort するしかないという残念な結果になってしまうのです。
難が無いわけではありませんが、Arduino で C++11 を使う価値が無いわけではありません。たとえば、Arduino Uno の場合、RAMはなんと2kバイトしかありません。2Gでも2Mでもなく、2kバイト(=2048バイト)しかないのです。しかも、AVRはハーバードアーキテクチャであり、命令とデータが別々のメモリ空間を使っています。普通のマイコンであれば、静的なオブジェクトに const 修飾子を付けておけば ROM に配置されるのに、AVR の場合は(希少な)RAM に配置されてしまうのです。PROGMEM を使って強引に ROM に配置できることはできますが、使い勝手は非常に悪いのです。そんなとき、C++11 の機能を使って、constexpr にできるところはそうしておくことで、RAM の使用を最小限に抑えることができるかもしれません。
こんなに小さな RAM なのに、Arduino は malloc 関数のためのヒープを持っています。だからといって潤沢なメモリを使えるわけはありません。スタックも結構厳しいのです。ですので、C++11 のムーブセマンティクスを使えば、一時オブジェクトの生成を最小限に抑えられるかもしれません。
メモリの節約の話に偏ってしまいましたが、Arduino で C++11 を使うことは決して無意味ではないのです。今回はこのあたりにしておきます。今後は、いろいろな技術情報を書いていきたいと思いますので、興味のある方は見に来てください。