setjmp/longjmpによるメモリリークパターン

setjmp/longjmpによるメモリリークパターンを紹介します。
次のコードを見てください。

main.c

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

jmp_buf env;

void sub(void)
{
  longjmp(env, 1);
}

int main(int argc, char** argv)
{

  char* buf1 = NULL;
  char* buf2 = NULL;

  buf1 = (char*)malloc(100);
  printf("1:buf1 = %p\n", buf1);

  if(setjmp(env)){
    printf("3:buf1 = %p\n", buf1);
    printf("4:buf2 = %p\n", buf2);
    free(buf1);
    free(buf2);
    return 0;
  }

  buf2 = (char*)malloc(200);
  printf("2:buf2 = %p\n", buf2);

  sub();

  return 0;
}

開始直後に確保したメモリをbuf1に、setjmp後に確保したメモリをbuf2に結び付けています。
その後sub()呼び出しによりlongjmpが行われ、遷移先(setjmp戻り真の場合の処理)で各メモリを解放する、というものです。
このコードをUbuntu Linux (x86)上でコンパイルし、実行すると

$ gcc main.c -O0
$ ./a.out 
1:buf1 = 0x83a9008
2:buf2 = 0x83a9070
3:buf1 = 0x83a9008
4:buf2 = 0x83a9070
$ 

となります。特に問題なく、メモリ解放できていそうです。
しかし、これを最適化ありでコンパイルすると、

$ gcc main.c -O2
$ ./a.out 
1:buf1 = 0x8170008
2:buf2 = 0x8170070
3:buf1 = 0x8170008
4:buf2 = (nil)
$

となります。
buf2がNULLになっているため、buf2の解放ができていません。
理由は、最適化されていない場合、buf1、buf2はスタックポインタ(レジスタ)が”指し示す”スタック上の1データにすぎないため、setjmpコール時点もlongjm後も変化しない。しかし、最適化が行われると、このコードの場合、buf1やbuf2そのものがレジスタに割り当てられようです。setjmpでコール時点でのそのレジスタ状態(buf2はNULLだという状態)が保持され、longjmpで遷移した時にsetjmpで保持されたもの(buf2はNULLだという状態)がレジスタとして復元されるためです。

http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/longjmp.3.html
に、

以下の条件が全て成立する場合、 longjmp() の呼び出しが行われた後の自動変数の値は未定義 (unspecified) となる。

  • その自動変数が、対応する setjmp(3) 呼び出しを行った関数のローカル変数である。
  • 自動変数の値が setjmp(3) と longjmp() の間で変更されている。
  • volatile として宣言されていない。

とあります。
また、

    longjmp() や siglongjmp() を使うと、プログラムは理解しづらく、保守しにくいものになる。別の方法が可能なら、それを使うべきである。

また、setjmpのほうにも

    setjmp() や sigsetjmp() を使うと、プログラムは理解しづらく、保守しにくいものになる。別の方法が可能なら、それを使うべきである。

と記載があります。

setjmp/longjmpの利用には細心の注意が必要です。

MonoDevelopコード生成ウィンドウからコード生成

UnityのC#スクリプトを書くために、Unityに付属していたMonoDevelopを使用しています。

継承元のメソッドをオーバライドする際に、雛形を自動生成する方法を探していました。(当然あるだろうと思っていましたので。)
メソッドを追加したい場所で、右クリックすると表示されるメニューの中に「コード生成ウィンドウを表示」というものがあり、これがどうやら自動生成をするための物らしく、必要なメソッドの一覧、生成するしないのチェックボックスらしきものが表示されました。

しかしながら、ここから実際にコードを生成するアクションが分からず、小一時間いろいろ調べていました。

そして、ようやく、(Macの場合、)Enterで入力、という答えにたどり着きました。
非常につまらない盲点でした。

ちなみにinterfaceの実装でメソッドを自動生成する場合はもっと簡単です。
コード内のinterfaceを指定している記述部分で右クリックします。メニューが表示されますので、「リファクタリング」>「暗黙に実装」で、

throw new System.NotImplementedException ();

のみを行う必要なメソッドが自動生成されます。

3要素を一括りにして扱う

C++で頻繁に二つの要素を一つをキーにして扱いたい時に、mapを使用していました。また、それ以上の数の要素を扱う場合は、mapの要素にクラスを入れて対応していました。

std::map<int, HogeClass*>

のような感じです。

ただ、どうしてもクラスや構造体を使いたくない場合があり、3要素をひとまとめにしたい、というケースがたまたまあり、どのようにしたら良いか調べてみました。すると、次のような例があり、採用しました。

std::map<int, std::pair<int, double>>

vectorの気遣い

vectorで(ポインタではなく)オブジェクト自体を取り扱う場合、push_backしたときに次のことが行われています。

  • push_backの対象となるオブジェクトをコピーコンストラクタでコピー
  • 既にpush_back済みのものがあれば、再びコピーコンストラクタでそのコピーを作り、元々コピーしてあったものをデストラクタで始末する。

実際にコードを書いて確認してみました。
まずは、クラス定義から。

class Hoge
{
public:
  int num;
  Hoge(int num);
  Hoge(const Hoge& me);
  ~Hoge();
  void printAbc(void);
};

Hoge::Hoge(int aNum)
{
  num = aNum;
  printf("constructer %d\n", num);
}

Hoge::Hoge(const Hoge& me) : num(me.num)
{
  printf("copy constructer %d\n", num);
}

Hoge::~Hoge()
{
  printf("destructer %d\n", num);
}

実際にvectorを使ってクラスHogeのオブジェクトを操作してみます。

int main(int argc, char** argv)
{
  Hoge sample1(1);
  Hoge sample2(2);
  std::vector<Hoge> iremono;

  iremono.push_back(sample1);
  iremono.push_back(sample2);

  return 0;
}

コンパイルして実行しますと、次のような結果が得られます。

constructer 1
constructer 2
copy constructer 1
copy constructer 2
copy constructer 1
destructer 1
destructer 1
destructer 2
destructer 2
destructer 1

最初のコンストラクタ二つは、ソースの3,4行目によるものです。このコンストラクタに対するデストラクタは、最後の二つで、mainを抜ける際に、ローカル変数であるsample1,sample2に対して発生しています。
最初のコピーコンストラクタは一回目のpush_back時に、つまりsample1のpush時に発生しています。そして次のコピーコンストラクタは二回目のpush_back時、つまりsample2のpush時に発生しています。直後、一回目のコピーコンストラクタでコピーされたもののコピーが発生しています。そして、次のデストラクタで一回目のpush_back時にコピーしたもののデストラクタが発生しています。
つまり、push_back時にはpushしたものがコピーされるだけではなく、既に入っているものも同時にコピーし直されます。

そして、次のデストラクタ二つはローカル変数iremonoがmainから抜けるとき、中のオブジェクトのデストラクタが発生することを示しています。このように明示しなくても中のオブジェクトをデストラクトしてくれるのは、vectorで(ポインタではなく)オブジェクト自体を持つことのメリットだと思います。ちなみに、このときのデストラクタの発生する順番はpush_backした順番です。

私はvectorでオブジェクト自体を持つのは避けるようにしています。上記のようなメリットはあるのですが、vectorの「気遣い」であるコピーコンストラクタ、デストラクタに、反って戸惑わされる恐れがあるからです。それよりも「後始末をしっかりする」ことさえ注意していればよい「ポインタを持つ」方が楽なのでは、と考えています。

vectorに詰め込んだオブジェクト解放するための便利関数

久しぶりにテンプレート関数を使おうとしたときのいくつかの間違いを覚書として書いておきます。

Utilityクラスなるものを用意し、次のような静的メソッドを宣言、定義しました。

Utility.h

template<typename T>
static void Utility::releaser(std::vector<T*>& v);

Utility.cpp

template<typename T>
void Utility::releaser(std::vector<T*>& v)
{
  std::vector<T*>::iterator it;
  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
}

これをコンパイルすると、

.cpp: 静的メンバ関数 ‘static void Utility::releaser(std::vector<T*>&)’ 内:
.cpp:???:???: エラー: need ‘typename’ before ‘std::vector<T*>::iterator’ because ‘std::vector<T*>’ is a dependent scope
.cpp:???:???: エラー: expected ‘;’ before ‘it’
.cpp:???:???: エラー: ‘it’ was not declared in this scope

といったエラーがでます。

because ‘std::vector’ is a dependent scope
はstd::vectorという部分がTに依存するため、型なのか変数名なのか、何なのか分からない、ということ言っています。
(VC++の場合は、問題なくコンパイルが通るらしいです。)

need ‘typename’ before ‘std::vector::iterator’
型だと分かるように「typename」をつけてください、と言っています。
この時点でこのコンパイラ自身は型だとわかっているようですが、どんなコンパイラでも通るように厳しく戒めてくれている、という理解です。

Utility.cpp

template<typename T>
void Utility::releaser(std::vector<T*>& v)
{
  typename std::vector<T*>::iterator it;

  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
}

言われた通りにつけてみましたが、今度は、

Hoge.o: In function `hogehoge(void)':
Hoge.cpp:???: undefined reference to `void Utility::releaser<Hoge>(std::vector<Hoge*, std::allocator<Hoge*> >&)'

と怒られてしまいました。
これは実際にHoge.cppでUtility::releaserを使用している個所でその実体が何か分からない、ということを言っています。
Utility.cppにおいて、クラスHogeを考慮したテンプレート関数の実体化を行っていない、かつ、Hoge.cppでも実体化を行っていないためです。
そこで、Hoge.cppで実体化が行われるように、Utility.h、つまりヘッダにテンプレートの実装を書きます。
Utility.cppの実装は消去し、

Utility.h

template<typename T>
static void Utility::releaser(std::vector<T*>& v)
{
  typename std::vector<T*>::iterator it;

  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
};

これで、テンプレート関数が使えます。

どうしても定義と実装を分けたい場合は、
明示的実装というものがあり、分けることもできるそうです。
予めテンプレートの使用パターンを宣言しておくというやり方です。
また、exportという記述子があり、定義と実装を分け、予めのパターン列挙も必要ないそうです。
ただし、こちらは対応コンパイラが少ないそうです。

このあたりのお話は、こちらが参考になりました。
http://d.hatena.ne.jp/pknight/20090826/1251303641
http://d.hatena.ne.jp/pknight/20090826/1251307608