2014/11/17

Arduino - typedef宣言を使用する際の注意

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;
}
製品紹介

1 件のコメント: