Arduino IDEで例外処理
Arduino IDEで例外処理(Try-Catchなど)を「トライ」したときの記録。ただし、期待どおり動作せず。
まずは情報から
Arduinoボード向けコンパイルオプション
”try-catch”が含まれるコードをBuildしようとすると、コンパイルオプション”-fexceptions”を使え、、というエラーメッセージが表示される。platform.txt(どこか深いところにある)にある”-fno-exceptions”を削除し、”-fexceptions”を記載することが必要。下記リンクは参考情報。
なお、ESP32ボード向け(今回はM5Stack利用)では、このコンパイルオプション追加は不要であった。
単純なコードだが
下記コードは単純なコードだが、Arduino UnoではBuildできず。どうも、tryやcatch内に”Serial.println()”があるためのようだ。
// ESP32:OK Arduino Uno:NG
void setup() {
Serial.begin(9600);
}
void loop() {
try {
Serial.println("1");
} catch (...) {
Serial.println("2");
}
}
もしかしたら、これもコンパイルオプションで回避できるのかもしれない(見つからず、、ではあったが)。ただし、ESP32ボード向けでは、上記コードはBuildできる。
深堀り(調査)
調べると次のサイトに、
"Since there are no standard exception classes defined (and the ArduinoSTL library doesn't compile on the due out-of-the-box), you'll have to write your own exception classes, i.e something like this:"
という記述が見つかる。自分で例外クラスを定義する必要があるのかもしれない、、。
サンプルコード
上記サイトをほぼそのまま利用し、次のコードを作成(ESP32ボード向け)。
class Exception
{
private:
const char* _msg;
public:
Exception(const char* msg)
{
_msg = msg;
}
const char* Message() const
{
return _msg;
}
};
void ThrowExceptionFunc(int n)
{
if (n > 1) {
throw Exception("Something bad happened");
}
}
void ValidateExceptionHandling(int val)
{
Serial.print("val = "); Serial.println(val);
try
{
ThrowExceptionFunc(10/val);
Serial.println("Exception was not thrown:");
}
catch(Exception& ex)
{
Serial.print("Exception happened:");
Serial.println(ex.Message());
}
catch (...) {
Serial.println("Other exceptions");
}
}
void setup() {
Serial.begin(115200);
}
void loop() {
while (Serial.available()) {
String data = Serial.readStringUntil('\n');
int num = data.toInt();
ValidateExceptionHandling(num);
}
}
概要は下記のとおり。
- PC(シリアル)から入力された数値(エラー処理は未考慮)を、例外処理関数(ValidateExceptionHandling())に渡して処理
- その処理関数内で、入力数値(val)により、定義した例外クラスを呼び出し
- ”10/val”の値が1以上でThrow(ThrowExceptionFunc()にて)
- Throwを受け取る処理(catch(Exception& ex))を定義
- すべての例外を受け取る処理(catch(...))を定義
結果
val = 2
Exception happened:Something bad happened
val = 14
Exception was not thrown:
val = 0
Guru Meditation Error: Core 1 panic'ed (IntegerDivideByZero). Exception was unhandled.
Core 1 register dump:
PC : 0x400d0c5c PS : 0x00060b30 A0 : 0x800d0d3e A1 : 0x3ffb1f60
A2 : 0x00000000 A3 : 0x3ffbfe88 A4 : 0x3ffbfe88 A5 : 0x3ffb1f80
A6 : 0x00000003 A7 : 0x00000000 A8 : 0x800d0c5a A9 : 0x3ffb1f40
A10 : 0x0000000a A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffb84ac
A14 : 0x00ff0000 A15 : 0x3ffb1e70 SAR : 0x0000000a EXCCAUSE: 0x00000006
EXCVADDR: 0x00000000 LBEG : 0x400014fd LEND : 0x4000150d LCOUNT : 0xffffffff
ELF file SHA256: 0000000000000000
Backtrace: 0x400d0c5c:0x3ffb1f60 0x400d0d3b:0x3ffb1f80 0x400d1f29:0x3ffb1fb0 0x400860ed:0x3ffb1fd0
Rebooting...
ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4
”val = 2”がThrowされるケース、”val = 14”がThrowされないケースとなる。”val = 0”にて、0で割るが発生し、すべての例外を受け取る処理(catch(...))が動作することを期待したが、そのとおりにならず、リセットしてしまった。
少々調べると、ESP32向けでは、”stdexcept”というファイルに、例外クラスが定義されていることがわかった。
- logic_error
- domain_error
- invalid_argument
- length_error
- out_of_range
- runtime_error
- range_error
- overflow_error
- underflow_error
これらを、catch()に記載してもうまくいかず。また、他のロジック(アルゴリズム)により、上記に類似した例外を発生させようとしたが、うまくいかず。
終わりに
結局、期待どおりとならず。忘れた頃にリトライするかもしれない。