[Arduino Uno] ArduinoSTLでC++のSTLのようなものを使う

Arduino CLIでArduinoSTLをインストールし、C++のSTLではおなじみのstd::vectorを使ってみました
2021.04.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部のうらわです。

最近Arduino(Arduino Uno Rev3 ATmega328)を購入し、Neovim/C++/Arduino CLIでお試し中です。

あまりArduinoの開発事情を知らずにC++で進めていますが、さっそく標準ライブラリは使えないけどどうすれば良いの、という問題にぶつかりました。初歩的かもしれませんがどう対応したかをご紹介します。

作業環境

  • Mac/NeoVim/C++/Arduino CLIで作業しています。Arduino IDEは使用していません。NeoVimではclangdを導入して補完や定義へ移動が効くようにしています。

  • arduino-cliコマンド実行時には--config-fileオプションで設定ファイルarduino-cli.yamlを指定します。これは、デフォルトの設定ファイルやライブラリの保存先($HOME/Library/Arduino15)から任意のディレクトリに変えるために指定しています。

# 例
$ arduino-cli board list --config-file ~/.arduino15/arduino-cli.yaml

上記の2点について、詳細は前回の記事をご参照ください。

ArduinoSTLのインストール

Arduino UnoではArduinoSTLというライブラリをインストールすることでC++の標準ライブラリのようなものを使うことできるようです。このライブラリを使わず<vector>などをインクルードしてもエラーになります。

ArduinoSTLのインストールはArduino CLIでできます。

# ライブラリを探す
$ arduino-cli lib search ArduinoSTL
Updating index: library_index.json downloaded
Name: "ArduinoSTL"
  Author: Mike Matera <matera@lifealgorithmic.com>
  Maintainer: Mike Matera <matera@lifealgorithmic.com>
  Sentence: A port of uClibc++ packaged as an Arduino library.
  Paragraph: This library includes important C++ functions, including cout and cin, printf and scanf. It also includes STL containers like vector and algorithm.
  Website: https://github.com/mike-matera/ArduinoSTL
  Category: Other
  Architecture: avr, samd
  Types: Contributed
  Versions: [0.1.1, 0.1.2, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 1.0.2, 1.0.4, 1.0.5, 1.1.0]
  Provides includes: ArduinoSTL.h
  // ...省略...

# インストール
$ arduino-cli lib install ArduinoSTL --config-file ~/.arduino15/arduino-cli.yaml
Downloading ArduinoSTL@1.1.0...
ArduinoSTL@1.1.0 downloaded
Installing ArduinoSTL@1.1.0...
Installed ArduinoSTL@1.1.0

compile_flags.txtにインストールしたArduinoSTLのヘッダーファイルの場所を記載しておきます。これでclangdによる補完等が機能します。

-I/path/to/.arduino15/libraries/ArduinoSTL/src

実装・コンパイルする

準備ができたので、std::vectorstd::sortを使う適当なC++ファイルを作成します。

#include <Arduino.h>
#include <ArduinoSTL.h>

std::vector<String> v;

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < 10; i++) {
    v.push_back(String("hello!") + String(i));
  }
}

void loop() {
  std::sort(v.begin(), v.end());
  for(int i = 0; i < 10; i++) {
    Serial.println(v[i]);
    delay(2000);
  }
}

コンパイルするとArduinoSTL関連で以下のエラーが出てしまいました。

$ arduino-cli compile --fqbn arduino:avr:uno DummySketch --config-file ~/.arduino15/arduino-cli.yaml
/path/to/.arduino15/libraries/ArduinoSTL/src/d
el_opnt.cpp:25:56: error: 'nothrow_t' in namespace 'std' does not name a type
 _UCXXEXPORT void operator delete(void* ptr, const std::nothrow_t& ) throw() {
                                                        ^~~~~~~~~
/path/to/.arduino15/libraries/ArduinoSTL/src/d
el_opvnt.cpp:25:58: error: 'nothrow_t' in namespace 'std' does not name a type
 _UCXXEXPORT void operator delete[](void* ptr, const std::nothrow_t& ) throw(){
                                                          ^~~~~~~~~
/path/to/.arduino15/libraries/ArduinoSTL/src/d
el_opvs.cpp:25:53: error: 'std::size_t' has not been declared
 _UCXXEXPORT void operator delete[](void * ptr, std::size_t) throw(){
                                                     ^~~~~~
/path/to/.arduino15/libraries/ArduinoSTL/src/d
el_ops.cpp:25:50: error: 'std::size_t' has not been declared
 _UCXXEXPORT void operator delete(void* ptr, std::size_t) throw(){

頑張っていろいろ検索したところ、以下のissueにワークアラウンドとしてAVRボードバージョンを最新の1.8.3から1.8.2にダウングレードする、という記載があります。Arduino CLIでバージョンを指定してインストールすることで、コンパイルエラーは消えました。

参考:https://github.com/mike-matera/ArduinoSTL/issues/56#issuecomment-648091795

# 何も指定しないと最新のArduino AVRボードバージョンでインストールされる
$ arduino-cli core install arduino:avr --config-file ~/.arduino15/arduino-cli.yaml

#バージョンを指定してインスールする
$ arduino-cli core install arduino:avr@1.8.2 --config-file ~/.arduino15/arduino-cli.yaml

おわりに

なんとかC++の感覚で実装を進められそうで良かったです。

その他、Arduinoの開発関連でよく使うライブラリは以下の記事がとても参考になりそうです。本記事で使用しているArduinoSTLと共存して利用可能なC++の標準ライブラリのような挙動をするライブラリについても解説されています。かなり便利そうなので使ってみようと思います。