地蔵インスタンス

2020年1月15日

過去の記事「動的メモリを使ってはいけない」で説明したように、組み込みで動的メモリを使う場面はほぼ皆無です。そして動的メモリを使わない限り、デストラクタは必要ありません。結果として組み込みプログラムでは、コンストラクタを持ち、デストラクタを持たないというクラスがほとんどを占めることになります。

ところが実は、C++ の仕様である「スコープを抜けたときには必ずデストラクタが呼ばれる」という仕組みだけを利用することで、コードをよりスマートに書き換えられる場合があります。その典型が割り込み禁止処理です。

やり方は簡単で、コンストラクタとデストラクタしかないクラスを定義し、そのインスタンスを置くだけです。私はこの種のインスタンスを「地蔵インスタンス」と呼んでいました。もちろん造語です。置いておくだけで効力が生まれることから、この名前があります。

コード例

地蔵インスタンスを使わないパターン

たとえば、あるレジスタのビットを操作する瞬間にだけ割り込み禁止をかけたいとします。コードはだいたい次のようになるでしょう。

void raisePortA0()
{
  __disable_interrupt();
  PORT_A |= 0x01;
  __enable_interrupt();
}

地蔵インスタンスを使うパターン

上のコードは、次のように置き換えることができます。

class Uninterruptible
{
public:
  Uninterruptible() { __disable_interrupt(); }
  ~Uninterruptible() { __enable_interrupt(); }
};

void raisePortA0()
{
  Uninterruptible ui;
  PORT_A |= 0x01;
}

メリット

コード量が減るわけでもないし、何が嬉しいのかわからない、と思われたかもしれません。地蔵インスタンスのメリットとは、次のようなものです。

つまらないミスが減る

割り込み禁止の例だと、よくあるミスにこのようなものがあります。disable を書いて、enable を書き忘れる。コードをコピペしたまま書き換えるのを忘れ、disable → disable となる。あるいは、うろ覚えのままコードを書いて、enable → disable としてしまう。地蔵インスタンスを使えば、このようなミスから逃れられます。

スコープがわかりやすい

地蔵インスタンスは、それが存在するスコープに効くという性質から、使う際には中括弧の存在が必須となります。そして、中括弧がつけばその中身はインデントされます。結果として、物事の始まりと終わりがソースコードの見た目に明確になります。

途中 return に強い

これが地蔵インスタンス最大の利点です。デストラクタは、スコープを抜けたときには必ず呼ばれます。それがたとえ goto によるものであろうが、例外によるものであろうが、「必ず」です。次の例を見てください。

void raisePortA0()
{
  Uninterruptible ui;
  if ((PORT_A & 0x01) != 0) { return; }
  PORT_A |= 0x01;
}

途中に return 文があります。古典的な割り込み禁止の記述の中でこれをやられると完全にアウトですが、地蔵インスタンスなら大丈夫です。デストラクタが食らいついてくれます。

このパターンに当てはまるものであれば、地蔵インスタンスは他にも応用が利きます。コンストラクタとデストラクタの性質をよく理解して、効果的に使ってみてください。