MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSA Developer News[MOSADeN=モサ伝]第317号

2008-10-28

目次

  • りんご味Ruby         第34回  藤本 尚邦
  • 藤本裕之のプログラミング夜話   #147
  • 高橋真人の「プログラミング指南」  第145回

りんご味Ruby   第34回  藤本 尚邦

これまでに、しばしば、DSL(Domain-Specific Language=ドメイン固有言語)や構文糖衣(syntax sugar)を作りやすいのがRubyの重要な特徴のひとつだということを書いてきました。前回、Mechanizeの構文糖衣について書いたので、今回はその流れで、Rubyでの構文糖衣やDSLの作り方について書くことにします。

■ (例) アクセッサ宣言の構文糖衣を追加する

さて、Objective-C 2.0 では、宣言プロパティという新しい構文が導入されました。例として、TODOリストのアイテムのクラスを定義するとします。Objective-C 2.0で宣言プロパティを使うと以下のようなコードになるでしょうか。

----------------------------------------------- TodoItem.m ---
@interface TodoItem : NSObject {
 NSString* summary;
 int       level;
 BOOL      done;
}
- (id)initWithSummary:(NSString*)str;
@property(readonly)  NSString* summary;
@property            int       level;
@property            BOOL      isDone;
@end

@implementation TodoItem
- (id)initWithSummary:(NSString*)str {
   if ((self = [super init])) {
       summary = str;
       level = 0;
       done = NO;
   }
   return self;
}
@synthesize summary;
@synthesize level;
@synthesize isDone = done;
@end
-----------------------------------------------


インターフェース部の@propertyでは、外部に対してプロパティに対するアクセサの名前・種類(読み書き属性)・型などを宣言し、実装部の@synthesizeでは、コンパイル時にアクセッサメソッドの定義を(自動)生成する指示を記述しています。

この宣言プロパティに近いものとして、Rubyには attr_reader, attr_writer,attr_accessor という構文糖衣があらかじめ用意されています(本当は構文ではなくメソッド呼出による疑似構文なのですがそれについては後述)。TodoItemクラスの定義をRubyで書くと

----------------------------------------------- TodoItem.rb ---
class TodoItem
 attr_reader   :summary
 attr_accessor :level, :done

 def initialize(summary)
   @summary = summary
   @level = 0
   @done = false
 end
end
-----------------------------------------------

というような感じになります。この attr_reader, attr_accesor は、クラスの定義のときに、指定された名前のインスタンス変数へのアクセッサメソッドを自動生成します。例えば attr_accessor :level のところは以下のようなコードを記述したのと同じことになります。

 

# reader (Objective-C用語ではgetterに相当)
 def level
   @level
 end

 # writer (Objective-C用語ではsetterに相当)
 def level=(new_value)
   @level = new_value
 end


Objective-Cを含め、よく使われているプログラミング言語の多くは構文を拡張するプログラムを書く手段を持っていないので、構文についてのちょっとしたアイディアを持っていても、処理系に新しく実装されるのを待ったり、開発チームに要望したり、あるいは自ら処理系に実装するしかありません。

(補足 – 構文を拡張する手段を持つプログラミング言語の有名どころは、Common Lisp や Scheme などのLISP系言語でしょう)

Rubyにも、本物の構文そのものを拡張するプログラムを書く手段はありません。その意味ではObjective-Cなどと同様なのですが、attr_reader のような疑似構文(つまり宣言プロパティ程度の構文)はメソッドとして実装することができます。つまり、Rubyプログラマは、疑似的な構文糖衣を実装してライブラリなどに含めることができるわけです。

例として、真偽値を返すアクセッサを宣言するための疑似構文attr_predicateを定義してみましょう。Rubyプログラミングでは、真偽値を返すメソッド名の末尾に`?’を付ける慣習があるわけですが、残念ながらそれに対応したアクセッサ宣言の疑似構文がRubyには用意されていません。もし今すぐどうしてもそれが欲しいと思ったら、すぐに自分で作ってみることができるわけです。

attr_predicate の仕様は、クラス定義の中で

 attr_predicate :done, :important

のように、真偽値を返すreaderの名前(複数可)を宣言すると

 

def done?
   @done ? true : false
 end

 def important?
   @important ? true : false
 end


のように、`?’の付いた名前を持ち真偽値型を返すアクセッサメソッドの定義が生成されるということにします。attr_prediateを組み込むプログラムは以下のようになります。

Module.module_eval do
 def attr_predicate(*names)
   names.each do |name|
     define_method("#{name}?") do
       instance_variable_get("@#{name}") ? true : false
     end
   end
 end
end


このコードをライブラリなどに含めておくと、そのライブラリをロードしたあとは、クラス定義の中で attr_predicate を使ってアクセッサを宣言することができるようになります。このコードの詳細については次回説明します。

藤本裕之のプログラミング夜話 #147

 前置きなしで話を続ける。「ゲーム作って一発当てる可能性」の話ね。あ,期待されると申し訳ないので最初に断っておくが,「これこれこんなゲームを作れば絶対当たってウハウハだぜ」という話ではない。この世界にエントロピーの増大以外に絶対なんてものはないのである。わかんないヒトのために言っておくと「エントロピーの増大」というのはつまり「どんどんどんどんわけがわかんなくなっていく」ということだからね。

 ……オレが検討したいのはそういうではなくて「ゲームを作って一発当ててあとの人生は悠々自適のヒダリウチワ」みたいな。ま,まぁそこまで言わなくてもそれなりに安定した生活というものを設計できるのか,そういう可能性はどのくらいあるだろうか,という話である。

 まず最初に,ちょっと本論からズレるが(と言ってもはやどこが本論なんだかオレにもよくわからないんだが)「ゲーム」というもの,この文脈でいうところの「コンピュータ・ゲーム」というものをきっちり解析しておきたい。

 世代的に近い多くのヒトがそうではないかと思うが,オレが最初に触れたコンピュータゲームは5mm四方くらいの光る矩形をパドルで打ち合うスカッシュみたいなもんだった。いやみたいなもんというかそういう名前がついていたかも知れない。あれはつまり,現実にあるゲーム(スポーツを含む)をコンピュータ上で実現したもので,Mac OS Xに付属しているチェスや初期の
Windowsの呼び物(笑)だったリバージ(なぜか「オセロ」とは呼ばない),数多あるトランプ・ゲーム,麻雀ゲーム,ゴルフや野球,サッカー,カーレースなどを模すものもこの類である。これを大ざっぱに第1群とする。

 これらとは別に,最初からコンピュータ上で遊ぶものとして設定,ルール,意匠その他イチから作るというものがある。初期のもので言えばブロック崩し(ああいう現実の遊びはない),インベーダゲーム,スーパーマリオ……。現在でも主にゲーム専門機上で定番化しているドラクエ,ファイナルファンタジー(他に何があるの? 白状するとオレそのテのゲームはやったことがない
のだ)などがこの類と言えよう。これが第2群ね。

 ちょっと考えれば……いや,やった経験があるヒトにとっては考えるまでもないことだと思うが,第1群に類するゲームをこれから作ってゲーム業界に参入……というシナリオには無理がある。ルールや設定など,ゲームの基礎部分が変わらない以上,商品価値の優劣はどうしても意匠,グラフィックの見事さや動きのリアリティ,音楽などで計られることになるからで,ハワード・ヒューズが映画監督を始めたみたいなシチュエーション以外では現実的ではないからだ。

 第2群にはまだ可能性がある。1980年代末期に一世を風靡した「テトリス」を思い起こして欲しい。旧ソビエト連邦の科学者アレクセイ・パジトノフ博士他3名によって開発されたこのゲームは,PCのみならずアーケードから家庭用ゲーム機,果ては携帯電話にまで移植された。それでもともとの開発者であるパジトノフ博士らがどのくらい潤ったのよ,と聞かれるとれはオレにも判らな
いが(なにしろ冷戦終結前だしね),あの種のゲームであれば低資本で開発でき,うまく行けば市場を席巻できるかも知れない。

 もちろん(実際このテトリスと類似品の間で裁判が争われたように),ゲームのルールには著作権は認められない。だからすぐに金に飽かして,例えば鳥山明のキャラクターデザイン,X-JAPANの音楽,渡辺久美子の声とかを使って類似品を作るメーカーが現れるかも知れない。まぁでもそういうメーカーも,ユーザから「真似し」とか「簒奪者」みたいな陰グチたたかれるよりはこっちに原案料みたいなものを払うことを選ぶであろうし……それを売り切りでなく歩合と決めておけば逆に鳥山明,X-JAPAN,渡辺久美子のおかげでこっちが潤うということになるわけでと,取らぬ狸の皮算用もあながち夢とは言いきれない。

 と,なると要はアイディアであるわけだが……え? 鳥山明とX-JAPANはいいが渡辺久美子って誰だって? それはあなた「ケロロ軍曹」を演ってる声優のひとであります。では次回はそのアイディアについて。
(以下次回 2008_10_24)                       

高橋真人の「プログラミング指南」第145回

プログラマのためのオブジェクト指向再入門(51)

〜XcodeによるPowerPlant X入門(34)〜

 こんにちは、高橋真人です。
 さて、“アタッチされているViewを探す”という、連載141回目で掲げたアタッチメントを外すための手順の第1ステップの説明がずいぶん長くなってしまいました。前々回と前回での解説をまとめますと、HIViewのFindViewByID()に渡す引数は、ObjectIDTという型で表されるViewの識別子だということになります。
 しかしながら、今回のコード例(連載140回目)に見られるように、引数には単に100などの整数値を与えるだけでよく、ここではObjectIDTという型を特段意識する必要がありません。前回までの説明では「整数を表す型」をいくつでも個別に作れる方法を説明をしましたが、その時の例のような関数の多重定義(オーバーロード)でもしない限り、これら新たに作った型も“普通の整数として”使えるところがこの仕組みの便利なところです。

 そんなこんなで、ようやくViewの検索の説明が済んだのですが、実際のコードではさらにもう一段階の処理が行われています。

  

 MyView *view = PPx::SafeDynamicCast(
                       GetContentView()->FindViewByID(100));

 ルートのViewに対してFindViewByID()を呼んで、アタッチメントの付けられているViewを探すわけですが、その結果として返された値をSafeDynamicCastという関数に渡しています。この関数が行っているのは、ダイナミックキャストを行い、その成否をチェックするということです。
 ダイナミックキャストというのは聞き慣れない方も多いかもしれません。もともとC言語に備わっているキャストという仕組みは、型の変換を行うものであることをご存知と思いますが、型変換にはいくつかの種類があるのです。
 C++においては、「すべての型変換をを一つのキャストで行ってしまうのは乱暴だ」ということから、役割ごとに分けています。const_cast、static_cast、dynamic_cast、reinterpret_castの4つです。これらすべてを解説するのは今の趣旨ではないので、今回はdynamic_castのみに絞ります。
 上のコードを例にしますと、FindViewByID()が返すのはViewへのポインタです。ところが、そのポインタが指しているのがPPx::Viewのインスタンスだとは限らないわけです。つまり、PPx::Viewを継承元に持つ別のサブクラスのインスタンスである可能性もあるわけです。
 そこで、“PPx::View *”を“MyView *”に変換する必要が出てきます。従来形式のキャストですと、仮にPPx::View *の指している実体がMyViewやそのサブクラスのインスタンスではなくても、有無を言わさず変換してしまいます。そうなると、キャストによって得られたポインタを使ってMyViewにしか存在しないメンバ関数を呼びだした場合にまずいことが起きてしまいます。普通
はアプリのクラッシュでしょう。
 そこでdynamic_castです。これを使って書くとコードは以下のようになります。

PPx::View *view = GetContentView()->FindViewByID(100);
MyView *myView = dynamic_cast(view);


 ちょっとゴツい見た目をしていますが、dynamic_cast<...>(…)というのがC++で新たに加えられたキャストの書き方です。キャストは目立った方がよい、というのが設計意図なので、あえてこのような見た目をしています。
 さて、上記のようにdynamic_castを使ってキャストを行った場合、たとえばviewがPPx::TextViewのインスタンスだったとしますと、myViewにはNULLが入ります。つまりキャスト自身が型判断も行い、要求している型変換に合わない場合にはNULLを返すようになっているのです。
 使い方としては、dynamic_castが返す値がNULLであるかどうかをチェックすることで、dynamic_castが成功したかどうかを判断します。PPxでは、SafeDynamicCast関数がdynamic_castでキャストして、もしキャストが失敗したら例外を投げる仕組みになっています。
 ところで、オリジナルのPPにも似たような仕組みを実現するマクロがありました。FindPaneByID_()というのがそれです。白状しますと、きょうの今まで、私は両者を全く同じ感覚で使っていたのです。しかし、今回の記事を書くに当たって改めてコードを読んでみると、PPxのSafeDynamicCastでは、キャストの失敗に関してしか例外を投げないということに気付きました。
 PPのFindPaneByID_マクロでは、Pane(*注)の検索に失敗した場合にも例外を投げてくれます。よって今回のコードでは、SafeDynamicCastが返した値に対しても、NULLでないかをチェックする必要があったのでした。
 厳密に考えれば、NULLを返すのはFindViewByID()なので、SafeDynamicCastに渡す以前にNULLかどうかのチェックをすべきだとは思いますが、SafeDynamicCastはNULLが渡された場合には、そのままNULLを返すようになっているため、“後”のチェックでも実質的には問題ありません。

注:以前の連載でも触れましたがオリジナルPPでは、ViewのスーパークラスはPaneです。Viewは階層構造的にViewまたはPaneを子に持つことができます。
 
過去記事の訂正です。連載142回目の中ごろ、

 本題に戻ります。PPx::WindowのGetWindowContentView()という関数により…

という部分。
【誤】GetWindowContentView()
【正】GetContentView()

◇MOSAからのお知らせと編集後記は割愛します◇

 

 MOSA Developer News   略称[MOSADeN=モサ伝]
        配信停止 mailto:mosaden-ml@mosa.gr.jp
 記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
      記事投稿受付 http://www.mosa.gr.jp/?page_id=850
Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
はそれぞれ各社の商標ならびに登録商標です。
このメールの再配信、および掲載された記事の無断転載を禁じます。
特定非営利活動法人MOSA  http://www.mosa.gr.jp/
Copyright (C)2007 MOSA. All rights reserved.