C/C++のようで違う,Arduino
前回の記事でも書きましたが,関数のプロトタイプ宣言をユーザが記述する必要がないなど,Arduino IDEで使用するプログラミング言語はC/C++とは若干異なります.
実際には,スケッチ内のタブのうち.inoファイルと拡張子のないファイルは,コンパイル前に1つの.cppファイルに統合されるようです. 一方,.cファイルと.cppファイルは個別にコンパイルされ,最後にリンクが実行されます.
これらの処理は,ユーザのコーディングを簡略化することを目的としているのだと思います. しかしながら,ここに罠が隠れており,私はその罠にかかってしまいました.
typedef宣言が無視されてしまう
コンパイルエラーとなるプログラム
一見すると問題のないソースコードのようですが,コンパイルエラーが発生します. メッセージは「led_test2:12: error: 'STATUS_LED' does not name a type」でした.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | /* * LED点滅回路 * File: led_test2.ino * Author: Keitetsu */ #define PIN_LED 2 typedef enum status_led{ OFF = 0, ON } STATUS_LED; /* * セットアップ関数 * * 引数: * なし * * 復帰値: * なし */ void setup() { pinMode(PIN_LED, OUTPUT); } /* * ループ関数 * * 引数: * なし * * 復帰値: * なし */ void loop() { static STATUS_LED led; led = led_toggle(led); delay(500); } /* * LED点滅関数 * * 引数: * STATUS_LED led LED点滅関数実行前のLEDの状態 * * 復帰値: * STATUS_LED LED点滅関数実行後のLEDの状態 */ STATUS_LED led_toggle(STATUS_LED led) { if (led == OFF){ digitalWrite(PIN_LED, HIGH); led = ON; } else { digitalWrite(PIN_LED, LOW); led = OFF; } return led; } |
問題の原因
この問題の原因は,Arduino IDEがプロトタイプ宣言を自動挿入する位置がプリプロセッサ(#includeや#defineのこと)の直後であることです. つまり,プロトタイプ宣言はプリプロセッサとtypedef宣言の間,8行目に自動挿入されることになります. したがって,typedef宣言でデータ型を定義する前にそのデータ型を使用したプロトタイプ宣言があることになり,コンパイルエラーとなってしまうのです.
問題の回避方法
方法(1) typedef宣言をヘッダファイルに移行する
Arduino Build Processでも紹介されている問題回避方法です. 自動挿入されるプロトタイプ宣言はプリプロセッサの直後なので,プリプロセッサでインクルードされるヘッダファイルでtypedef宣言を行えば良い,というものです.
方法(1)を適用したプログラムを示します. 下記は既存タブのled_test2.inoです. 新規作成するヘッダファイル,led_test2.hをインクルードしています.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | /* * LED点滅回路 * File: led_test2.ino * Author: Keitetsu */ #include "led_test2.h" #define PIN_LED 2 /* * セットアップ関数 * * 引数: * なし * * 復帰値: * なし */ void setup() { pinMode(PIN_LED, OUTPUT); } /* * ループ関数 * * 引数: * なし * * 復帰値: * なし */ void loop() { static STATUS_LED led; led = led_toggle(led); delay(500); } /* * LED点滅関数 * * 引数: * STATUS_LED led LED点滅関数実行前のLEDの状態 * * 復帰値: * STATUS_LED LED点滅関数実行後のLEDの状態 */ STATUS_LED led_toggle(STATUS_LED led) { if (led == OFF){ digitalWrite(PIN_LED, HIGH); led = ON; } else { digitalWrite(PIN_LED, LOW); led = OFF; } return led; } |
下記は新規タブのled_test2.hです. typedef宣言を記述しています.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* * File: led_test2.h * Author: Keitetsu */ #ifndef __LED_TEST2_H__ #define __LED_TEST2_H__ typedef enum status_led{ OFF = 0, ON } STATUS_LED; #endif |
方法(2) プロトタイプ宣言を自分で記述する
プロトタイプ宣言が既に存在する場合は,自動挿入が実行されません. したがって,typedef宣言の後方に自分でプロトタイプ宣言を書いておくことでも,この問題を回避できます.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /* * LED点滅回路 * File: led_test2.ino * Author: Keitetsu */ #include "led_test2.h" #define PIN_LED 2 typedef enum status_led{ OFF = 0, ON } STATUS_LED; /* プロトタイプ宣言 */ STATUS_LED led_toggle(STATUS_LED led); /* * セットアップ関数 * * 引数: * なし * * 復帰値: * なし */ void setup() { pinMode(PIN_LED, OUTPUT); } /* * ループ関数 * * 引数: * なし * * 復帰値: * なし */ void loop() { static STATUS_LED led; led = led_toggle(led); delay(500); } /* * LED点滅関数 * * 引数: * STATUS_LED led LED点滅関数実行前のLEDの状態 * * 復帰値: * STATUS_LED LED点滅関数実行後のLEDの状態 */ STATUS_LED led_toggle(STATUS_LED led) { if (led == OFF){ digitalWrite(PIN_LED, HIGH); led = ON; } else { digitalWrite(PIN_LED, LOW); led = OFF; } return led; } |
製品紹介
売り上げランキング: 6
参考になりました。 de 野兎
返信削除