第13回

ポリモーフィズム

種類の違うものを、同じ手続きで扱う。

基本

IMU imu;
GPS gps;

Sensor* sensors[2];

sensors[0] = &imu;
sensors[1] = &gps;
for (int i = 0; i < 2; i++) {
    sensors[i]->update();
}

それぞれのオブジェクトのupdate()が呼ばれる。ループを書く側は、中身がIMUなのかGPSなのかを知る必要がない。

マイコンでの応用

例えばセンサの種類が変わっても、

Sensor* sensor;

として扱える。これにより、上位の制御コードをセンサの具体的な種類から分離できる。センサを別の製品に載せ替えても、上位のコードは書き換えずに済む。

     Controller          ← Sensorの種類を知らない
         │
         ↓
      Sensor*            ← 共通インターフェース
         │
   ┌─────┴─────┐
   ↓           ↓
  IMU         GPS

注意点

組み込みシステムでは、動的メモリ確保や仮想関数を無条件に多用するのではなく、

  • RAM使用量
  • Flash使用量
  • 実行時間
  • リアルタイム性
  • メモリ断片化

なども考慮する必要がある。

動かして確かめる

演習に入る前に、まずこのサンプルを動かして結果を見る。追加の部品は要らない。光るのはボードに載っている内蔵LEDで、それ以外はシリアルモニタの出力で確かめられる。code/examples/13-polymorphism/を開き、pio run -t uploadで書き込んでからpio device monitorで出力を見る(手順と、外付けのLEDなどをつなぐ場合は開発環境にまとめてある)。

code/examples/13-polymorphism/
├── include/
│   ├── Sensor.hpp
│   ├── IMU.hpp
│   ├── GPS.hpp
│   └── ToF.hpp
└── src/
    ├── IMU.cpp / GPS.cpp / ToF.cpp
    └── main.cpp     Sensor* しか出てこない
code/examples/13-polymorphism/src/main.cpp
// 第13回:種類の違うセンサを、同じ手続きで扱う
//
// loop() は中身が IMU なのか GPS なのか ToF なのかを知らない。
// センサを1種類増やすとき、loop() を1文字も変えずに済むことを確かめる。

#include <Arduino.h>

#include "GPS.hpp"
#include "IMU.hpp"
#include "Sensor.hpp"
#include "ToF.hpp"

IMU imu;
GPS gps;
ToF tof(6);  // センサはつないでいない。ピン番号は保持するだけ

// 上位のコードは「Sensorであること」しか知らない
Sensor* sensors[] = {&imu, &gps, &tof};
const int SENSOR_COUNT = sizeof(sensors) / sizeof(sensors[0]);

void setup() {
    Serial.begin(115200);
    delay(1000);

    Serial.print("センサの数: ");
    Serial.println(SENSOR_COUNT);
}

void loop() {
    for (int i = 0; i < SENSOR_COUNT; i++) {
        sensors[i]->update();
        sensors[i]->print();
    }

    Serial.println();
    delay(1000);
}
シリアルモニタの出力
センサの数: 3
IMU  roll=0.50 pitch=0.20
GPS  lat=35.6587 lon=139.7455
ToF  distance=375.00 mm

IMU  roll=1.00 pitch=0.40
GPS  lat=35.6588 lon=139.7456
ToF  distance=350.00 mm

確かめること。

  • loop()の中にIMUもGPSもToFも出てこない。Sensor*しか出てこない
  • 4種類目のセンサを足して、loop()を書き換えずに済むことを確かめる
  • sizeof(sensors) / sizeof(sensors[0])で個数を求めている。この手間は第16回で消える
  • 種類ごとにifで分けて書いた場合、センサが増えるたびにどこが伸びるかを考える

演習:センサを配列でまとめて更新する

  • IMU / GPS / ToFをSensor*の配列に入れる
  • loop()で全センサを一括更新する
  • センサを1種類追加したとき、loop()側を書き換える必要があるかを確認する