delphi memo

home
更新日:2006/3/28

delphi pascal

型可変オープン配列パラメータ

array of constパラメータ。TVarRecの配列に変換される。
C言語のprintfのような、型がバラバラで可変数の引数を渡すことが出来る。Format関数などが使用。大量のoverload関数作るよりはこれかVariantを使ったほうが楽。文字列やExtendedなんかはポインタで受け取るので注意。
DeCALはこれを使ってジェネリックなコンテナを実現している。
単純な使用例

procedure VType(V : array of const);
var
  i: Integer;
begin
  for i :=0 to High(V) do begin
    case V[i].VType of
      vtInteger    : ShowMessage('整数型: '                + IntToStr(V[i].VInteger));
      vtBoolean    : ShowMessage('真偽型: '                + BoolToStr(V[i].VBoolean));
      vtChar       : ShowMessage('文字型: '                + V[i].VChar);
      vtExtended   : ShowMessage('浮動小数点型: '          + FloatToStr(V[i].vExtended^));
      vtString     : ShowMessage('文字列型: '              + V[i].VString^);
      vtPointer    : ShowMessage('ポインタ型: '            + Format('%p', [V[i].VPointer]));
      vtPChar      : ShowMessage('文字ポインタ型: '        + V[i].VPChar);
      vtObject     : ShowMessage('オブジェクト型: '        + V[i].VObject.ClassName);
      vtClass      : ShowMessage('クラス参照型: '          + V[i].VClass.ClassName);
      vtWideChar   : ShowMessage('UNICODE文字型: '         + V[i].VWideChar);
      vtPWideChar  : ShowMessage('UNICODE文字ポインタ型: ' + WideChar(V[i].VPWideChar));
      vtAnsiString : ShowMessage('長い文字列型:'           + string(V[i].vAnsiString));
      vtCurrency   : ShowMessage('通貨型: '                + CurrToStr(V[i].VCurrency^));
      vtVariant    : ShowMessage('バリアント型: '          + string(V[i].VVariant^));
      vtInterface  : ShowMessage('インターフェース型');
      vtWidestring : ShowMessage('UNICODE文字列型: '       + WideString(V[i].VPWideChar));
      vtInt64      : ShowMessage('64Bit整数型: '           + IntToStr(V[i].VInt64^));
    end;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  SStr: Shortstring;
  AStr: AnsiString;
  WChar: WideChar;
  WStr: WideString;
  C: Currency;
  I64: Int64;
  V: Variant;
  I: IUnknown;
begin
  SStr := 'shortstring';
  AStr := 'ansistring';
  C := 56.78;
  V := 'Variant';
  I64 := High(Int64);
  WChar := 'U';
  WStr  := 'Unicode';
  VType([High(Integer),false, 'A', 12.34, SStr, @SStr, PChar(Astr), Form1,
        TForm1, WChar, @WChar, AStr, C, V, I, WStr, I64 ]);
end;

静的配列参照プロパティ

こういうことも出来るよ程度。

  THoge = class
  private
    FStringArray: array[1..3] of string;
  public
    property Str1: string read FStringArray[1] write FStringArray[1];
    property Str2: string read FStringArray[2] write FStringArray[1];
    property Str3: string read FStringArray[3] write FStringArray[1];
  end;

TScopeCursor

MFCのCWaitCursorっぽいやつ。変数宣言はインターフェースにすること。

type
  IScopeCursor=interface
  ['{72F9BF87-DE15-47F7-8AA5-3B5FCE699447}']
    procedure Restore;
  end;

  TScopeCursor= class(TInterfacedObject,IScopeCursor)
  private
    FDefaultCursol: TCursor;
    FHasRestored: Boolean;
  public
    constructor Create(ChangeCursor: TCursor = crHourGlass; DefaultCursor: TCursor = crDefault);
    destructor Destroy; override;
    procedure Restore;
  end;
implementation
uses Forms;
{ TScopeCursor }
constructor TScopeCursor.Create(ChangeCursor, DefaultCursor: TCursor);
begin
  inherited Create;
  FHasRestored := False;
  FDefaultCursol := DefaultCursor;
  Screen.Cursor := ChangeCursor;
end;

destructor TScopeCursor.Destroy;
begin
  if (not FHasRestored) and (Screen.Cursor <> FDefaultCursol) then
    Screen.Cursor := FDefaultCursol;
  inherited;
end;

procedure TScopeCursor.Restore;
begin
  if Screen.Cursor <> FDefaultCursol then
    Screen.Cursor := FDefaultCursol;
  FHasRestored := True;
end;

//使用例
procedure TForm1.Button1Click(Sender: TObject);
var
  cur : IScopeCursor; //クラス型では無い
begin
  Cur := TScopeCursor.Create;
  Sleep(1000);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  cur : IScopeCursor;
begin
  Cur := TScopeCursor.Create(crHSplit, crCross);
  Sleep(1000);
end;

集合型

C言語でBIT演算でフラグ管理するのと同じことが出来る。
ステータス管理なんかに便利。
色々演算できるが忘れがちなので一通りの操作サンプル。

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, TypInfo;

type
  Flag = (BIT1, BIT2, BIT3,BIT4,BIT5, BIT6, BIT7, BIT8);
  Flags = set of Flag;  //要素数8の集合は符号なし8 ビットとして扱われる

procedure WriteFlag(F: Flags);
var
  i: Integer;
begin
  Write('[ ');
  for i := 0 to 7 do begin
    if (Byte(F) and (1 shl i)) <> 0 then
      Write(GetEnumName(TypeInfo(Flag), i) + ' ');
  end;
  Write(']');
end;

procedure WriteBool(B: Boolean);
begin
  WriteLn(BoolToStr(B, True));
end;

var
  F1,F2: Flags;
  B: byte;
begin
  WriteLn('代入');
  F1 := [BIT1];      //F1 = [BIT1]
  Write('  F1 := [BIT1] = ');
  WriteFlag(F1);
  WriteLn;

  F1 := [BIT1,BIT3]; //F1 = [BIT1,BIT3]
  Write('  F1 := [BIT1,BIT3] = ');
  WriteFlag(F1);
  WriteLn;

  WriteLn('集合操作ルーチン');
  Include(F1,BIT4);  //F1 := F1 + BIT4
  Write('  Include(F1,BIT4) = ');
  WriteFlag(F1);
  WriteLn;

  Exclude(F1,BIT4);  //F1 := F1 - BIT4
  Write('  Exclude(F1,BIT4) = ');
  WriteFlag(F1);
  WriteLn;

  WriteLn('和集合');
  F1 := [BIT1, BIT2] + [BIT2, BIT3];  //F1 = [BIT1,BIT2,BIT3](AとBの全要素)
  Write('  [BIT1, BIT2] + [BIT2, BIT3] = ');
  WriteFlag(F1);
  WriteLn;

  WriteLn('差集合');
  F1 := [BIT1, BIT2] - [BIT2, BIT3];  //F1 = [BIT1](AにあってBにない要素)
  Write('  [BIT1, BIT2] - [BIT2, BIT3] = ');
  WriteFlag(F1);
  WriteLn;

  WriteLn('積集合');
  F1 := [BIT1, BIT2] * [BIT2, BIT3];  //F1 = [BIT2](AとBともに含まれる要素)
  Write('  [BIT1, BIT2] * [BIT2, BIT3] = ');
  WriteFlag(F1);
  WriteLn;

  WriteLn('キャスト');
  //要素8個までならByte型へキャスト可能。16個ならWord、32個ならLongword
  Byte(F1) := $FF;
  Write('  Byte(F1) := $FF = ');
  WriteFlag(F1);
  WriteLn;

  B := 0;
  F1 := Flags(B); //F1 =[] (直に書くとキャストできない)
  Write('  F1 := Flags(Byte(0)) = ');
  WriteFlag(F1);
  WriteLn;

  WriteLn('in');
  F1 := [BIT1, BIT2];
  Write('  BIT1 in ');
  WriteFlag(F1);
  Write(' = ');
  WriteBool(BIT1 in F1);

  F1 := [BIT1,BIT2,BIT3];
  F2 := [BIT1,BIT2,BIT3];

  WriteLn('比較');
  Write('  ');
  WriteFlag(F1);
  Write(' = ');
  WriteFlag(F2);

  Write(' = ');
  WriteBool(F1 = F2); //F1とF2は一致

  Write('  ');
  WriteFlag(F1);
  Write(' <> ');
  WriteFlag(F2);
  Write(' = ');
  WriteBool(F1 <> F2);

  F1 := [BIT1, BIT2,BIT3];
  F2 := [BIT2, BIT3];

  Write('  ');
  WriteFlag(F1);
  Write(' >= ');
  WriteFlag(F2);
  Write(' = ');
  WriteBool(F1 >= F2);  //F2はF1に含まれていると真

  Write('  ');
  WriteFlag(F1);
  Write(' <= ');
  WriteFlag(F2);
  Write(' = ');
  WriteBool(F1 <= F2);  //F1はF2に含まれていると真

  if DebugHook <> 0 then ReadLn;
end.

バグ

ClientDatasetを右クリックして「項目の作成」を選択して、コレクションエディタで「項目の新規作成」を選択すると、

モジュール 'rtl70.bpl' のアドレス 400056FC でアドレス 4095755C に対する読み込み違反がおきました。.

と出ることがある。
これはdelphiのバグで、登録したパッケージやエキスパートなんかのImage baseが$00400000の時に起こるらしい。
詳細 (borland newsgroup)
ソースがあるパッケージならプロジェクトオプションのイメージベースを他のアドレスに変更して再構築すれば回避できるが、bplしか提供されてない商用のパッケージなどはコンポーネント|パッケージのインストールで該当パッケージのチェックを外してdelphiの再起動が必要になる。
tdumpでbplファイルをダンプ出来るので、 $(DELPHI)\Projects\Bpl やパッケージフォルダのbplを一通り調べてImage baseが$00400000のものは構築しなおしたほうがが無難。
Image baseは$00400000から変更すべきなので、パッケージを作成する時は忘れないように。DLLを作る時も変えておく。exeのときはかまわない。

コンパイラ指令

Helpからは短い名前で引けなくて分かりづらいので対応表。

{$A}
{$Align} アラインメント。packed使ったほうが分かりやすい
{$B}
{$BoolEval} ショートサーキット。プロジェクトオプションで設定すべき。$Bに依存するif文は書かないこと
{$C}
{$Assertion} Assert。プロジェクトオプションで設定すべき。Assertはエラーチェックには使わない
{$D}
{$DebugInfo} デバッグ情報。オフにする意味はあまり無い。
{$D 文字列}
{$Description}
{$E}
{$EXTENSION} 生成されるファイルの拡張子
{$G}
{$IMPORTEDDATA} 別ユニットの大域変数参照。ほぼ意味が無い。
{$H}
{$LONGSTRINGS} 普通はShortStringを使う
{$I}
{$IOCHECKS} 普通はON
{$I 文字列}
{$INCLUDE}
{$IFOPT}
{$IF}は{$DEFINE}定義の判定だが{$IFOPT}はコンパイラ条件を調べる。 {$IFOPT I+} if IOResult =0 then raise Error; {$ENDIF}
{$J}
{$WRITEABLECONST} 型付定数への代入。 デフォルトはOFFになっているが、関数スコープでのStatic変数の代替手段が無いためONにせざるを得ない場合が多い。
{$L}
{$LOCALSYMBOLS} デバッグに必要。普通はON
{$L 文字列}
{$LINK}
{$M}
{$TYPEINFO} RTTIに必要なので常にON
{$M minstacksize,maxstacksize}
{$MINSTACKSIZE},{$MAXSTACKSIZE}
{$O}
{$OPTIMIZATION} 最適化。プロジェクトオプションで設定すべき。
{$P}
{$OPENSTRINGS} {$LONGSTRINGS OFF}の状態でのみ有効
{$Q}
{$OVERFLOWCHECKS} オーバーフローチェック。ONにしておいて損は無い。遅くなるならそこだけOFFにする。
{$R}
{$RANGECHECKS} 範囲チェック。常にONでもいい。わざと範囲外アクセスするソースもあるがその時はそこだけOFFにする。
{$R 文字列}
{$RESOURCE}
{$U}
{$SAFEDIVIDE} 初期Pentiumの浮動小数点演算バグに対応。今は要らない。
{$T}
{$TYPEDADDRESS} 型付@演算子。Addr関数は{$T}に影響されない。
{$V}
{$VARSTRINGCHECKS} 長さの違うShortstringを引数に出来るか制御する
{$W}
{$STACKFRAMES} MemChekなどでデバッグする時にはON
{$Y}, {$YD}
{$DEFINITIONINFO}, {$REFERENCEINFO} シンボル情報。ブラウザを良く使うなら{Y+}(宣言、参照先シンボルを保存)にすると、参照先がつかえるようになり便利
{$X}
{$EXTENDEDSYNTAX} 拡張構文。オフにすることはほとんど無い
{$Z}
{$MINENUMSIZE}

ライブラリ

コンテナ

標準のコンテナはListかHashしかないので DeCALを使うとSetやMap、C++STLのようなアルゴリズムなんかも使えるようになる。templateではないので癖が多いが付属のpdfが詳しい。
コンテナはMap,MultiMap,Set,MultiSet,HaseSet,MultiHashSet,MultiHashMap,List,Arrayが使え、Iteratorもある。
付いてくるSuperStreamを使うとObjectのシリアライズが便利らしい。
επιστημηさんのSTL samplesを移植してみるとこんなかんじになる。

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, DeCAL;

procedure ToString(const Obj: DContainer);
var
  Iter: DIterator;
begin
  Write(' [');
  Iter := Obj.start;
  while not atEnd(Iter) do begin
    Write(IntToStr(GetInteger(Iter)));
    Advance(Iter);
    if not atEnd(Iter) then
      Write(' ');
  end;
  WriteLn('] ');
end;

function Quad(Ptr : Pointer; const Obj : DObject) : DObject;
begin
  Result := Make([asInteger(Obj) * 4]);
end;

function PlusInt(Ptr : Pointer; const obj1, obj2 : DObject) : DObject;
begin
  Result := Make([asInteger(Obj1) + asInteger(Obj2)]);
end;

var
  X: DArray;
  Y: DArray;
  Z: DArray;
begin
  X := nil;
  Y := nil;
  Z := nil;
  try
    X := DArray.create;
    Y := DArray.create;
    Z := DArray.create;

    X.Add([0, 1, 2, 3, 4]);  //X.PushBack([0, 1, 2, 3, 4])とも書けるのはstd::vectorよりちょっとうれしい

    Write('x[]                : ');
    ToString(X);
    TransformUnary(X, Y, MakeUnary(Quad));

    Write('y[n] = x[n] * 4    : ');
    ToString(Y);
    TransformBinary(X, Y, Z, MakeBinary(PlusInt));

    Write('z[n] = x[n] + y[n] : ');
    ToString(Z);
  finally
    FreeAll([X, Y, Z]);
  end;

  if DebugHook <> 0 then ReadLn;
end.

*nextPermutation、prevPermutation、mergeSortは未実装。

JEDI

あまり使っていないが、JCLの方のdebugライブラリのStack系は例外のとき便利。
インストールしたら新規作成->ダイアログに出来るException Dialogは未処理例外が起きると勝手に補足してくれてスタックフレームや動作環境なんかをレポートしてくれるのでとても便利。日本語表示するなら使う前にFontを変更。

synapse

配布元
オープンソースのNETWORKライブラリ。
Indyに比べるとシンプルで見通しがいい。 コンポーネントではなくライブラリなのでインストールが楽。

//メール受信サンプル
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, pop3send;

var
  Pop: TPOP3Send;
  i: Integer;
begin
  Pop := TPOP3Send.Create;
  Pop.UserName := 'username';
  Pop.Password := 'password';
  Pop.TargetHost := '127.0.0.1';
  Pop.TargetPort := '110';
  Pop.Login;  //何も指定しないとAPOPを試して駄目ならLOGINコマンドを使用する
  WriteLn('LOGIN: '+ Pop.ResultString);
  Pop.Stat;
  WriteLn('STAT: ' + Pop.ResultString);
  for i := 1 to Pop.StatCount do begin
    Pop.RETR(i);
    WriteLn('RETR: ' + Pop.FullResult.Text); //タイトルなどはデコードが必要
  end;  

  Pop.Logout;
  Pop.Free;
  if DebugHook <> 0 then ReadLn;
end.

その他

unit test

DUnitWizardを入れると雛型を作ってくれる。うちの環境だと時々エラーで落ちることがある。

gexpert

とりあえずデザインフォーム右クリックで出てくる「Copy Component Name」とエディタのツールバーカスタマイズ(ここにコンパイルとかユニット表示、フォーム表示、フォームユニット切り替えなんかを入れると、とても楽)、TODOListのためにだけでも使う価値がある。IDE Menu Shorcut、Clean Directoriesも便利。

pascal doc

配布元
ドキュメント生成ツール。基本的にJavaDocに近い。
フォームフォントなどの日本語化作業が必要になるがソースは付いてるので難しくない。
モジュールエクスプローラーを使いながら未記入のコメントを埋めていくのは楽。
パラメータや戻り値の書き方に癖がある。というか@param hoge as a THogeなんて書かないとコンフリクト扱い ←コメント自動生成すると勝手に書いてくれた

csv reader

自作CSV読み込みクラスfile
一度に全部読み込むので速度そこそこ、メモリは食う
ファイルサイズ2Gまでは読めるかもしれない
最大フィールド数は134217728程度?
パブリックドメインです。無保証、無サポートを承知の上使用してください。

  使い方
    LoadFromFileで読み込んでRow[Index]でstringの動的配列を取得できる
    csv := TCSV.Create;
    try
      csv.LoadFromFile('hoge.csv');
      for i := 0 to csv.Count -1 do
        for j := 0 to Length(csv.Rows[i]) -1 do
          s := s + csv.Rows[i][j];
    finally
      csv.Free;
    end;

FastMM4

FastMM4ベンチ
文字列結合がノーマルMMより遅いのは意外。結果は環境で変わるのであまり信用しないで自分で試すことをお勧め。