プレゼンテーション資料
第
21回 エンバカデロ・デベロッパーキャンプ
2012年3月9日~10日
時間 セッション概要 14:00~14:30 【G1】オープニングセッション 「2012年 - エンバカデロの製品戦略」 エンバカデロ・テクノロジーズ 日本法人代表 藤井 等 14:30~15:30 【T2】テクニカルセッション 「実践!Delphiデバッグテクニック」 株式会社シリアルゲームズ 取締役・シニアエンジニア 細川 淳 15:30~15:40 休憩 15:40~16:40 【T3】テクニカルセッション 「Delphi/C++Builder + FastReportで作る実用レポート出力」 株式会社ドリームハイブ 代表取締役 山本 悟 16:40~16:50 休憩 16:50~17:50 【T4】テクニカルセッション 「FireMonkeyファーストインプレッション」 有限会社エイブル 富永 英明 17:50~18:00 休憩 18:00~20:00 【W5】 ワークショップ 「FireMonkeyアプリケーション構築実習」 司会:エンバカデロ・テクノロジーズ エヴァンジェリスト 高橋智宏 20:00~20:30 休憩・移動 プログラム 3月9日
時間 セッション概要 9:00~9:50 【W6】ワークショップ 「挑戦!Delphiクイズで実力チェック」 エンバカデロ・テクノロジーズ エヴァンジェリスト 高橋智宏 9:50~10:00 休憩 10:00~10:50 【T7】テクニカルセッション 「RadPHPでスマホアプリを作ろう!」 エンバカデロ・テクノロジーズ エヴァンジェリスト 高橋智宏 10:50~11:00 休憩 11:00~11:50 【T8】テクニカルセッション 「Delphi言語再入門」 株式会社シリアルゲームズ 取締役・シニアエンジニア 細川 淳 東洋テクニカルシステム株式会社 システム開発部 福士 光 11:50~12:00 休憩 12:00~12:50 【T9】テクニカルセッション 「Delphi/C++BuilderでiOS/Macアプリを作ろう!」 エンバカデロ・テクノロジーズ エヴァンジェリスト 高橋智宏 3月10日 ネットワークの使用について ・ セッション会場では、無線LANをご利用いただけますが、オンライン配信パフォーマンスに影響を与える恐れがあ るため、 会場無線LANを用いたUstream、GotoWebinar、YouTube等のビデオ視聴、その他のダウンロードなどはお控えください。 セッション資料のダウンロード ・ セッション資料の最新版ならびに配布資料に含まれないプレゼンテーション資料については、後日Embarcadero Developer
17Th Developer Camp
「RadPHPで
スマホアプリを作ろう!」
エンバカデロ・テクノロジーズ エヴァンジェリスト 高橋智宏 【T7】テクニカルセッションアジェンダ
•
RadPHP XE2 に関するアップデート
•
Web画面のレイアウト調整
– 横幅が重要 • PHP & JavaScriptのイベントの実装詳細 – 画面右端に張り付くボタンを実機で確認• iOS (iPhone 4, iPad 2)
• Android 2.2~ (スマホ, タブレット) • Windows Phone 7.5
• BlackBerry
•
少し本格的なWebアプリを作ってみる
– jQuery Mobile 特有の注意点!!
– TMS社のIntraWeb iPhone Controls Packのデモアプリと同様のものを
•
Webアプリをフルスクリーンで
デモ
17Th
Developer Camp
RadPHP XE2 に関する
アップデート
アップデート – 主な変更点
•
Update 4
– ビルド番号: 4.4.0.1656
•
ZendFrameworkのサンプルを追加
– ZHttp, ZJson, ZJsonServer, ZRestClient, ZRestServer
•
モバイル端末の背景を追加
– 各種Android端末
– BlackBerry
•
JQuery Mobile 1.0 final を統合
– jQuery 1.6.4 を内蔵
•
PhoneGap 1.3.0 を統合
17Th
Developer Camp
jQuery Mobile
jQuery Mobile とは?
• 2010年8月11日に始まったオープンソースプロジェクト
• jQueryをベースに、モバイルデバイスのWebブラウザに特化したGUIを
提供するJavaScriptライブラリおよびCSS
サポートされるプラットフォーム
横幅は重要
•
基本的に、モバイルWebアプリは横スクロールしません
– もちろん、縦スクロールはします•
Web画面の横幅は、端末ごとに異なります
– さらに、iOS, Android, WP7 では、端末の回転により変化します – 横幅は、JavaScriptの window.innerWidth で取得可能端末の回転を検知するには?
•
iOS
– jQuery Mobile の orientationchangeイベント を利用する
• window.onresizeイベントの監視は不要!!
•
Android 2.2~
– window.onresizeイベントを利用する • orientationchangeイベント が来ても無視する!! – innerWidthは回転前の値を示すので使えない…•
Windows Phone 7.5
– window.onresizeイベントを利用する • そもそも orientationchangeイベント が発生しない…•
BlackBerry
viewport metaタグのwidthパラメータ
•
スマホでもタブレットでも width=320 でOK !?
– WP7以外の端末 • window.innerWidth 端末の向きに応じた実際の横幅 – Windows Phone 7.5 • window.innerWidth 常に320 !? – 端末が横向きなら画面全体が拡大表示されてしまう!! • viewoport で width=device-width を指定すると… – window.innerWidth 端末の向きに応じた実際の横幅画面右端に張り付くボタン
• PHP側の実装 • 手順その1 : MPageのOnCreateイベント – WP7に対処 • 手順その2 : MPageのOnStartBodyイベント – ボタンの移動を行う関数をJavaScriptで記述 – jQueryのAPIで、ボタンの外側のdivのCSSの left を変更画面右端に張り付くボタン
• 手順その3 : MPageのOnOrientationChange Javascriptイベント – Android に対処 • 手順その4 : MPageのOnPageShow Javascriptイベント – Android, WP向けにonresizeイベントハンドラのインストール – とりあえず、すぐにボタンの移動を行う実機で確認 – 画面サイズ, ボタンサイズ, 回転
• iPhone 4, iPad 2
• Androidスマホ, Androidタブレット
• Windows Phone 7.5
17Th
Developer Camp
少し本格的な
Webアプリ
jQuery Mobile 特有の注意点
•
複数のWebページのイメージが、単一のWeb画面内で同
居することがある
– Ajaxを利用した画面遷移 • 例: 横にスライドしながら新しいページが表示される場合 – そのため、あるページを設計・実装する際、その中に配置す るコンポーネント名は、複数ページ全体で一意になるように すべき。 • 同一のidを持つHTML要素が、同居して競合を起こすのを防ぐ • 独自に、コンポーネント名(変数名)の命名規約を設けてくださ い。•
iOS 5.0 の iPhone の Safari 向けには、MPageの
touchOverflowEnabledプロパティを false に設定!!
まずは、お手本となるWebアプリを確認
•
Delphi XE2(C++Builder XE2)
– IntraWeb XII
– TMS IntraWeb iPhone Controls Pack(トライアル版)の
メインページ – デザイン
•
MListは、PHP側で動的に構築
MLink 画面遷移に使う。単にボタンとしても使える MobileTheme テーマを指定する。 カスタムテーマへの応用も可 MList リストビュー。行数は可変なので、高さが変わる 項目クリック時に編集ページに遷移させる。 MToolBar 複数のMlinkを配置できる Settingクリック時に設定ページに遷移させるメインページ – コード
•
MPageのOnCreateイベントで、MListの項目を動的に生
設定ページ – デザイン
•
Saveボタン: PHPの $_SESSION 変数をAjaxで更新
MLink 遷移元(前)のページに戻る、特別なリンク MToggle 2つの値を管理できるトグルスイッチ MButton 画面情報をPHPに送信 AjaxモードでRadPHPのイベントハンドラを起動可
設定ページ – コード
•
トグルスイッチの値を $_SESSION で管理。最初に画
面を生成する際に、トグルスイッチの初期値を指定
•
[Back]ボタンに関する処理は無い
– Webブラウザ内だけで、前の画面を復帰させてしまう。 • 前の画面の内容を、PHP側で更新できないので注意!!編集ページ – デザイン
•
Backボタンは、UpdateボタンでAjax更新後、モードを変更
MLink 遷移元(前)のページに戻る、特別なリンク MEdit 文字列を編集する MSlider 例: 最小値(0)~最大値(100) のスライダ MComboBox 項目を選択するコンボボックス。複数選択可 MButton 画面情報をPHPに送信 AjaxモードでRadPHPのイベントハンドラを起動可編集ページ – コード
•
MSliderとMComboBoxの値はテキトーに設定する
– [Save]ボタンを押して、Ajax経由で画面を更新•
Backボタンは、[Save]ボタンで画面を更新後、メインペー
ジを明示的に呼び出すリンクに変化させる。
– メインページの内容を更新して表示させたいので17Th
Developer Camp
iOS で Webアプリを
フルスクリーン表示
4
iOS専用のタグを<head>…</head>に追加
•
MPageのOnShowHeaderイベントを利用
– スプラッシュ画像を用意 – iPhone,iPhone(Retina),iPad用に3種類のアイコンを用意 – フルスクリーン可であることを知らせるmetaタグ•
iOSのSafariで[ホーム画面に追加]を利用
17Th Developer Camp
「Delphi言語再入門」
株式会社シリアルゲームズ 取締役 細川 淳 【T8】テクニカルセッション
17Th
Developer Camp
Classについて改めて
class
• Delphi を使う上で切っても切れない class 型 • class 型について、どの位の事をご存じですか? • 良くは判らないけど、component もクラスだし、Form を作ったら強制的 にクラスになっちゃうから、ただ何となく使っている? • ここでは、そんな class 型について深く見ていくことにします – ただ、そもそも class とはなんぞや?という話はしません – もう少し深く見ていきます。class 定義
•
class 定義はヘルプより下記の様になっています
type
className = class [abstract | sealed] (ancestorClass)
memberList end;
•
abstaract, sealed って?
class 属性
•
abstract 属性
– 抽象クラスを表す属性です – この属性を指定するとクラスは抽象クラスとなり生成が禁 じられ……ません • 下位互換性を保つために抽象クラスの生成が許可されて います →実質 abstract 属性は、あまり意味がありません。 • しかし、abstract メソッドがある場合、警告が出ます – abstract メソッドについては後述class 属性
•
sealed 属性
– sealed を付けて宣言すると、そのクラスを継承する事が できなくなります • Java の final と同じです。 – Delphi にも final はあるのですが、メソッドに付けます。 • ただし class helper は使えます – 拡張するだけなので当然ですね – class helper でメソッドを隠蔽可能ですclass 属性 - sealed
type
TTestClass = class sealed public
procedure Test; virtual; end;
TTestClass2 = class(TTestClass) public
procedure Test; override; end;
ここで、下記のエラーが発生する
class 属性 - sealed
type
TTestClass = class sealed public
procedure Test; virtual; end;
TTestClass2 = class helper for TTestClass procedure Test; end; class helper として拡張するのは OK メソッドを隠蔽して元の Test を呼ばないようにできる 隠蔽した元の Test は inherited Test; として呼び出し可能
class 定義 - ancestorClass
• ancestorClass は継承元クラス – 何も指定しないと TObject を継承します • TObject もクラスとして定義されている(System.pas)ので TObject って一体なんだ!という話になりますが…… • コンパイラマジックの一種と考えて良いと思います • TObject • TObject は特別なクラスです。 • 全てのクラス型の大元になります。 • TObject のメソッドを使えないクラスはありません。 • record 型もメソッドを持てるようになりましたが、class 型との最 大の相違は TObject を先祖にもつか?ということです。 つまり、record 型は TObject を先祖にもちません!class とクラス参照型(メタクラス)
Delphi 言語では class も1つのオブジェクト(※)です。 例えば Application.CreateForm(TForm1, Form1); は、皆さん見たことがあると思います。 ここで、見て欲しいのが TForm1 と書いてあるところです。 class は型ですが、型そのものを引数として渡しています。 これは integer などでは不可能な事です。 このように class 型を参照する型を「クラス参照型」といいます(「メタクラス」 ともいいます)class とクラス参照型(メタクラス)
Application.CreateForm の宣言を見てみます procedure TApplication.CreateForm(
InstanceClass: TComponentClass; var Reference);
TComponentClass = class of TComponent; こんな風になっています。
ここで、赤枠で囲った部分がクラス参照型の宣言です。
このようにクラス型そのものを渡す事によって、Factory を作ったり、多態性 を確保する事が容易になります。
class とクラス参照型(メタクラス)
typeTFigure = class(TObject) public
procedure Draw; virtual; abstract; end;
TFigureClass = class of TFigure; TTriangle = class(TFigure)
public
procedure Draw; override; end;
TRect = class(TFigure) public
procedure Draw; override; end;
TFigureDrawer = class(TObject) private
FFigure: TFigure; public
constructor Create(const iFigureClass: TFigureClass); procedure Draw;
end;
constructor TFigureDrawer.Create(const iFigureClass: TFigureClass); クラス参照型を定義
三角形を描画する
class クラスメソッド
class にはクラスメソッドといわれるものがあります。 具体的には このように宣言の前に "class" をつけたメソッドです。 クラスメソッドはインスタンスを必要としません。 つまり のように呼び出すことができます。 さて、Method1(); と Method2(); の違いはなんでしょうか……? typeTFoo = class(TObject) public
class procedure Method1;
class procedure Method2; static; end; procedure Test; begin TFoo.Method1; TFoo.Method2; end;
classクラスメソッドと静的メソッド
普通に考えるとクラスメソッドはインスタンスが存在しないため、Self が存在しません。 それは、Self とは、インスタンスを表しているからです。 しかし!ここで、Delphi では Class もオブジェクトである、という事を思い出してください! Method1; の中で Self を参照できてしまうのです! typeTFoo = class(TObject) public
class procedure Method1;
class procedure Method2; static; end;
class procedure TFoo.Method1();
begin Self.Method2; Self.Create; end; クラスメソッド中の Self はクラスオブジェクトそのものを 指します。 なので、このように Self をクラスのように使えるのです。
class クラスメソッドと静的メソッド
それに対して "static" 指定をされたクラスメソッドは、Self を持ちません。 このことから、static 指定されたメソッドを特に「静的メソッド」と呼びます。
type
TFoo = class(TObject) public
class procedure Method1;
class procedure Method2; static; end;
class procedure TFoo.Method2();
begin
Self.Create; end;
ここで、下記のエラーが出ます
class クラスメソッドと静的メソッド
ちなみに、バイナリレベルでも違いがでます。 static 指令がない Method1 の呼び出しでは、 eax レジスタに Self を代入して呼び出していま す。 しかし、Method2 ではシンプルに何もせずに 呼び出しているだけです。 特にクラス参照型を欲しない場合は static 指 令を付けた静的メソッドを使うとコードサイズが 若干小さくなります。 typeTFoo = class(TObject) public
class procedure Method1;
class procedure Method2; static; end; procedure Test; var Foo: TFoo; begin Foo := TFoo.Create; TFoo.Method1(); Foo.Method1(); TFoo.Method2(); end; Unit1.pas.58: TFoo.Method1;
0051A757 A16CA65100 mov eax,[$0051a66c] 0051A75C E853020000 call TFoo.Method1 Unit1.pas.59: Foo.Method1;
0051A761 8B45FC mov eax,[ebp-$04] 0051A764 8B00 mov eax,[eax] 0051A766 E849020000 call TFoo.Method1
class メンバの可視性
•
private, protected, public, published
– これらの指定を、もちろん見たことがあると思います。
– private, protected, public については、説明は不要かと
思いますが、一応説明すると下表の様になります 可視性 自分自身 継承先クラス 同一ユニット private ○ ○ protected ○ ○ ○ public ○ ○ ○ strict private ○ strict protected ○ ○
classメンバの可視性 - published
published と public は、公開範囲は同じように思えます
では、何が違うのでしょうか?
published は public と違い RTTI を生成します。
RTTI とは「実行時型情報」
です。
※本来「型」とは、コンパイル時に必要なものです。
たとえば、Integer 型に Class のインスタンスを代入しようとすればコンパイルエラー になります。
しかし、Integer 型と Class のインスタンスは同じ4バイトです(32bitの時)。 CPU から見たときに違いはありません。
class メンバの可視性 - published
•
published 指定されたメンバは
– ソースコードレベルの可視性は同じ – RTTI が生成される – RTTI を介して外部のプログラムがメンバーを参照できる • オブジェクトインスペクタでプロパティを編集できるのは、こ のためです。 – メソッドの overload はできない • もちろん public では overload できます。class メンバの可視性 - published
public, published の違いについて誤解を恐れずにいうと
•
public は、
ソースコードレベルで公開
– ソースコード中でメンバを参照できる•
published は、
バイナリレベルで公開
– コンパイル済みのバイナリに対して、参照できるとなります
class のバインディング
•
RTTI が出てきたので、ここでバインディングについても
見ていきます。
•
バインディングとは
– virtual – dynamic – override のことです。class のバインディング
•
virtual と dynamic
– virtual と dynamic はソースコードレベルでは同じ動作を します。 – どちらも、override 可能、abstract 指定可能です。 – 違いをヘルプで引くと下記の様に書かれています。 – 意味はわかるけど仕組みが判りませんね バインディング 最適化 備考 virtual 実行速度を最適化 最も効率的な方法 dynamic サイズを最適化 たまにしかオーバーライドされない時に使用するclass のバインディング
実際の所、実装上の違いは、どうなっているかというと となります。 先ほどの表の「たまにしかオーバーライドされない時に使用する」というのは 「派生先でメソッドテーブル」が生成されないため、コードサイズが小さくなる 、という意味です。 バインディング 意味 virtual 派生クラスでもメソッドテーブルが生成される dynamic 基本クラスにのみメソッドテーブルが生成されるclass のバインディング - VMT
• VMT とは Virtual Method Table の事です。
Method1 Method2 Method3 Method4 VMT メソッドの呼び出しは、Table への参照に置 き換わります 概念的にはメソッドのポインタへの配列です。 たとえば、ソースコードに Method1(); という呼び出しコードがあった場合 VMT[0](); といったコードになる、ということです。 Method1(); VMT 内の 0 番目の参照を呼び出す
class のバインディング - VMT と override
•
override
Method1 Method2 Class Foo の VMT Method1 Method2 Class Bar の VMT typeTFoo = class(TObject) public
procedure Method1(); virtual; procedure Method2(); virtual; end;
TBar = class(TFoo) public
procedure Method1(); override; end;
テーブルの内容が置き換わる!
//擬似コードとして書くと
VMT[0] := @TFoo.Method1; // Method1 のアドレスが入った // TBar で TFoo の Method1 を override すると
class のバインディングと abstract
•
abstract
Method1 Method2 Class Foo の VMT Method1 Method2 Class Bar の VMT typeTFoo = class(TObject) public
procedure Method1(); virtual; abstaract; end;
TBar = class(TFoo) public
procedure Method1(); override; end;
場所が確保されているだけで 中身は無い!
//擬似コードとして書くと
VMT[0] := nil; // アドレスは nil !
// TBar で TFoo の Method1 を override すると
メソッドポインタ
今まで見てきた通り Class には2つの構成要素があります。 – Self インスタンスのアドレスを表す – Method メソッドのアドレス クラス参照型は、クラスオブジェクトのアドレスを渡しています。 アドレスは、32bit のマシンでは4バイトで表されます。 VMT に格納されているメソッドのアドレスも4バイトです。 Self が指すクラスのインスタンスも4バイトです。メソッドポインタ
Object 必要なバイト数 Self 4 byte Method 4 byte 計 8 byte typeTFoo = class(TObject) private
FEvent: TNotifyEvent;
procedure TestEvent(Sender: TObject);
end; procedure Test; var Foo: TFoo; begin Foo := TFoo.Create; Foo.FEvent := Foo.TestEvent; end; Foo と TestEvent で、それぞれ4バイト必要 Delphi の Pointer 型は4バイトしか格納できないけど…… どうやって、アドレスを代入しているのか?? ここで、イベントについてちょっと見てます。
メソッドポインタ
Delphi では「メソッドポインタ」を使ってイベントを管理しています。 ヘルプを引用すると メソッドポインタは,特定のクラスインスタンスの特定のメソッドを指す特殊なポインタ型です。 メソッドポインタはほかの手続き型と同じように動作しますが,手続き型と違ってクラスインスタン スへの隠されたポインタを保持しています。 つまり、メソッドポインタは、コードを書いていると気づきませんが、実は8バ イトのポインタなのです。 メソッドポインタの宣言には下記の様に of object を指定します。 typeTTestEvent = procedure(Sender: TObject; iFoo: TFoo) of object;
var
メソッドポインタ - TMethod
type
TFoo = class(TObject) private
procedure Method;
end;
procedure Test;
type
TProc = procedure of object;
var Foo: TFoo; Proc: TProc; Method: TMethod; begin Foo := TFoo.Create; Proc := Foo.Method; Method := TMethod(Proc); TProc(Method)(); Foo.Free; TMethod という面白い機構が存在します。 TMethod は 8 byte のレコードで、メソッドポインタを保持できます メソッドポインタは TMethod へキャスト可能で す。 そして、TMethod もメソッドポインタにキャスト できます。 procedure Test; type
TProc = procedure of object;
var Foo: TFoo; Proc: TProc; begin Foo := TFoo.Create; TMethod(Proc).Data:= Foo; TMethod(Proc).Code:= Foo.MethodAddress('Method') Proc; Foo.Free; こんな風にも書けます
メソッドポインタ - TMethod
type
TProc = procedure of object; TFoo = class(TObject)
private FMsg: String; procedure Method; end; procedure TFoo.Method; begin ShowMessage(FMsg); end; procedure Test; var Foo: TFoo; Bar: TFoo; Proc: TProc; Method: TMethod; begin Foo := TFoo.Create; Bar := TFoo.Create; Foo.FMsg := 'Foo' Bar.FMsg := 'Bar'; Proc := Foo.Method; Method := TMethod(Proc); TProc(Method)(); Method.data := Bar; TProc(Method)(); TMethod は Code, Data という2つのメンバを持っています。
Code は、メソッドのアドレス Data は、インスタンスのアドレス です。
17Th
Developer Camp
Classの演習
TMethodCaller を作る
いままで、通して来てクラスについての理解が深まったと
思います。
TMethodCaller の要件
•
クラスとメソッド名を引数にもつ静的メソッド
– メソッドの名前は仮に execute とした場合、下記の様に 呼び出せる • TMethodCaller.Execute(TTestClass, 'Method');•
このメソッドを呼び出すとクラスを生成し、該当メソッドを
呼び出す
– つまり、上記の例は↓と同じ事をする • (TTestClass.Create).Method();•
なお、呼び出せるメソッドは引数を持たない手続きとしま
す
TMethodCaller - RTTI
•
RTTI を扱うクラスがあります。
•
それを使うとメソッドの引数なども簡単に取得できます。
•
それを使えば今回のように引数なしのメソッドではなくて
も、安全に呼び出すことができます。
– 腕に自信がある方は、是非挑戦してみてください。TMethodCaller - 宣言部
unit uMethodCaller;
interface type
TMethodCaller = class(TObject) private
type TCalledProc = procedure of object; public
class procedure Execute( const iClass: TClass;
const iMethodName: String); static; end;
TMethodCaller - 実現部
implementation
class procedure TMethodCaller.Execute( const iClass: TClass;
const iMethodName: String);
var Obj: TObject; Proc: TCalledProc; begin Obj := iClass.Create; try TMethod(Proc).Data := Obj; TMethod(Proc).Code := Obj.MethodAddress(iMethodName); if (TMethod(Proc).Code <> nil) then
Proc; finally Obj.Free; end; end; end.
TMethodCaller - 使い方
type
TTestClass = class(TPersistent) // TObject から生成すると published のところで警告発生 published procedure Test; end; procedure TTestClass.Test; begin ShowMessage('TEST'); end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMethodCaller.Execute(TTestClass, 'Test');
17Th Developer Camp
「Delphi言語再入門」
~拡張されたRTTIを試してみる
東洋テクニカルシステム株式会社 システム開発部 福士 光 【T8】テクニカルセッションアジェンダ
•
従来のRTTIでできること
•
Delphi 2010で新しく拡張されたRTTI(拡張RTTI)で
できるようになったこと
•
試してみる(1)~クラス内のメンバの列挙
•
試してみる(2)~クラス内のメンバの値の取得
•
拡張RTTIと属性を使う上での注意事項
基本知識~RTTIとは?
•
RTTI(実行時型情報)とは?
– RunTime Type Informationの略(Informationではなく Identificationとすることもある)。 – 実行時にメモリ上のオブジェクトのデータ型に関する 情報を取得、操作できる。 – Delphiでは従来からpublished宣言を行うことでRTTIが 生成され、 IDEがコンポーネントの持つRTTIを利用して いる。 – Delphi 2010/XE/XE2ではRTTIが拡張され、より多くの ことが可能になった(実行バイナリのサイズが大きくなる という副作用がある)。
17Th
Developer Camp
従来のRTTIでできること
従来のRTTIでできること(1)
•
(System.)TypInfoユニット
•
実行時にプロパティの型情報を探す。
– publishedなプロパティのみ(コンパイラ指令{$M+} または {$TYPEINFO ON}が必要) – GetPropInfo関数、GetPropList関数など – PPropInfo = ^TPropInfo (レコード型へのポインタ)•
実行時に型情報を元に値を取得、設定する。
– GetXXXXProp/SetXXXXProp関数(XXXXは対象となる プロパティの型による)•
DelphiのIDEがフォームをストリーム化したりプロパティ
エディタで使用しているのはご存知のとおり。
従来のRTTIでできること(2)
•
仮想メソッドテーブル(VMT)
– virtual/overrideと宣言されたメソッドのテーブル (クラスごとに存在)•
動的なメソッドのテーブル
– dynamic/overrideあるいはmessageと宣言された メソッドのテーブル(テーブル上では動的メソッドなのか メッセージハンドラなのかは区別されていない)17Th
Developer Camp
拡張RTTIでできるように
なったこと
拡張RTTIでできるようになったこと(1)
•
(System.)RTTIユニット
•
実行時にフィールド、プロパティ、メソッドの型情報を
取得する。
– コンパイラ指令{$RTTI}で拡張RTTIをプロパティ、 フィールド、メソッドのそれぞれに対してどの範囲 (published/public/protected/private)のものに 付けるのかを制御 – クラス型(class)またはレコード型(record)が対象 →型情報そのものは全ての型(Integer, Boolean, ...)に 存在している拡張RTTIでできるようになったこと(2)
•
実行時に型情報とインスタンスへのポインタを元に
各種の操作を行う。
– フィールドの値の取得、設定 – プロパティの値の取得、設定 – メソッドの呼び出し(invocation)拡張RTTIでできるようになったこと(3)
•
属性(attribute)による注釈付け(annotation)
– クラス型あるいはレコード型に属性で注釈を付ける – クラス型あるいはレコード型のメンバ(フィールド、 プロパティ、メソッド)に属性で注釈を付ける • .NET Frameworkと同様の記法を使う [<Attr>] [<Attr>(<parameterlist>)] • 先頭の“T”と末尾の“Attribute”、コンストラクタの “.Create”を省略できる [T<Attr>Attribute.Create]拡張RTTIでできるようになったこと(4)
•
属性(attribute)による注釈付け(annotation)
– カスタム属性(custom attribute)の宣言 • (プロパティやフィールドの値ではなく)属性クラスの型で 区別する →例外ハンドラを記述するときに例外オブジェクトの型で 区別を行うのと同様に • コンストラクタで渡した値(定数のみ)をフィールドまたは プロパティに保存して参照することもできる • TCustomAttributeクラスから派生したカスタム属性 クラスを宣言して使用する拡張RTTIでできるようになったこと(5)
•
属性(attribute)による注釈付け(annotation)
– 実行時にクラス型、レコード型に付けられている属性を 抽出する – 実行時にクラス型、レコード型のメンバに付けられている 属性を抽出する•
属性を使う状況
– 同じ型から派生していても区別して処理したい – 同じ型のメンバでも区別して処理したい拡張RTTIでできるようになったこと(6)
•
拡張RTTIと属性についての補足。
– 拡張RTTIはデフォルトでは以下の範囲に付けられている (Systemユニットで定義) – Delphi 2010以降の実行ファイルが大きくなってしまう 原因のひとつ – {$RTTI EXPLICIT ...}で(継承元クラスの指定とは 独立して)拡張RTTIを付ける範囲を指定できる可視性 private protected public published
フィールド 〇 〇 〇 〇
プロパティ × × 〇 〇
拡張RTTIでできるようになったこと(7)
•
拡張RTTIと属性についての補足。
– 属性は検索、抽出されるときに実体が生成される →検索、抽出しなければ性能上のペナルティはない →実行バイナリ、占有メモリのサイズのペナルティはある – 拡張RTTIを扱うコードは遅い•
どのような場合に拡張RTTIを使えばよいのか?
– クラスに対する汎用な処理の記述 →ORマッパやXMLへのシリアライズ/デシリアライズ →クラス構造のツリー表示17Th
Developer Camp
試してみる(1)
クラス内のメンバの列挙
クラス内のメンバの列挙(1)
•
TRTTIContext ((System.)RTTIユニット)
– 全ての操作はここから始まる
– 高度なレコード型
– 内部リソースの管理、解放のためクラスのコンストラクタ、
デストラクタのようにclass function Createと
procedure Freeを呼ぶことが推奨されています uses Rtti; var ctx: TRttiContext; begin ctx := TRttiContext.Create; try // RTTIを扱う finally ctx.Free; end; end;
クラス内のメンバの列挙(2)
•
TRTTIContext
– GetTypeメソッド
• 指定されたクラス型のRTTIオブジェクト(TRTTITypeから 派生したクラスのインスタンス)を取得
クラス内のメンバの列挙(3)
•
TRTTIType
– クラス型(RTTIオブジェクトの基底クラス)
– GetPropertiesメソッド
• 所属するクラスのプロパティのRTTI情報を全て取得
function GetProperties: TArray<TRttiProperty>;
– GetFieldsメソッド
• 所属するクラスのフィールドのRTTI情報を全て取得
function GetFields: TArray<TRttiField>;
– GetMethodsメソッド
• 所属するクラスのメソッドのRTTI情報を全て取得
function GetMethods: TArray<TRttiMethod>;
– 階層順に(継承先から継承元に向かって)RTTI情報が
クラス内のメンバの列挙(4)
•
TRTTIType
– GetDeclaredPropertiesメソッド – GetDeclaredFieldsメソッド – GetDeclaredMethodsメソッド – そのクラスで定義したプロパティ/フィールド/メソッドの RTTI情報だけがリストアップされるクラス内のメンバの列挙(5)
•
TRTTIProperty
– クラスのプロパティのRTTI情報
– クラス型(TRTTIMemberから派生)
– Nameプロパティ(名前)
property Name: string;
– Visibilityプロパティ(可視性)
クラス内のメンバの列挙(6)
•
それでは実際に試してみましょう。
– 新規作成→VCLフォームアプリケーション – フォーム上にボタン(Button1)とメモ(Memo1)を配置 – メモのAnchorsにakRightとakBottomを追加、 crollBarsをssBothに変更。クラス内のメンバの列挙(7)
procedure TForm1.EnumProperties(AObject: TObject);
const
MemberVisibilities: array [TMemberVisibility] of String = ('private', 'protected', 'public', 'published');
var
ctx: TRttiContext; prop: TRttiProperty;
begin
Memo1.Lines.Add('Class: ' + AObject.ClassName); ctx := TRttiContext.Create;
try
for prop in ctx.GetType(AObject.ClassType).GetProperties do begin
Memo1.Lines.Add(' Property: ' + prop.Name +
‘ (’ + MemberVisibilities[prop.Visibility] + ‘)’); end; finally ctx.Free; end; end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumProperties(Form1); EnumProperties(Button1); EnumProperties(Memo1);
クラス内のメンバの列挙(8)
•
とりあえず実行してみましょう。
•
Memo1.Lines.Addの行にブレークポイントを設定し、
prop(TRttiProperty)を評価してみる。
17Th Developer Camp
試してみる(2)
クラス内のメンバの値の
取得
5
クラス内のメンバの値の取得(1)
•
TValue ((System.)RTTIユニット)
– 拡張RTTIでデータを格納する – 高度なレコード型 – バリアントもどき(“バリアント型の軽量版”) – 実際のデータはFDataフィールド(TValueDataレコード 型、共用体)に格納しているクラス内のメンバの値の取得(2)
•
TValue
– Kindプロパティ
• 型の種類を列挙として取得
property Kind: TTypeKind;
– TypeInfoプロパティ
– TypeDataプロパティ
• 型の情報をレコード型として取得
property TypeInfo: PTypeInfo read GetTypeInfo;
クラス内のメンバの値の取得(3)
•
TValue
– IsXXXXメソッド/プロパティ • 格納されているデータの状態を問い合わせる IsEmpty/IsObject/IsInstanceOf/IsClass/IsOrdinal/IsType/IsArray – AsXXXX/TryAsXXXXメソッド • 格納されているデータを特定の型で取得する AsObject/AsClass/AsOrdinal/AsType/AsInteger/AsBoolean AsExtended/AsInt64/AsInterface/AsString/AsVariant/AsCurrency TryAsOrdinal/TryAsTypeクラス内のメンバの値の取得(4)
•
TValue
– 暗黙の型変換(implicit conversion) • データを格納する string/Integer/Extended/Int64/TObject/TClass/Boolean – FromXXXXメソッド • データを格納する FromVariant/From<T>/FromOrdinal/FromArray – ToStringメソッド • データをとりあえず文字列化クラス内のメンバの値の取得(5)
•
TRTTIPropertyとTValue
– TRTTIProperty.GetValue
• 特定のインスタンスのプロパティの値を取得
function GetValue(Instance: Pointer): TValue;
– TRTTIProperty.SetValue
• 特定のインスタンスのプロパティの値を設定
procedure SetValue(Instance: Pointer; const AValue: TValue);
– TRTTIPropertyが示しているのは型に関する情報で
クラス内のメンバの値の取得(6)
•
こちらも試してみましょう。
– GetValueでプロパティの値を取得して、 TValue.ToStringで文字列化します – GetValueは例外が起きるかもしれないので try...except...endで保護しますprocedure TForm1.EnumProperties(AObject: TObject);
const
MemberVisibilities: array [TMemberVisibility] of String = ('private', 'protected', 'public', 'published');
var
ctx: TRttiContext; prop: TRttiProperty; V: TValue;
begin
Memo1.Lines.Add('Class: ' + AObject.ClassName); ctx := TRttiContext.Create;
try
for prop in ctx.GetType(AObject.ClassType).GetProperties do begin try V := prop.GetValue(AObject); Str := V.ToString; except on E: Exception do begin Str := 'Error (' + E.Message + ')'; end; end;
Memo1.Lines.Add(' Property: ' + prop.Name +
' (' + MemberVisibilities[prop.Visibility] + ')' + ' Value = ' + Str); end; finally ctx.Free; end; end;
クラス内のメンバの値の取得(7)
クラス内のメンバの値の取得(8)
•
SetValueメソッドでプロパティの値を変更することも
可能です。
•
GetValueで取得したTValueのTypeInfo.Kindが
tkClassなら、AsObjectはクラス型のプロパティです。
•
あるいは
TRTTIPropertyやTRTTIFieldのHandle
プロパティが
nilでなければPTypeInfo
(=^TTypeInfo)なので、Handle.Kindでその
プロパティ
/フィールドの定義の型を知ることもできます。
→再帰処理で複合クラスのトラバースが実現できます。17Th
Developer Camp
拡張RTTIと属性を使う上での
注意事項
拡張RTTIと属性を使う上での注意事項(1)
•
配列に対するサポートが弱い。
– Delphi XE2ではTRttiTypeにGetIndexedProperties
メソッドが追加されて配列プロパティに関する情報を取得 できるようになったが、通常のプロパティに比べて微妙に 使えない – 静的配列のフィールドもうまく扱えない – 動的配列のフィールドは通常のプロパティ並に扱える ので、配列プロパティ、静的配列のフィールドの代替と して動的配列のフィールドを用意してエイリアス的に使う という回避策が有効
拡張RTTIと属性を使う上での注意事項(2)
•
属性のコンストラクタ
– TCustomAttributeのコンストラクタはパラメータを 持たないが、派生したクラスでコンストラクタを定義する ことで値を渡すことができる(フィールド、プロパティでその 値を保持する)constructor Create(const AFooValue: String);
– しかしコンストラクタのパラメータは定数しか使えない
(文字列定数はOK)
– ポインタでも定数なら使えるはずだが、現実には内部
17Th
Developer Camp
参考文献
参考文献 (1)
•
Delphiのヘルプ
RTTI の操作 (オンライン) http://docwiki.embarcadero.com/RADStudio/ja/RTTI_ %E3%81%AE%E6%93%8D%E4%BD%9C%EF%BC%9A%E3%82%A4%E 3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9•
“Rob’s Technology Corner”
“Delphi 2010 - RTTI & Attributes”シリーズ
http://robstechcorner.blogspot.com/2009/09/so-what-is-rtti-rtti-is-acronym-for-run.html
参考文献 (2)
•
Delphi 2010 Handbook
– Marco Cantù著 – CreateSpace (http://www.createspace.com/) – ISBN978-1450597265 (ISBN1450597262) – 43.50USD(書籍版)、28.00USD(ebook) http://www.marcocantu.com/dh2010/ http://www.amazon.com/dp/1450597262 (書籍) http://sites.fastspring.com/wintechitalia/product/ delphi2010handbook (ebook/PDF) – 英語ですが表現は比較的平易。 – 拡張RTTIについて記述があります(Chapter 3、4)。参考文献 (3)
•
Delphiクイックリファレンス
– Ray Lischner著 – 光田 秀、竹田 知生訳 – オライリー・ジャパン – ISBN978-4873110400 (ISBN 4-87311-040-8) – 4,725円 http://www.oreilly.co.jp/books/4873110408/ http://www.amazon.co.jp/dp/4873110408 – 残念ながら絶版です。 – 従来のRTTIについて記述があります。参考文献 (4)
•
Inside Delphi
– Ray Lischner著 – 光田 秀訳 – 大野 元久、服部 誠監修 – アスキー – ISBN978-4756119513 (ISBN 4-7561-1951-4) – 9,240円 http://www.amazon.co.jp/dp/4756119514 – こちらも残念ながら絶版です。 – 従来のRTTIについて記述があります。17Th Developer Camp
Delphi/C++Builderで
iOS/Macアプリを作ろ
う!
エンバカデロ・テクノロジーズ エヴァンジェリスト 高橋智宏 【T9】テクニカルセッションアジェンダ
•
OS Xで動的ライブラリ(.dylib)
– Delphiで作成して、Delphiアプリから呼び出し – C++Builderで作成して、C++Builderアプリから呼び出し•
SQLite3 を利用する
– C++BuilderでOS X内蔵のSQLite3を利用する•
OpenCL を利用する
– C++BuilderでOS X内蔵のOpenCLを利用する•
OS XでSOAPクライアントを作成する
– Win32/Win64のSOAPサーバーからのTClientDataSetを受信して、 FireMonkeyのStringGridに表示•
Update 4 で新たに追加されたFireMonkey向けモ
バイルコネクタ
デモ デモ デモ デモ デモ17Th
Developer Camp
OS Xで動的ライブラリ(.dylib)
Delphiで作成
•
lib[プロジェクト名].dylib が生成される
Delphiアプリから呼び出し
•
external ‘xxxx.dylib’;
C++Builderで作成
•
[プロジェクト名].dylib が生成される
– 共有メモリマネージャ(MEMMGR.LIB,BORLNDMM.DLL)は無い
•
int32_t, char16_t などの型(stdint.h)を使いましょう
C++Builderアプリから呼び出し
•
#pragma link ‘xxxx.dylib’
17Th
Developer Camp
SQLite3 を利用する
/usr/include/sqlite3.h , /usr/lib/libsqlite3.dylib
•
[ツール]-[オプション]-[環境オプション]-[リモート
プロファイル]-[リモートパス]
– 必要に応じて、.h と .dylib をPAServerからインポート
データベースファイルの作成またはオープン
•
#include <sqlite3.h>
•
#pragma link ‘libsqlite3.dylib’
•
sqlite3_open: 作成またはオープン
テーブルの作成 および 行のINSERT
•
C++BuilderのUnicodeString型(1文字16ビット)を使用
– 一部、UTF8String型(UTF8Encode関数)を使用•
テーブルの存在確認
– システムテーブルにSELECT文を実行 • sqlite3_get_table / sqlite3_free_table•
テーブルの作成
– sqlite3_exec で CREATE TABLE文を実行
•
行のINSERT
– パラメータ付きクエリ (例: …=:param …) – char16_t版の関数を利用 • sqlite3_prepare16 / sqlite3_reset: SQL文の準備 • sqlite3_bind_text16: 文字列パラメータのセット • sqlite3_step: SQL文の実行SELECT文で行を検索
•
sqlite3_step とその戻り値で結果セットをイテレート
– SQLITE_ROW: 行がある – SQLITE_DONE: 行が無くなった•
sqlite3_column_text16 でカレント行の文字列を取得
– 戻り値の型を char16_t* 型にするのを忘れずに!! – C++BuilderのUnicodeString型に変換する17Th
Developer Camp
OpenCL を利用する
OpenCL on Snow Loepard, Lion
•
OpenCL とは?
–
出典: wikipedia
•
C++Builder XE2 がサポートしている OS X
OpenCL,OpenGL - /System/Library/Frameworks
•
[ツール]-[オプション]-[環境オプション]-[リモート
プロファイル]-[リモートパス]
– フレームワークとして OpenCL と OpenGL をインポート • OpenGL も忘れずに!! • 必要なヘッダとライブラリが利用できるようになりますGPUに送り込むカーネルコードを定義
•
コンパイル前のカーネルコードを文字列で定義
– C言語的なコードを書きます
• char配列でもファイルでもOK
計算用の初期値, GPUに接続, コードのコンパイル
•
アプリ(ホスト)側で計算用の初期値(配列)を準備
– この配列の値をGPU(デバイス)にコピーして渡します
•
1個のGPU(デバイス)に接続して、コードをコンパイル
GPUで使用するパラメータの作成
•
パラメータ用の配列をGPU内に作成して、そこに
アプリ(ホスト)側の配列をコピーする
•
計算結果を格納する配列をGPU内に作成
GPUで計算実行, 結果を取得して確認, 後始末
• GPU内の複数の計算ユニットで並列実行 – clFinish関数で実行終了を待ち合わせ • clEnqueueNDRangeKernel関数は非同期で実行される • 計算結果の配列をGPUからホストにコピー – clEnqueueReadBuffer関数で取得(読み込みが終わるまで待たされる) – 試しに、CPUの計算結果とGPUの計算結果を比較する • 作成したリソース群を解放17Th
Developer Camp
OS XでSOAPクライアントを
作成する
作成するサンプルシステムの目標・設計
•
SOAPサーバー
– Win32(またはWin64)のDelphi(またはC++Builder)で作成 – スタンドアロン(.exe)サーバー – ポート番号 8080 – TClientDataSetを返すメソッドをクライアントに公開 • ただし、TClientDataSetそのものではなく、XML化した文字列 (string)を採用•
SOAPクライアント
– MacOS X向けFireMonkeyアプリケーション • Delphi または C++Builder で作成 – WSDLからSOAPクライアント用プロキシを生成 • Windows版およびMacOS X版で共通!! – サーバーから取得したTClientDataSetをTStringGridに表示SOAPサーバー – データモジュールを用意
• [ファイル]-[新規作成]-[その他]-[Delphiプロジェクト]-[Webサービス]-[SOAP サーバーアプリケーション] – サンプルのSOPAサーバーインターフェースを作成 • サービス名は Employee • [ファイル]-[新規作成]-[その他]-[Delphiプロジェクト]-[Delphiファイル]-[データ モジュール] – TClientDataSet を配置 – [項目の設定]でフィールドを追加 • TIntegerField – FieldName は id • TWideStringField – FieldName は fullnameクライアントに公開するメソッド
• function getEmployeeDataSetXML: string; stdcall;
– interface と 実装class を編集 • [サービス名]Intf.pas
• [サービス名]Impl.pas
SOAPクライアント – WSDLをインポート
• [ファイル]-[新規作成]-[その他]-[Delphiプロジェクト]-[Webサービス]-[WSDLインポータ] – FireMonkeyフォームからクライアントプロキシユニットを参照 • FireMonkeyフォーム上に、以下を配置 – TButton – TClientDataSet – TStringGrid • [項目エディタ]で項目(TStringColumn)の追加 – Id – fullnameSOAPクライアント – TClientDataSetを復元
17Th Developer Camp
Update 4 で新たに追加された
iOS/FireMonkey向け
モバイルコネクタ
5
Update 4 をインストールすると…
• iOS FireMonkey(FreePascal)向けのモバイルコネクタがリポ ジトリフォルダに追加される – …¥RAD Studio¥9.0¥ObjRepos¥ja[en]¥dsrest¥connectors¥ • freepascal_ios42 フォルダ: iOS 4.2以降 • freepascal_ios50 フォルダ: iOS 5.0向け • DataSnap RESTサーバーのプロジェクト – DelphiのWebModule用ユニットのuses(例: WebModuleUnit1.pas) • Datasnap.DSProxyFreePascal_iOS– C++BuilderのWebModule用ユニットの.h(例: WebModuleUnit1.h)
• #include <Datasnap.DSProxyFreepascal_iOS.hpp>
– プロジェクトのproxyフォルダにモバイルコネクタ(必要があればコピー)
• サーバーを起動してWebブラウザまたは専用ツールを起動
モバイルコネクタをクライアントで利用する
• モバイルコネクタ用の.pasファイル 15個 をプロジェクトに追加 接続先を指定 デフォルトのプロトコルはhttp DataSnapサーバーへの接続 サーバーメソッドの呼び出し DataSnap特有の例外 DBXExceptionWindows上で実行すると…
• モバイルコネクタでサーバーメソッドの呼び出しをテストする
– Windows上では、メソッドの呼び出し時に例外が発生する仕様 • XcodeとiPhoneシミュレータでのデバッグが必須