はじめに
Arduinoでライブラリ化を考えているので、コーディングルールや命名規則を調べてみました。
(英訳が間違っていたらすみません(^^;)
※調べてみて、個人的には…あまり納得できないルールもありました。。
目次
- Arduino exampleスタイルガイド
- Arduino APIスタイルガイド
- その他 標準ライブラリに基づいたルール
1. Arduino exampleスタイルガイド
この方法でコーディングする必要はありませんが、コードを全レベルのユーザーに対して明解にしたい場合に役立ちます。これらはガイドラインであり、明確 かつ しっかりまとまったルールではありません。ガイドラインの一部は相互に競合する場合があります。
APIスタイルガイドも参照してください。
setup()とloop()の場所
setup()
とloop()
は、プログラムの最初に記載する。
全ての関数がこの2関数から呼ばれるため、初心者にとってもプログラムの概要を把握しやすい。
コメント
- 全ての変数と定数宣言に、その変数が何をするかコメントする。
- 全てのコードブロックに(できればブロックの前に)コメントする。
- 全てのfor文にコメントする。
if文
常にブロックフォーマットを用いる。
if (somethingIsTrue == TRUE) {
doSomething;
}
以下のようにはしない。
if (somethingIsTrue) doSomething;
ポインタ
ポインタの使用を避ける。
#define
#defineの使用を避ける。
変数
- 1文字の変数名は避ける。
-
val
やpin
のような変数名は避ける。buttonState
やswitchPin
のように表現する。 - ピン名称や変化しない値を定義する場合、const int型を使う。#defineほど面倒ではないが、変数と定数の差異はわかる。
- uint8_t等より、できるだけboolean, char, byte, int, unsigned int, long, unsigned long, float, double, string, array, void等の変数型を用いる。これらはドキュメントで説明されており、名前が簡単すぎない。
- 以下のように、ユーザーを混乱させる番号の組み合わせは避ける。
pin1 = 2
pin2 = 3
etc.
ピン番号を振り直す場合は、以下のように配列の使用を考える。
int myPins[] = { 2, 7, 6, 5, 4, 3 };
これにより、配列要素を使って新たなピン番号を参照できる。
digitalWrite(myPins[1], HIGH); // turns on pin 7
また以下のように、シーケンスで全てのピンをON/OFFすることも可能になる。
for (int thisPin = 0; thisPin < 6; thisPin++) {
digitalWrite(myPins[thisPin], HIGH);
delay(500);
digitalWrite(myPins[thisPin], LOW);
delay(500);
}
開始時のコード表現
良いタイトルブロック:
/*
スケッチタイトル
制御内容を簡潔に表現する.個々のピンに接続されたコンポーネントを参照する.
The circuit:
* 各inputに接続されているコンポーネントのリスト
* 各outputに接続されているコンポーネントのリスト
Created 年月日
By 著者名
Modified 年月日
By 著者名
http://url/of/online/tutorial.cc
*/
2. Arduino APIスタイルガイド
これは、ライブラリAPI表記用のスタイルガイドです。これらのいくつかは、上級プログラミングの慣例に反しています。それを承知の上で、多くのビギナーがArduinoをできるだけ簡単に始められるように作りました。よって、心の中でその原則を唱えながらコーディングしてください。
※その他、設計思想に関する記載は割愛しました(今回の調査目的から外れるため)。
完全な日常の単語を使う
関数名や変数名を簡潔にし過ぎない。技術的なものではなく、日常用語を使用する。 身近な概念の一般知識に対応する用語を選択する。専門知識を前提としない。
例えば、これがArduinoでpwm()
ではなくanalogWrite()
を使っている理由である。しかしながら、それが一般的に使われているか、基本的な名称であれば、略語は許容できる。例えば"HTTP"は比較的によく知られており、“SPI”はプロトコル名として有効である(“serial-peripheral interface”では、きっと長すぎる)。(プロトコルとして“TWI”や“I2C”は一般的に呼ばれているため、“Wire”は恐らく間違いだった)。
一般人にとって異なる意味を持つ単語は避ける
例えばプログラマーにとって、エラーは何かが発生したときの通知である。一方 一般人は悪いコトになる。
確立されているコアライブラリとスタイルを使う
- 入力のreadにread()、出力のwriteにwrite()を使う。例えば、
digitalRead()
、analogWrite()
など。 - バイトストリームを扱う場合、Stream.h と Print.h ライブラリを使う。それが適切でない場合は、少なくともモデルとしてそのAPIを使用するようにする。詳細は、以下を参照。
- ネットワーク・アプリケーションの場合、基本的に Client と Server ライブラリを使う。
- ライブラリのインスタンスを初期化するために、通常各種設定を含むbegin()を使う。それの終了にend()を使う。
関数名にスネークケースではなく、ローワーキャメルケースを使う
例えば analogReadは、analog_readではない。myNewFunctionは、my_new_function ではない。
可読性のために、Processing.orgからこれを採用した。
※記法の分類は、4cresさんの「識別子の命名規則(記法)の種類について」が参考になります。
LONG_CONSTANT_NAMES_FULL_OF_CAPS は読みにくい
(LONG_CONSTANT_NAMES_FULL_OF_CAPS : 大文字の長い定数名)
可能なら、シンプルにしてみる。
bool型引数を避けるようにする
その代わりに、それらの差異を表現する名前を持った 異なる2関数の提供を検討する。
ポインタの知識を前提としない
C言語の初心者が最も大きな障害に出くわし、& や *で大混乱する。よって、API中のそれらを避けられるのであれば、そうする。例えば、一つの方法として *表記ではなく配列表記を使うことによって参照渡しをする。
void printArray( char* array);
は、以下に置き換えられる。
void printArray(char[] array);
const charのような構造を使用してポインタ渡しをしているライブラリもいくつかあるが、それらを渡すことをユーザーに要求することは避けている。例えば、以下より
foo.readAccel(&x, &y, &z);
以下のように使っている。
xAxis = adxl.readX();
yAxis = adxl.readY();
zAxis = adxl.readZ();
シリアル通信
シリアル通信を使用する場合、ユーザーはハードコーディングの"Serial"よりも任意のStreamオブジェクトを指定する余地がある。これはライブラリにMegaやDueの全シリアルポートへの互換性を持たせ、SoftwareSerialのような代替インタフェースを使用することができる。Streamオブジェクトは、ライブラリのコンストラクタ、または begin()関数に(ポインタではなく参照として)渡すことができる。それぞれのアプローチ例は、 Firmata 2.3 か XBee 0.4を参照。
バイトストリーム通信を提供するライブラリを書くとき、ArduinoのStreamクラスを継承すれば、Streamオブジェクトを受け入れている全ライブラリで使用することができる。もし可能なら、read()が直ちにバッファ内のデータにアクセスし、受信待ちが発生しないように、受信データをバッファリングする。もし可能なら、write()は送信バッファにデータを保存する必要があるが、バッファが全送信ータをすぐに格納するスペースを持っていない場合は、待たなければならない。待機中にyield()関数が呼ばれる必要がある。
ここにAdafruitからのライブラリ例がある。デバイス関数を、うまくハイレベルのアクティビティへ落とし込んでいる。
https://github.com/adafruit/Adafruit-BMP085-Library
https://github.com/adafruit/DHT-sensor-library
これは、Wire(I2C)ライブラリからの抽象化でいい仕事をしている: https://github.com/adafruit/RTClib
3. その他 標準ライブラリに基づいたルール
以下のArduino標準ライブラリを見て、気づいたルールをまとめてみました。
記載項目は、__moaiさんの「各プログラミング言語のコーディング規約まとめ」を参考にさせていただきました。
(今回ピックアップしたルールが、すべて以下のArduino標準ライブラリに当てはまる訳ではありません)
- SD
- USBHost
- Ethernet2
+α
エンコーディング(文字コード)
UTF-8
インデント
- 文字種別:スペース
- インデント幅:2
インポート文
- 記述箇所:ファイルの先頭
- 宣言順:次の順番でグループ化する
- 標準ライブラリ
- サードパーティに関するもの
- ローカルなアプリケーション/ライブラリに特有のもの
命名規約
- 単語の省略は原則行わない
- 略語の扱い:略語で使われてる大文字をそのまま命名でも大文字として表現する
パッケージ名
- 基本的にアッパーキャメルケースを使う
- ただし、ライブラリ内の「examples」「src」「extras」「utility」は全て小文字で表現する
※おそらく、これは慣習なのだと思います
ファイル名
- アッパーキャメルケースを使う
- publicクラスはそのクラスで1ファイルとする
- 例外としてprivateクラスはpublicクラスのファイル内に含めても良い
クラス名
- 基本的に "ファイル名"+"Class"とする
- クラス名が長くなる場合はファイル名と同一でも良い
メンバ変数名
"_"+ローワーキャメルケース または ローワーキャメルケースのいずれかを使う
構造体名
- パアッパーキャメルケース または 全て大文字のスネークケースのいずれかを使う
- 要素名は、ローワーキャメルケースを使う
定数定義名
全て大文字のスネークケースを使う
列挙型(enum)名
- アッパーキャメルケースを使う
- 要素名は、全て大文字のスネークケースを使う
グローバル変数名
アッパーキャメルケースを使う
ローカル変数名
全て小文字のスネークケース または ローワーキャメルケースのいずれかを使う
おわりに
Arduinoのコーディングルールと命名規則を調べてみました。
標準ライブラリであっても、作者や出所が異なっているため、ルールが統一されていないというのが正直な感想でした。「3. その他 標準ライブラリに基づいたルール」には 一部 私の好みが反映されているところがあるかもしれませんが、ご容赦ください。
参照URL
- Arduino公式の Style Guide 「Writing Example Code」
- Arduino公式の API Style Guide
- Qiita 識別子の命名規則(記法)の種類について
- Qiita 各プログラミング言語のコーディング規約まとめ