Difference between revisions of "Streaming components/ja"

From Free Pascal wiki
Jump to navigationJump to search
m (Reverted edits by Lm2Gzv (Lm2Gzv); changed back to last version by Saeka-jp)
 
(12 intermediate revisions by 2 users not shown)
Line 26: Line 26:
 
== TReader / TWriter ==
 
== TReader / TWriter ==
  
These are the worker classes, which reads/writes a TComponent to/from a stream (See CreateLRSReader and CreateLRSWriter).
+
TReaderとTWriterは、ストリームにたいしてTComponentを読み書きするためのワーカークラスです。(訳注:ストリームクラスにたいして補助的に便利な関数の集まりになっています。)
They use a '''Driver''' to read/write a special format. At the moment there are a reader (TLRSObjectReader) and a writer (TLRSObjectWriter) for binary object format defined in the LResources unit and a writer (TXMLObjectWriter) for TDOMDocument defined in Laz_XMLStreaming.
+
CreateLRSReaderとCreateLRSWriterを見てください。
The LResources unit also contains functions to convert binary format to text and back (LRSObjectBinaryToText, LRSObjectTextToBinary). The LCL prefers UTF8 for strings, while Delphi prefers Widestrings. So there are some conversion functions as well.
+
これらは、特別なフォーマットを読み書きする場合'''Driver'''を使います。
 +
このとき、LResourcesユニットで定義されるバイナリオブジェクトフォーマットのためのreader(TLRSObjectReader)とwriter(TLRSObjectWriter)があり、Laz_XMLStreamingで定義されるTDOMDocumentのためのwriter (TXMLObjectWriter)があります。
 +
 
 +
LResourcesユニットは、バイナリフォーマットからテキストに変換する関数とその逆の関数を含みます。
 +
(LRSObjectBinaryToText, LRSObjectTextToBinary).
 +
DelphiではWidestringでしたが、LCLは文字列に対してはUTF8を好みます。
 +
そのための変換関数も同様にいくつかあります。
  
 
== 独自コンポーネントの書き方 - Part 1 ==
 
== 独自コンポーネントの書き方 - Part 1 ==
Line 42: Line 48:
 
   end;
 
   end;
  
== Writing a component to a stream ==
+
== ストリームにコンポーネントを書き出す ==
  
 
[[doc:lcl/lresources|LResources]] ユニットには次のような関数があります:
 
[[doc:lcl/lresources|LResources]] ユニットには次のような関数があります:
 
   procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);
 
   procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);
  
It writes a component in binary format to the stream.
+
これは、コンポーネントをバイナリフォーマットでストリームに書き出します。
For example:
+
 
 +
たとえば次のようにします:
 +
 
 
<pre>
 
<pre>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
Line 64: Line 72:
 
</pre>
 
</pre>
  
== Reading a component from a stream ==
+
(訳注:この例では、メモリ上にグループボックスのすべてのプロパティがバイナリ形式で保管される。
 +
 一般的には、バッファとして使う以外には、メモリ上に入れる例は少ない。ファイルストリームをつかって、それをファイルに保存して、後日読み出したり、ソケットストリームをつかって、ソケットで別のマシンに転送したりして、オブジェクトの状態を「保存」「転送」することができる。完全に保存するためには、オブジェクトの状態がすべてプロパティでpublishされていなくてはならないことに注意しよう。
 +
Lazarusでは、フォームのデザイン等(=つまりプロパティの設定値)を設計時(コンパイル時)にリソースに保存し、実行時にリソースからプロパティを復元して実行するメカニズムがある。
 +
他の言語では、一般的には、シリアライズと呼ばれることがある)
 +
 
 +
== ストリームからコンポーネントを読み出す ==
  
 
LResources には次のような関数があります:
 
LResources には次のような関数があります:
Line 70: Line 83:
 
     var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
 
     var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
  
* AStream is the stream containing a component in binary format.
+
* AStream はコンポーネントをバイナリフォーマットで含んでいるストリームを指定します。
* RootComponent is either an existing component, which data will be overwritten, or it is nil and a new component will be created.
+
* RootComponent は存在しているコンポーネントを指定した場合はそのプロパティは上書きされます。nilを指定した場合は、新しいコンポーネントがストリームから生成されます。
* OnFindComponentClass is a function, that is used by TReader to get the class from the classnames in the stream. For example:
+
* OnFindComponentClass はTReaderによって、ストリーム内のクラス名から、クラスを取得するのに使われる、イベント関数です。(訳注:クラス参照型を取得することで、TReaderは名前文字列からそのクラスのコンストラクタを呼ぶことができる)
 +
 
 +
たとえば、次のように、書くことができます:
 
<pre>
 
<pre>
 
procedure TCompStreamDemoForm.OnFindClass(Reader: TReader;
 
procedure TCompStreamDemoForm.OnFindClass(Reader: TReader;
Line 83: Line 98:
 
end;
 
end;
 
</pre>
 
</pre>
* コンポーネントを新規に生成した場合、OWner にはそのコンポーネントのオーナーとなります。
+
* コンポーネントを新規に生成した場合、Owner にはそのコンポーネントのオーナーとなります。
 +
 
 +
== ストリーム可能なプロパティについて  ==
  
== Streamable properties ==
+
TReaderやTWriterがストリームできるプロパティには、いくつかの条件があります。
  
There are some limitations, what types TReader/TWriter can stream:
+
* 基本型はストリームできます。すなわち、integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, などなど. .
 +
* TPersistent とその派生クラスはストリームできます。
  
* Base types can be streamed: string, integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, etc. .
+
* 構造体、object型、TPersistentから派生しないクラス型は、ストリームできません。
* TPersistent and descendants can be streamed
+
それらをストリームするには、TReader/TWriterにその方法を教える必要があります。次を見てください。[[#Streaming custom Data - DefineProperties]].
  
* records, objects and classes not descending from TPersistent can not be streamed. To stream them you need to tell TReader/TWriter how. See below [[#Streaming custom Data - DefineProperties]].
+
== カスタムデータをストリームする - DefineProperties ==
  
== Streaming custom Data - DefineProperties ==
+
DefinePropertiesをオーバーライドすることで、通常ではストリームできないデータをストリームすることができるようになります。この方法は、すべてのデータに対して、たとえ基本型をもたないものでも、ストリームすることを可能にします。たとえば、あなたが作ったコンポーネントの変数 '''FMyRect: TRect'''をストリームしたい場合、次のような3つのメソッドをコンポーネントに加えます。
  
You can stream additinal arbitrary data by overriding DefineProperties. This allows to stream all data, that have no base types. For example to stream a variable '''FMyRect: TRect''' of your component, add the following three methods to your component:
 
 
<pre>
 
<pre>
 
procedure DefineProperties(Filer: TFiler); override;
 
procedure DefineProperties(Filer: TFiler); override;
Line 103: Line 120:
 
</pre>
 
</pre>
  
続けて次のコードを記述します:
+
続けて次のように実装してください:
  
 
<pre>
 
<pre>
Line 144: Line 161:
  
 
This will save MyRect as a property 'MyRect'.
 
This will save MyRect as a property 'MyRect'.
 +
これで、MyRectをプロパティとして保存できます。
 +
しかし、もし、たくさんのTRectをストリームする必要がある時、このようなコードで何度も書きたくないでしょう。
 +
LResourcesユニットには、rectプロパティを定義する手続きを書く例があります。
  
If you stream a lot of TRect, then you probably do not want to write everytime ths code.
 
The unit LResources contains an example how to write a procedure to define a rect property:
 
 
   procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
 
   procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
 
    
 
    
上述の方法はこのように非常に短く記述することもできます:
+
さきほどの方法はこれを使うと非常に短く記述することができます:
 
<pre>
 
<pre>
 
procedure TMyComponent.DefineProperties(Filer: TFiler);
 
procedure TMyComponent.DefineProperties(Filer: TFiler);
Line 160: Line 178:
 
== 独自コンポーネント - Part 2 ==
 
== 独自コンポーネント - Part 2 ==
  
Now the example can be extended and we can use arbitrary properties with only a few lines of code:
+
今回の例では、たった数行のコードで、クラスを拡張して任意のプロパティを使うことができるか、を示します。
 +
 
 
<pre>
 
<pre>
 
type
 
type
Line 187: Line 206:
 
このコンポーネントは [[RTTI controls/ja|RTTI コントロール]] を使用して、保存、読み込み利用ができます。これ以上、詳細なコードを書く必要はありません。
 
このコンポーネントは [[RTTI controls/ja|RTTI コントロール]] を使用して、保存、読み込み利用ができます。これ以上、詳細なコードを書く必要はありません。
  
== Writing and Reading components from/to XML ==
+
== コンポーネントをXMLへストリームする ==
  
Streaming components is simple:
+
XML形式でコンポーネントをストリームさせるのはとても簡単です。
See the example in lazarus/examples/xmlstreaming/.
+
lazarus/examples/xmlstreaming/ の例を見てください。
  
 
== 結論 ==
 
== 結論 ==
Line 197: Line 216:
  
 
こちらも参照してください。
 
こちらも参照してください。
 
+
[[RTTI controls/ja|RTTI コントロール]]
[[RTTI controls]]
 

Latest revision as of 08:26, 12 April 2007

Deutsch (de) English (en) français (fr) 日本語 (ja) polski (pl) português (pt)

日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報

はじめに

通常、ハードディスクやネットワークストリームにデータを蓄えておく場合は、それらの特性に応じてロードやセーブ(読み込みや書き込み)のためのコードを書かなければなりません。このチュートリアルでは、余分な load/save コードを書くことなく、RTTI を使用して、ストリームから読み込みや書き込みを行うクラスの書き方について述べます。

lazarus コードでのサンプルや TCheckBox を含んだ TGroupBox をストリームに保存したり、両方のコンポーネントのコピーを作成してストリームから戻すといったデモを説明します。

 <lazaruspath>/examples/componentstreaming/ を参照してください。

RTTI コントロールとのコンビネーションで、GUI と ディスク/ネットワークとの間でプログラムデータをやり取りする際に必要なコードの量を最小限にすることができます。

TComponent / TPersistent

TPersistent クラスは、Classes ユニット内で定義され、{$M+}というコンパイラスイッチで使用されます。このスイッチは定義されたクラスに対して、 Run Time Type Information(RTTI)(訳注:実行時型情報)を付加するコンパイラ指令です。 つまり、TPersistentクラスと、その派生クラスは、実行時型情報をもつクラス群として機能することを意味します。'Published' のプロパティは'public'のプロパティと同様に使用することができますが、実行時型情報が付加されていることで、これらのクラスに対しては、実行時にプロパティへの動的なアクセスが可能になります。

すべてのPublished のプロパティが、読み書きできるだけでなく、実行時に、どのような名前で、どのような型になっているかをリストアップすることができます。 IDE では、ユーザーが生成したコンポーネントを、(今つくられたコンポーネントがどのようなものか、過去につくられたIDEが知る由もありませんが、しかし..)IDEがそのクラスをプロパティを通して自由に扱えるようにするための優れた機能です。

TComponentはTPersistentから派生し、子コンポーネントを持てるようになっています。これはストリーミングで大変重要な事項です。1つのコンポーネントがルートコンポーネントになると、子コンポーネントのリストからlookup rootによって参照されます。

(訳注:1つのクラスがストリーミング or シリアライズされる時は、メモリ上での参照問題をなんらかの形で解決する必要がある。ルートはストリーミングの基点となるクラスであり、参照解決の重要な1つである。)

TReader / TWriter

TReaderとTWriterは、ストリームにたいしてTComponentを読み書きするためのワーカークラスです。(訳注:ストリームクラスにたいして補助的に便利な関数の集まりになっています。) CreateLRSReaderとCreateLRSWriterを見てください。 これらは、特別なフォーマットを読み書きする場合Driverを使います。 このとき、LResourcesユニットで定義されるバイナリオブジェクトフォーマットのためのreader(TLRSObjectReader)とwriter(TLRSObjectWriter)があり、Laz_XMLStreamingで定義されるTDOMDocumentのためのwriter (TXMLObjectWriter)があります。

LResourcesユニットは、バイナリフォーマットからテキストに変換する関数とその逆の関数を含みます。 (LRSObjectBinaryToText, LRSObjectTextToBinary). DelphiではWidestringでしたが、LCLは文字列に対してはUTF8を好みます。 そのための変換関数も同様にいくつかあります。

独自コンポーネントの書き方 - Part 1

カスタムコンポーネントは次のようにシンプルです:

type
 TMyComponent = class(TComponent)
 private
   FID: integer;
 published
   property ID: integer read FID write FID;
 end;

ストリームにコンポーネントを書き出す

LResources ユニットには次のような関数があります:

 procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);

これは、コンポーネントをバイナリフォーマットでストリームに書き出します。

たとえば次のようにします:

procedure TForm1.Button1Click(Sender: TObject);
var
  AStream: TMemoryStream;
begin
  AStream:=TMemoryStream.Create;
  try
    WriteComponentAsBinaryToStream(AStream,AGroupBox);
    ... save stream somewhere ...
  finally
    AStream.Free;
  end;
end;

(訳注:この例では、メモリ上にグループボックスのすべてのプロパティがバイナリ形式で保管される。  一般的には、バッファとして使う以外には、メモリ上に入れる例は少ない。ファイルストリームをつかって、それをファイルに保存して、後日読み出したり、ソケットストリームをつかって、ソケットで別のマシンに転送したりして、オブジェクトの状態を「保存」「転送」することができる。完全に保存するためには、オブジェクトの状態がすべてプロパティでpublishされていなくてはならないことに注意しよう。 Lazarusでは、フォームのデザイン等(=つまりプロパティの設定値)を設計時(コンパイル時)にリソースに保存し、実行時にリソースからプロパティを復元して実行するメカニズムがある。 他の言語では、一般的には、シリアライズと呼ばれることがある)

ストリームからコンポーネントを読み出す

LResources には次のような関数があります:

 procedure ReadComponentFromBinaryStream(AStream: TStream;
   var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
  • AStream はコンポーネントをバイナリフォーマットで含んでいるストリームを指定します。
  • RootComponent は存在しているコンポーネントを指定した場合はそのプロパティは上書きされます。nilを指定した場合は、新しいコンポーネントがストリームから生成されます。
  • OnFindComponentClass はTReaderによって、ストリーム内のクラス名から、クラスを取得するのに使われる、イベント関数です。(訳注:クラス参照型を取得することで、TReaderは名前文字列からそのクラスのコンストラクタを呼ぶことができる)

たとえば、次のように、書くことができます:

procedure TCompStreamDemoForm.OnFindClass(Reader: TReader;
  const AClassName: string; var ComponentClass: TComponentClass);
begin
  if CompareText(AClassName,'TGroupBox')=0 then
    ComponentClass:=TGroupBox
  else if CompareText(AClassName,'TCheckBox')=0 then
    ComponentClass:=TCheckBox;
end;
  • コンポーネントを新規に生成した場合、Owner にはそのコンポーネントのオーナーとなります。

ストリーム可能なプロパティについて

TReaderやTWriterがストリームできるプロパティには、いくつかの条件があります。

  • 基本型はストリームできます。すなわち、integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, などなど. .
  • TPersistent とその派生クラスはストリームできます。
  • 構造体、object型、TPersistentから派生しないクラス型は、ストリームできません。

それらをストリームするには、TReader/TWriterにその方法を教える必要があります。次を見てください。#Streaming custom Data - DefineProperties.

カスタムデータをストリームする - DefineProperties

DefinePropertiesをオーバーライドすることで、通常ではストリームできないデータをストリームすることができるようになります。この方法は、すべてのデータに対して、たとえ基本型をもたないものでも、ストリームすることを可能にします。たとえば、あなたが作ったコンポーネントの変数 FMyRect: TRectをストリームしたい場合、次のような3つのメソッドをコンポーネントに加えます。

procedure DefineProperties(Filer: TFiler); override;
procedure ReadMyRect(Reader: TReader);
procedure WriteMyRect(Writer: TWriter);

続けて次のように実装してください:

procedure TMyComponent.DefineProperties(Filer: TFiler);
var
  MyRectMustBeSaved: Boolean;
begin
  inherited DefineProperties(Filer);
  MyRectMustBeSaved:=(MyRect.Left<>0)
                     or (MyRect.Top<>0)
                     or (MyRect.Right<>0)
                     or (MyRect.Bottom<>0);
  Filer.DefineProperty('MyRect',@ReadMyRect,@WriteMyRect,MyRectMustBeSaved);
end;

procedure TMyComponent.ReadMyRect(Reader: TReader);
begin
  with Reader do begin
    ReadListBegin;
    FMyRect.Left:=ReadInteger;
    FMyRect.Top:=ReadInteger;
    FMyRect.Right:=ReadInteger;
    FMyRect.Bottom:=ReadInteger;
    ReadListEnd;
  end;
end;

procedure TMyComponent.WriteMyRect(Writer: TWriter);
begin
  with Writer do begin
    WriteListBegin;
    WriteInteger(FMyRect.Left);
    WriteInteger(FMyRect.Top);
    WriteInteger(FMyRect.Right);
    WriteInteger(FMyRect.Bottom);
    WriteListEnd;
  end;
end;

This will save MyRect as a property 'MyRect'. これで、MyRectをプロパティとして保存できます。 しかし、もし、たくさんのTRectをストリームする必要がある時、このようなコードで何度も書きたくないでしょう。 LResourcesユニットには、rectプロパティを定義する手続きを書く例があります。

 procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
 

さきほどの方法はこれを使うと非常に短く記述することができます:

procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  DefineRectProperty(Filer,'MyRect',@FMyRect,nil);
end;

独自コンポーネント - Part 2

今回の例では、たった数行のコードで、クラスを拡張して任意のプロパティを使うことができるか、を示します。

type
  TMyComponent = class(TComponent)
  private
    FID: integer;
    FRect1: TRect;
    FRect2: TRect;
  protected
    procedure DefineProperties(Filer: TFiler); override;
  public
    property Rect1: TRect read FRect1 write FRect1;
    property Rect2: TRect read FRect2 write FRect2;
  published
    property ID: integer read FID write FID;
  end;

procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  DefineRectProperty(Filer,'Rect1',@FRect1,nil);
  DefineRectProperty(Filer,'Rect2',@FRect2,nil);
end;

このコンポーネントは RTTI コントロール を使用して、保存、読み込み利用ができます。これ以上、詳細なコードを書く必要はありません。

コンポーネントをXMLへストリームする

XML形式でコンポーネントをストリームさせるのはとても簡単です。 lazarus/examples/xmlstreaming/ の例を見てください。

結論

RTTI は強力なメカニズムです。これは全てのクラスでストリームを簡単に取り扱うことができ、また膨大で退屈な load/save コードの記述を回避するのに役立ちます。

こちらも参照してください。 RTTI コントロール