近況

最近のこと

2009-12-06

C++BuilderでもDelphiのインターフェイスを使ってクラスを作成できます。ヘルプには詳しい解説が載っていないので、調査してなんとか実装できました。まだ不明な部分が多いのですが、結果はC++Builder Tipsのページにまとめてあります。

インターフェイスを使うクラスは、TInterfacedObjectの派生クラスでなければなりません。また、TObjectは単一継承しかサポートしません。それゆえ、VCLに含まれる多くのクラスに後からインターフェイスを持たせることはできません。

やや最近のこと

2009-12-01

ファイルとディレクトリに共通する抽象概念は何か。ファイルクラスとディレクトリクラスを作るとすれば、それらをどんな名前のクラスから派生させれば良いのか。適切な名前を求めて調べました。

.NETフレームワーク(バージョン4)において、FileInfoクラスとDirectoryInfoクラスは、FileSystemInfoクラスから派生しています。逆算してみると…"ファイルはファイルシステムである"も"ディレクトリはファイルシステムである"も、ちょっと合っていない気がします。

Java(バージョン6)において、Fileクラスは、Objectクラスから派生しています。このFileクラスは、ファイルとディレクトリの機能を併せ持っています。

Qt(バージョン4.6)において、FileクラスとDirectoryクラスは、どちらも派生元クラスを持ちません。

BeOSのAPIにおいて、BFileクラスとBDirectoryクラスは、BEntryクラスから派生しています。"Entry"という汎用的な言葉をファイル用語として限定してしまうとは、大胆です。

そもそもディレクトリとは何か。ファイルシステムの実装レベルで見ると、ディレクトリは、ディレクトリ属性が付けられたファイルに過ぎないようです。つまり、ディレクトリもファイルだということになります。それゆえ、ファイルとディレクトリに共通する抽象概念は存在しないことになります。無い物に名前は付けられません。

しかし、この仕組みは話をややこしくします。"ファイル"という用語が二種類の意味を持つことになるからです。一つは、ファイルシステムのエントリという意味でのファイル。もう一つは、ディレクトリでは無いエントリという意味でのファイル。例えば、FileExistsという関数は、名前からだけでは機能を特定できません。

2009-11-07

ヘルプによるとTThreadのOnTerminateイベントは、処理がExecuteから戻った後、スレッドが廃棄される前に発生します。つまり、Executeはサブスレッドが本処理を行う場所、OnTerminateハンドラはメインスレッドが後処理をする場所として割り当てられていると見ることができます。

後処理に属する以上、OnTerminateハンドラでは例外が起きる可能性のある処理を行うべきではありません。例えば、スレッドから計算結果を受け取る手段としてOnTerminateイベントを利用したくなるかもしれません。しかし、後処理以外の目的であれば、専用のイベントを用意し、本処理にあたるExecuteでハンドラを呼ぶ方がTThreadの仕様に適っています。

2009-11-03

"Milestones 2000"をちょくちょくプレイしています。1990年にKen Franklin氏によって製作された、Apple IIGSで動作する、一人用カードゲームです。プレイヤーはコンピュータを相手に1500Kmの自動車レースをします。カードには、自分の車を進める、相手の車を妨害する、妨害から回復する、特殊な権利を与える、といったものが用意されています。各レースの内容は得点に換算され、先に5000点に達した方が最終的な勝利者となります。グラフィックとサウンドが愉快で、プレイをより楽しいものにしています。

"Milestones 2000"は、"What is Apple IIGS?"というサイトに置かれている、エミュレータ用ハードディスクイメージに入っています。プログラムファイルは、"GSHardDrive:Shareware.Games:Ken.Franklin:Milestones2000"というフォルダに入っています。

2009-10-30

"Indy Coding Conventions and Standards"の"Source Code Standards"では、変数名に次のような接頭辞を付けることになっています。
  • A - 値渡しの引数
  • V - 参照渡しの引数(var指定が付けられたもの)
  • F - メンバ変数
  • G - グローバル変数
  • L - ローカル変数(汎用的に使われる整数(i、j、kなど)と文字列(s、t、uなど)は除く)
ここで興味深いのは"L"です。一般的に、ローカル変数の名前は小文字で始めるという規約が広く用いられています。しかし、それでは変数名と言語の予約語が衝突してしまう可能性があります。例えば、C++においてコーダーが変数名を"int"にしたいと思っても、予約語の"int"と衝突しています。このような場合は、"intValue"などの別の変数名を用いるしかありません。

ローカル変数の名前に"L"を付けることになっていれば、"int"は"LInt"と表記します。こちらの方が、"intValue"よりも、コーダーの意向に近い表現になっていると言えます。("int"という変数名の是非はさておき。)

具体例を挙げます。

ローカル変数の名前に"L"を付けない場合:

int __fastcall HexCharToInt(char AHexChar)
{
    int intValue = HexCharToIntTable[static_cast<unsigned char>(AHexChar)];
    if(intValue < 0) {
        throw EConvertError("The program cannot convert the hexadecimal character to an integer.");
    }
    return intValue;
}

ローカル変数の名前に"L"を付ける場合:

int __fastcall HexCharToInt(char AHexChar)
{
    int LInt = HexCharToIntTable[static_cast<unsigned char>(AHexChar)];
    if(LInt < 0) {
        throw EConvertError("The program cannot convert the hexadecimal character to an integer.");
    }
    return LInt;
}

接頭辞の"L"を導入することに関する問題としては、シフトキーを押す回数が増えてタイプしづらい、一般的な表記ではないので読み書きに慣れが必要、といったものが考えられます。

2009-10-05

enumの変数名は悩ましい。enum class相当の機能を利用できない状況では、識別子の衝突を回避する工夫が必要になります。VCLでは短い(二三文字程度)接頭辞を変数名に付けるのが慣習になっています。この方法の長所は、オブジェクトインスペクタの狭いテキストエリア内でも値の入力・確認がしやすいこと。短所は、接頭辞の重複が起きる可能性があること。

2009-10-03

.NET FrameworkのSystem.IO.File.Deleteは、削除の対象となるファイルが存在しない場合には例外を送出しません。一方、Windows APIのDeleteFileは、同様の場合に関数は失敗となります(VCLのDeleteFileも同じ)。

一連の処理の後始末という文脈でファイルの削除を行う時は、前者の仕様の方が好都合です。

後者を用いて前者と同じ機能を実現すると、次のようになります。

bool __fastcall EnsureDeleteFile(const AnsiString& AFileName)
{
    return
        ::DeleteFile(AFileName.c_str()) ||
        ::GetFileAttributes(AFileName.c_str() == 0xFFFFFFFF);
}

2009-10-02

C++Builder 6でテストしてみると、静的変数が初期化されるタイミングは次の通りです。

WinMainより前に初期化される

  • ファイルスコープの静的変数
  • クラスの静的変数
WinMainの後に初期化される
  • 関数内の静的変数 (関数が初めて呼ばれた時に初期化される)
複数のスレッドによる呼び出しを前提とする関数において、内部に静的変数を置くことには、安全に変数を初期化するための用意が必要です。

2009-10-01

ロットロットも面白い…と、手元の作業がなかなか進みません。

2009-09-24

ここのところ、セガのテトリスをMAMEでプレイしていました。Rogueと同様、ちょっとしたミスがゲームオーバーにつながります。何とかレベル99に到達するようになったので、熱中するのはこの辺までにしたいと思います。

2009-09-21

天才てれびくんMTKクラシックで流れたのは"ココロ磁石"でした。あの頃の私は鼻声だった、とは数年後の彼女(杏)自身の回想。

2009-09-16

特定のコントロールのみにファイルのドロップを許可するには。一つの方法として、コントロールのWindowProcプロパティの値を自前のメソッドに置き換えるというものがあります。この自前のメソッドの中でWM_DROPFILESメッセージをトラップするというわけです。

TApplicationのOnMessageイベントでWM_DROPFILESメッセージをトラップする方法は、複数のウィンドウにおいて同イベントを処理したい場合に実装が煩雑になります。

フックを使う方法もあります。しかし、一個のウィンドウクラスに複数のインスタンスの生成を許可する場合には、フックハンドルをウィンドウインスタンスと結びつけて管理する必要があります。フックプロシージャ内では動的にフックハンドルを取得することになり、アプリケーション全体の処理速度が低下してしまいます。

2009-09-15

リストビューの項目の選択状態に応じて、関連するコントロールの使用可・不可を切り替える処理があります。では、どのタイミングで関連するコントロールの状態を更新すればよいのか。

TListViewのOnChangeイベントは、リストビューの項目が選択・選択解除される度に発生します。しかし、このイベントのハンドラで関連するコントロールの状態を更新することには問題があります。

リストビューの項目をクリックして選択が一個ずつ切り替わる時、OnChangeイベントは続けて二回発生します。それぞれの発生時において項目の選択数は0と1を示すため、関連するコントロールの状態が使用不可から使用可へと瞬間的に切り替わります。つまり、描画のちらつきが起きてしまいます。

そこで、解決法として、OnChangeイベントとWM_IDLEメッセージを組み合わせる方法があります。OnChangeイベントハンドラでは、リストビューの状態に変更があったことを記録する内部フラグをオンにします。そして、WM_IDLEメッセージハンドラでは、内部フラグがオンになっていたら、関連するコントロールの状態を更新し、内部フラグをオフにします。これで関連するコントロールの状態の更新を一括して行うことができます。

WM_IDLEメッセージをトラップする手段には、MESSAGE_HANDLERマクロ、TApplicationのOnIdleイベント、TActionListのOnUpdateイベント、といったものがあります。

2009-09-10

Classic Computer Magazine Archiveでは、コンピュータ関連の古い(1970年代中頃から1990年代初頭まで)雑誌がデジタル化されて閲覧できるようになっています。個人的にはNEC Trekの記事があってうれしい。

2009-09-05

Opera 10を使ってみました。エフェクトに注力が偏っています。エフェクト以外の部分は所々で大雑把です。空白ページを開始時に表示できない、設定がPreferenceとAppearanceに分散していてアクセスしづらい、ページブロックはパーツ毎に指定できない、スピードダイアルを外すことができない、など。

Firefox自体は遅くても、PrefbarとAdblock Plusは便利です。これがFirefoxを使う理由の一つになっています。

しかし、OperaもFirefoxも、初回起動時に各自のサイトが表示されるようになっています。これは陰謀のようなものです。

2009-07-11

OWLは現在CodeGearからソースとバイナリがダウンロードできるようになっています。BorlandがC++で書くとこうなるという資料として見ることもできます。

2009-07-10

K-Meleonは不具合が多い…。再びFirefoxを使っています。

アーカイブ

戻る