a4_d0005: a_DbgObjInf の使い方

  • (by K, 2026.04.15)

(0)

  • これは主に自分用のメモです。

(1)

  • クラスを作るとき、一番最後に a_DbgLv1( a_DbgObjInf doi[1]; ) を入れます。
  • 例:
    a_class(a_Preprocessor_SourceFiles) {
        a_VecChr buf[1], stk[1];
        char *s;
        a_DbgLv1( a_DbgObjInf doi[1]; ) // ← ココ!
    };
    
    // [註] a_DbgLv1(...) は a_DbgLv >= 1 のときだけ有効になるマクロです。#if (a_DbgLv >= 1) ~ #endif だと3行になりますが、これなら1行で書けます。
  • [Q] なぜ一番最後にするのですか?
  • [A] これはデバッグモードの時だけ存在するメンバ変数で、リリースモードの時は消えてしまいます。もしこれが一番最後以外の場所にあると、他のメンバ変数のオフセットがデバッグモードとリリースモードとで変化することになり気持ち悪いからです。
  • [Q] 以下はみんな a_DbgLv >= 2 が基準なのに、ここだけ a_DbgLv >= 1 が基準なのはなぜですか?
  • [A] 「デバッグレベル2以上」がacl4におけるデバッグモードなので、以下が2以上を判断の基準にしているのは正しいのです。
  • デバッグレベル1は、クラスのサイズだけデバッグモードと同じだけど、そのほかはリリースモードと同じという特殊なモードです。これはデバッグモードにするとクラスのsizeofが変わってしまうことがあって、もしかしたらそれが引き金で「デバッグモードではうまくいくのに、リリースモードではうまくいかない」という現象があり得るかもしれなくて、その調査用に設定されているデバッグレベルなのです。

(2)

  • init関数の一番最後で a_DbgObjInf_ini() を呼び出します。
  • 例:
    a_static void a_Preprocessor_SourceFiles_ini(_aDef_ a_Preprocessor_SourceFiles *w)
    {
        a_VecChr_ini4(_a_ w->buf, w->stk, 0, 0);
        a_VecChr_reserve0(w->buf);
        w->s = w->buf->p;
        a_DbgLv2( a_DbgObjInf_ini(_aThr, w->doi, "Preprocessor_SourceFiles"); ) // ← ココ!
    }
    
     // [註] a_DbgLv2(...) は a_DbgLv >= 1 のときだけ有効になるマクロです。
  • [Q] なぜ一番最後にするのですか?
  • [A] 途中でinitに失敗するかもしれないですよね。そういう時は a_DbgObjInf_ini() を呼ぶべきではないです(これはdeinit忘れに気づくための仕組みなので、最後に呼べばそれでよいのです。)
  • そのほかの注意点:
    • (1) クラス内のメンバ変数(オブジェクト)をinitするときは、 _a_ を渡してください。メンバ変数がリークするかどうかはクラスライブラリの責任であって、ユーザプログラムの責任ではありません。だからクラスライブラリ内のソース行を指すべきです。
    • (2) a_DbgObjInf_ini() するときは _aThr を渡してください。このオブジェクトを deinit し忘れたのはユーザプログラムの責任だからです。
    • (3) もしこのクラスが何らかのクラスでしか使われないサブグラス的なものであるなら、 doi[1] を持たせる必要はありません。deinit 忘れを検出する必要がないからです。失敗する心配がないものをまじめにチェックするのはただの無駄です。ただしクラスの開発途中には有効かもしれないので、その時はあってもいいです。
  • 備考欄:
    • _a_ は _arg_ の略で、 __FILE__, __LINE__, のこと。ただし a_DbgLv が2未満の時は _a_ は消える。
    • _aDef_ は _argDef_ の略で、 const char *a_fil, int a_lin, のこと。ただし a_DbgLv が2未満の時は消える。
    • _aThr は _argThrough の略で、 a_fil, a_lin のこと。ただし a_DbgLv が2未満の時は消える。

(3)

  • deinit関数の一番最初で a_DbgObjInf_din(_aThr, w->doi); を呼び出します。
  • 例:
    a_static void a_Preprocessor_SourceFiles_din(_aDef_ a_Preprocessor_SourceFiles *w)
    {
        a_DbgLv2( a_DbgObjInf_din(_aThr, w->doi); ) // ← ココ!
        intptr_t i, n = a_VecChr_N(w->stk) / (sizeof (a_Preprocessor_SourceFile *));
        a_Preprocessor_SourceFile **p = (a_Preprocessor_SourceFile **) w->stk->p;
        for (i = 0; i < n; i++) {
            a_Preprocessor_SourceFile_din(p[i]);
            a_free(_a_  p[i], sizeof (a_Preprocessor_SourceFile));
        }
        a_VecChr_din4(_a_ w->buf, w->stk, 0, 0);
    }
  • [Q] なぜ一番最初にするのですか?
  • [A] deinit忘れを検出するための仕組みなのですから、忘れずに呼んでくれた時点で、さっさとフラグを下げるべきです。いつまでも残っていると紛らわしいです
  • [Q] deinit関数が _aDef_ 情報を取るのはなぜですか? いや、それはおそらく、 a_DbgObjInf_din() が _aThr を要求するからだと思いますが、ではなぜ a_DbgObjInf_din() はユーザプログラム内の行情報を必要とするのですか?
  • [A] ごくまれに、initしていないオブジェクトのdeinitをしてしまうバグを作ってしまうかもしれません。その時に、問題の記述がどこにあるのかをエラー報告するためです。
  • 一番最初に呼べば、対応するinitがないdeinitをした場合でも、未初期化ポインタを使った解放処理みたいな悪夢が始まる前にエラー終了させることができます。

(4)

  • [Q] a_DbgObjInf_ini / a_DbgObjInf_din はどんな機能を提供してくれますか?
  • [A] 以下の3つです。
    • オブジェクトを init したのに deinit してない(=リーク)の検出。
    • オブジェクトを init してないのに deinit してしまった(もしくは二重 deinit)の検出。
    • オブジェクトを init して、さらにもう一度 init してしまった、の検出。
  • [Q] 少し改造すれば、リークしたオブジェクトを自動で全部 deinit するとかもできそうですよね?
  • [A] それは多分できるでしょうが、しかしやるべきではないと考えます。まず、この機構はデバッグモードでしか機能しないように作ってあり、オブジェクトを全部片づけるといった「リリースモードでも十分に必要な機能」との相性がよくないです。そういうことがやりたければ BufFree 的なものを作って、それでやるべきでしょう(これはデバッグモードでもリリースモードでも同じように動きます)。

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2026-04-16 (木) 08:23:22 (61d)