めっちゃ便利なマイコンESP32をArduinoとして使った際に、std::stringをStringに値渡しするのにちょっと手間取ったのでメモ。
環境はArduino IDE 1.8.9 / esp32-arduino 1.0.0です。
なお筆者あんまり分かっていないので、もしかしたら危ない処理してたり間違ったこと書いたりしているかもしれません。自己責任でどうぞ。
問. Stringを引数に持つ関数にstd::stringを値渡ししたい
ArduinoのStringとC++のstd::stringを混同するというのは、人生誰しも1回は経験することだと思います。ちなみに私はしばらくの間、全く同じものだと思っていました()
String、std::stringそれぞれに便利なところがあるよねーということで、両方使ったプログラムを作ったのですが、その中でStringを引数に持つ関数にstd::stringを値渡ししたい場面が出てきました。
とりあえず何も考えずに書いたコードが以下の通りです。
//受け取った文字列を大文字にしてエコーバック void changeUC(String str){ str.toUpperCase(); Serial.println(str); } //改行コードが来るまでreadしてchangeUCに投げる void loop(){ static std::string rcvMsg; while(Serial.available()){ tmp = Serial.read(); rcvMsg.push_back(tmp); if(tmp == '\n'){ changeUC(rcvMsg); rcvMsg.clear(); } } }
はい。Stringなんてどうせstd::string継承してごちゃごちゃしたようなやつだろうからそのまま渡してオッケーでしょ、とかいう筆者の軽い気持ちがよく表れていますね。
これをArduino IDEでコンパイルするとこんなエラーが出ます。
no known conversion for argument 1 from 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'String'
「std::stringからStringに変換なんてできねーよバーカ」だそうですぐぬぬ。
答. char型の文字列にしちゃえばいい
ArduinoのStringには、次のようなコンストラクタが設定されています。
String stringOne = "Hello String"; // using a constant String String stringOne = String('a'); // converting a constant char into a String String stringTwo = String("This is a string"); // converting a constant string into a String object String stringOne = String(stringTwo + " with more"); // concatenating two strings String stringOne = String(13); // using a constant integer String stringOne = String(analogRead(0), DEC); // using an int and a base String stringOne = String(45, HEX); // using an int and a base (hexadecimal) String stringOne = String(255, BIN); // using an int and a base (binary) String stringOne = String(millis(), DEC); // using a long and a base String stringOne = String(5.698, 3); // using a float and the decimal places
(https://www.arduino.cc/reference/から引用)
色々あって便利そうですが、とりあえずchar型文字列を渡してやればStringにできるようです。
std::stringには中身の文字列をchar型文字列としてその先頭ポインタを返してくれるメソッドc_str()があります。
ということで、changeUC()の呼び出しを下記のように変えれば、std::stringからStringに値渡しができることになりますね。
changeUC(rcvMsg.c_str());
入力
abc
出力
ABC
ばっちりです。
※ std::string.c_str()の取扱いには注意
こんな感じでよくお世話になるc_str()ですが、取り扱いには注意が必要なようです。
c_str()の戻り値はstringの持つ文字列の先頭文字へのポインタ*charです。
よって、c_str()で取得した文字配列の中身を変更すると、元のstringが存在するメモリを直接書き換えてしまうことになります。
そのため、c_str()で取得したポインタの中身を書き換えることは禁止されています。
今回の場合は、ArduinoのStringのコンストラクタに渡した段階で値がコピーされるので、そこから作ったStringの中身は自由に変更して問題ありません。たぶん。
以上、初心者のメモでした。
コメント