2014/11/26

[c/c++]enumはプリプロセスでは評価できません

「何を当たり前のことを」と言われそうだが、まあ聞いておくれ。

今、BLEの再履修をしているところだ。
一通り小さなサービスを動かしてきたことになるので、始めた当時にはよくわからなかったものが、今では理解できるようになったかもしれない。
私の場合、学習→試作→学習→試作、を繰り返すような感じで物事を把握することが多い。
なんというか、覚えるときは紙に目一杯書き込まないと覚えられないような、体で感じるタイプらしい。

 

まあそんなわけで、BLEのことを調べ直しているのだが、設定できる値の範囲が気になってきた。
例えば、connIntervalは7.5msec~4secの間だ。
これをS110とかでは、こんな表し方になる。

#define CONN_MAX_INTERVAL               MSEC_TO_UNITS(1000, MY_UNIT_1_25_MS)

MSEC_TO_UNITS()はapp_util.hにあるマクロで、こんな定義だ。

#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))

なんでこんな式かというと、connIntervalは7.5 + 1.25n、というBluetoothの仕様になっているからだ。
TIMEはmsec単位なので、それを1000倍して割る。
ということは、全体としてはμsecになってしまうので、そうならないようにRESOLUTIONも1000倍された値を使う。
「どうせconnIntervalなんて固定値でしか持たないだろうから、そこまでやらなくていいんじゃないの」と思ったが、実装にも仕様にも固定じゃないといかんというわけではなく、もしかしたら普通は曜日や時間帯に合わせて可変にするものだけど、サンプルだから固定にしているだけなのかもしれん。

 

前置きが長くなって済まん。
nRF51で使うconnInterval値の範囲は6~3200になるのだが、そういうマクロ値が既に定義されていることを知った。
BLE_GAP_CP_MAX_CONN_INTVL_MINが6で、BLE_GAP_CP_MAX_CONN_INTVL_MAXが3200とか。
ならば、コンパイル前に定義値の範囲チェックをしておけば、変なバグで悩むことが減るんじゃなかろうか、と思ったのだ。

それで、こんなチェックマクロを埋め込んだ。

#if (CONN_MAX_INTERVAL < BLE_GAP_CP_MAX_CONN_INTVL_MIN)
#error connInterval_Max(Connection) too small.
#elif (BLE_GAP_CP_MAX_CONN_INTVL_MAX < CONN_MAX_INTERVAL)
#error connInterval_Max(Connection) too large.
#endif  //CONN_MAX_INTERVAL

 

メモリを消費するでもなく、ビルド段階で範囲チェックができてよかろう、と満足したのだが、これがgccでエラーになるのだ(他のは知らん)。
さんざん悩んだが、結果としてはこのせいだった。

enum {
    UNIT_0_625_MS = 625,
    UNIT_1_25_MS  = 1250,
    UNIT_10_MS    = 10000
};

えっ、ここにenum使ってたの??
なんでまた・・・。

#defineはプリプロセス段階で展開されるけど、enumはコンパイル時に展開される。
だからプリプロセスとしては、MSEC_TO_UNITS(1000, MY_UNIT_1_25_MS)みたいなenumを含む式を見つけると、展開できないという扱いにしてしまい、ここではゼロとみなして「ゼロで割ってます」というエラーになっていたのだ。

まあ、enumにするとデバッガなどで値が見やすくなるのはあるけど、ここでは使ってほしくなかったなぁ。
せっかくdefineがあるけど、ここは自分で7.5msecとか4secとかの定義値を作って、値でチェックする方式に置き換える方がよさそうだ。
時間かかったわー。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。