MOSA Multi-OS Software Artists

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

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

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

2007-09-18

目次

  • 「りんご味Ruby」       第11回  藤本 尚邦
  • 藤本裕之のプログラミング夜話   #122
  • 高橋真人の「プログラミング指南」  第120回
  • 書籍紹介      ペーパープロトタイピング

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

今回は少し趣向を変えて、RubyのC言語用ライブラリとしての側面を見てみることにしましょう。

■ rubyコマンド

Rubyインタプリタの核心部分(ソースコードの解釈・実行やオブジェクトシステムなど)のバイナリコードは、すべて libruby というライブラリ(/usr/lib/libruby.dylib)の中に入っています。rubyコマンドも、その実体はlibrubyをリンクしたごく小さなC言語のプログラムです。以下は、ruby コマンドのソースプログラムであるRubyソースツリーの中のmain.cよりプリプロセッサマクロを削除して簡潔にしたCプログラムです。

----------------------------------------------- myruby.c ---
#include "ruby.h"
int main(int argc, char** argv)
{
 ruby_init();
 ruby_options(argc, argv);
 ruby_run();
 return 0;
}
-----------------------------------------------


このプログラムをmyruby.cというファイルに保存して、以下のようにターミナルでコンパイルしてlibrubyとリンクすればrubyコマンドが出来上がります。

 $ cc -I/usr/lib/ruby/1.8/universal-darwin8.0
      -lruby myruby.c -o myruby

実行してみましょう。

 $ ./myruby --version
 ruby 1.8.2 (2004-12-25) [universal-darwin8.0]
 $ ./myruby -e 'puts Time.now'
 Sat Sep 15 08:14:44 JST 2007


まさしくrubyコマンドそのものですね。

■ librubyを使ったhello world #1

C言語入門したときに書くhello worldプログラムは、文字列の出力にC言語の標準ライブラリに含まれているprintf関数を使うために、stdio.hをインクルードしています。librubyの関数を使う場合には、以下のようにruby.hファイルをインクルードします。

----------------------------------------------- rhello1.c ---
#include 
int main(int argc, char** argv)
{
 int num_args;
 VALUE args[1];

 ruby_init();
 rb_eval_string("$stdout.puts 'hello,world'");
 return 0;
}
-----------------------------------------------


さきほどと同じようにコンパイル・リンクして実行してみましょう。

 $ cc -I/usr/lib/ruby/1.8/universal-darwin8.0
      -lruby rhello1.c -o rhello1
 $ ./rhello1
 hello, world


このプログラムは、Rubyインタープリタを初期化してから”$stdout.puts ‘hello,world’” という文字列をRubyプログラムとして評価(実行)しています。rb_eval_stringは任意のC文字列をRubyプログラムとしてパースして評価する関数です。rb_eval_stringがあれば、どんなRubyプログラムでもCプログラムから実行できるわけですから、他に何も必要ないような気がするかもしれませんね。

しかし、これだけではC言語のデータとRubyのオブジェクトを相互に変換するのが大変ですし、せっかくC言語で書いているので、Rubyプログラムの構文解析をバイパスして直接Rubyのオブジェクトシステムを使いたいところです。

ということで、librubyにはRubyのオブジェクトシステムと直接やりとりをして、Rubyオブジェクトの生成・クラスやメソッドなどの定義・メソッド呼出しするためのAPIがたくさん備わっています。そのようなAPIを使って rhelloを書き換えてみましょう。

----------------------------------------------- rhello2.c ---
#include 
int main(int argc, char** argv)
{
 int num_args;
 VALUE args[1];

 ruby_init();
 args[0] = rb_str_new2("hello, world");
 num_args = sizeof(args) / sizeof(VALUE);
 rb_io_puts(num_args, args, rb_stdout);
 return 0;
}
-----------------------------------------------


さきほどと同様にコンパイル・リンクすれば動くはずです。
このプログラムは:

 ・ruby_init関数により、Rubyインタープリタを初期化
 ・rb_str_new2関数により、Ruby文字列を生成
 ・rb_io_puts関数により、標準出力(rb_stdout)に文字列を出力

しています。これをRubyで書くと

$stdout.puts "hello,world"

となります。rb_str_new2は、Ruby文字列を生成する関数群の1つで、Stringクラスのクラスメソッドに相当します。rb_io_putsはIOクラスのインスタンスメソッドに相当します。

■ まとめ

今回は簡単な紹介しかできませんでしたが、libruby は Ruby のオブジェクトシステムにアクセスするためのよくできたAPIになっています。実際、Rubyに標準で含まれているString,Array,Hashなどのクラスは、librubyのAPIを使ってC言語で記述されたRubyクラスの定義として素直に読み下すことができます。

拡張ライブラリの作り方も基本的に同じで、C言語に馴染んでいる方であればlibrubyのAPIを使って簡単に書くことできます。これもいずれ紹介したいと思います。今回の話に興味を持った方は、Rubyのソースに含まれているREADME.EXT.ja というテキストファイルを読むとよいでしょう。

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

 ようやく涼しくなってきてやれやれといったところである。さてツールバー話の2回目、実際にウインドウにツールバーを出すためのコードの書き方である。おっと、例によって info.plist に定義されている NSPrincipalClass を我らが(我らがってほどのもんでもないが) MyApplicarion に変更するのを忘れずに。
 これから作るサンプルでは起動時に表示されるウインドウに即ツールバーを表示したいので、MyApplication のスーパークラスである NSApplication のfinishLaunching をオーバーライドしてここでツールバーを作り込むわけだが、NSToolbar というオブジェクトはホントに単なる「バー」であって、真の主役はその中身である NSToolbarItem であるということを体験してもらおう。finishLaunching を以下のようにコーディングする。

- (void)finishLaunching{
    [super finishLaunching];
    NSToolbar*     aToolBar =
         [[[NSToolbar alloc] initWithIdentifier:@"MOSA_SAMPLE"] autorelease];
    [window setToolbar:aToolBar];
}


 最初にスーパークラスの同じメソッドを呼んでいるのはお約束、スーパークラスがここでやっている処理を省くと何にも起こらないので忘れないように。次に「MOSA_SAMPLE」という identifier を指定して aToolBar というオブジェクトを生成し、これをウインドウにセットする。「ウインドウにツールバーをくっつける」だけでいいならやることはこんだけなのだ。是非ビルドして実行してみてもらいたい。
 ウインドウのタイトルバーの下にお馴染のものよりはかなり細身のツールバーが出るはずだ。こんなのはツールバーぢゃないって? いやいやこれがツールバーなのである。その証拠にウインドウの右肩にある小さいボタン(これの正式名称が分からない。誰か知りません?)を押すと隠れ、もう一回押すと出てくるでしょ?

 まぁしかしこれでは確かに間違いなくツールバーだか何の役にも立たない。ここに各種の機能を持った NSToolbarItem が並んで初めて「使えるツールバー」になるわけだ。ここまで分かっていただいたところで前回の最後にMyApplication.h に定義したインスタンス変数、

NSMutableDictionary*          toolbarItems;


というのを何に使うのかを説明しよう。ツールバーは、自身に登録されているアイテムのインスタンスを必要に応じてデリゲートから取得して表示する。つまりツールバー自身はそのアイテムのインスタンスを全く保持していないのね。そんなもん自分で持ってりゃいいぢゃないかと思うかもしれないけど、おいおい説明するようにいろいろと理由があるのだ。
 で、デリゲート側としては要求されるたんびにいちいち新しくそのアイテムを作るのはタイヘンなので(いやプログラムだから一度コーディングしちゃえば別にタイヘンぢゃないけど時間というコストがかかるので)、最初に全部作ってこの辞書オブジェクトに登録しておき、要求があったらこっから引っ張り出してコピーして渡すわけである。……ほんぢゃひとつ新しいツールバーアイテムを作ってこの辞書に登録してみよう。上の finishLounching の、setToolbar: のコールの前に以下のコードを追加する。

    toolbarItems = [[NSMutableDictionary alloc] initWithCapacity:0];
    NSToolbarItem* catItem =
         [[[NSToolbarItem alloc] initWithItemIdentifier:@"Cat"] autorelease];
    [catItem setLabel:@"Cat"];
    [catItem setPaletteLabel:@"Cat"];
    [catItem setToolTip:@"Type 'Cat'"];
    [catItem setImage:[NSImage imageNamed:@"Cat.tif"]];
    [catItem setTarget:self];
    [catItem setAction:@selector(typeCat:)];
    [toolbarItems setObject:catItem forKey:@"Cat"];
    [aToolBar setDelegate:self];


 ここでは「Cat」という identifierで、「Cat.tif」というイメージをアイコンとし、「Cat」というラベルとパレットラベル(カスタマイズパレットに出るラベル)、「Type ‘Cat’」というガイダンス(マウスを上に持っていくと出てくる説明)を持ち、押されるとこのオブジェクト(MyApplication)に
typeCat: というメッセージを送ってくる NSToolbarItemを作成して辞書に登録している。そして最後にこのオブジェクトをツールバーのデリゲートに。…
…え、ツールバーにアイテムを登録しないでいいのかって? それについてはまた次回。
                            (2007_09_13)

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

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

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

 こんにちは、高橋真人です。早速始めます。
 前回の記事で、PPxにおけるコマンドハンドリングの動作原理については解説しないと言いましたが、今テーマにしているメニューハンドリングに関しましては補足が必要と思いますので、少し触れておきます。
 まず、今までにも何度か触れてきたようにCarbonイベントモデルにおけるイベントは、クラス(級)とカインド(種類)の組み合わせで出来ています。
 メニューにも絡んでくるコマンド関連のイベントには、以下の2つがあります。

・コマンドイベント
 クラス: kEventClassCommand/カインド: kEventCommandProcess
・メニュー更新イベント
 クラス: kEventClassCommand/カインド: kEventCommandUpdateStatus

 コマンドイベントはメニューを選択したり、コマンドを割り当ててあるボタンをクリックすることで発せられますが、メニュー更新イベントはメニューが表示される必要が生じたときに発生します。ユーザーがマウスクリックで実際にメニューバーをクリックした時に限らず、キーボードからメニュー選択のショートカットを押した場合も、この「必要が生じた時」に該当します。
 つまり、Carbonイベントモデルにおけるメニューハンドリングの基本は、こ
れらのイベントをいかに処理するかということだということを踏まえておいてください。

 それでは、話の続きを続けます。
 前回までの話で、SpecificMenuCommandEnableDoerの役割は分かりましたでしょうか? もしかしたら「単にメニューを有効にするだけなら、InterfaceBuilderで有効にしておけばいいだけでは?」と思われた方もいるかもしれません。
 確かに、特定のメニュー項目が常時有効になっていればよいということであれば、そういう考え方もありでしょう。(ただし、必ずしもうまくは行くとは限りませんが)
 しかし、前にもお話したようにメニューというのは状況に応じて有効、無効を切り替えたり、場合によっては表示内容が変化したりするものです。そのためにはどうしても「動的な処理」が必要になるわけです。
 今はSpecificMenuCommandEnableDoerがアプリケーション
にある(つまり、MyApplicationの継承元の1つであるということ)ため、常時コマンドのアップデートのためのイベントを受けられる形になっています。従って、終了メニューは常に有効な状態のままであるわけです。
 あとで触れますが、MyWindowの継承元の一つになっているSpecificMenuCommandDoerも、同じようにSpecificMenuCommandEnableDoer<
kHICommandClose>を継承しているため、このMyWindowのインスタンスが存在す
る時、つまりウインドウが表示されている時は、kHICommandCloseのメニュー更新イベントがこのウインドウに処理されて、Closeメニューアイテムが有効になるのです。
 ウインドウが1つもない場合には、kHICommandCloseのメニュー更新イベントはMyApplicationに処理されますが、MyApplicationにはkHICommandCloseのメ
ニュー更新イベントを処理するためのハンドラは組み込まれていませんから、
CommandConverterに処理が委ねられます。
 前回CommandConverterは裏方だと言いましたが、実は「いずれのイベントハンドラにも処理されないメニュー更新イベント」は、最終的にCommandConverterに引き取られることになります。CommandConverterに引き取られたメニュー更新イベントのメニュー項目は無効にされます。
 このように、メニューの有効・無効についてを動的に処理する仕組みの一つとして、SpecificMenuCommandEnableDoerはあるわけです。
 このメニューを処理するために用意された諸クラスの仕組みが分かるようになると、例えば何もウインドウが出ていない時には有効になっているメニュー項目がウインドウが出ている時には無効になるような挙動を実現する(つまり
SpecificMenuCommandEnableDoerの逆の動き)ことも可能です。
 もともとのPPxのクラスにはそのような機能を持ったものはありませんが、PPxのオブジェクト指向の仕組みを利用すれば、さほど手間をかけずにそのようなクラスを作ることも可能ですので、後ほど実際に試してみたいと思います。

 さて話をコードの解説に戻して、MyApplicationが継承している最後のクラス、SpecificMenuCommandDoerを見てみます。
 先ほども触れましたように、SpecificMenuCommandDoerは、SpecificMenuCommandEnableDoerを継承していますが、同時にSpecificCommandDoerをも継承しています。クラスの冒頭の宣言部は以下のようになっています。

template 
class     SpecificMenuCommandDoer : public SpecificCommandDoer,
                                 public SpecificMenuCommandEnableDoer {


 MyApplicationでは、テンプレート引数はkHICommandCloseですので、コンパイル時にはこのTCommandIDの部分がkHICommandCloseとなります。
 SpecificCommandDoerは、前にもあったようにユーザーのメニュー選択に対して反応するクラスです。しかし、メニューを選択するためにはそもそもメニュー項目が選択可能になっている、つまり有効化されている必要があります。
 こういった必要性から、メニューコマンドの処理と、メニュー項目の更新のクラスをひとまとめにしてクラス化したのがSpecificMenuCommandDoerです。
 MyApplicationが持っている関数のうち、DoSpecificCommand()がメニュー選択によって発生したコマンドのイベントを処理するわけです。

【訂正】
連載の第118回目に掲載したコードのうち、以下の部分が欠落しておりました。お詫びして補足します。

====================== MyWindow.h =========================

#include 
#include 

class MyWindow : public PPx::Window,
                 public PPx::CommandConverter,
                 public PPx::SpecificMenuCommandDoer
{
protected:          <---この行が抜けておりました
    virtual OSStatus   DoSpecificCommand(
                            PPx::CommandIDType,
                            PPx::SysCarbonEvent&          ioEvent);

書籍紹介           ペーパープロトタイピング

 解説担当:高橋政明
ペーパープロトタイピング
         最適なユーザインタフェースを効率よくデザインする
 Carolyn Snyder 著
 黒須 正明 監訳
 オーム社 ISBN 4-274-06566-9  3,360円

 今年のWWDCのUIのセッションで絶賛されていた本の日本語版です。実はちょうど一年程前に購入したものの少し読んだだけで放置していました。

 ペーパープロトタイプとはソフトウェアでもwebデザインでも(ハードウェアの操作さえも)「紙」に書いてユーザーに使ってもらい機能などを評価するユーザビリティテストの手法です。紙を使うことから短時間で手軽におこなえ何よりまったくプログラミング作業が不要で実際の製品(試作品)が存在しない状態からテストをはじめることができます。
 反面チームで行う大掛かりなユーザビリティ調査の側面を持っています。開発者が個人で手軽にやってみるわけにはいかないそうです。(私はこの点期待はずれだったので読み進めずにいました)

 本書はペーパープロトタイプについて掘り下げ、事細かに具体的に解説しています。このことは「大掛かりなユーザビリティ調査」とは無縁の個人技術者には『自分とは接点がない』と思わせます。しかし、読んでみるとユーザビリティに関する多くの考え方から人間関係の機微にまで話が及び得るものも多いはずです。

 残念ながら私にはペーパープロトタイピングを実践する機会はまだありませんが、ソフトウェアのプロトタイプやwebのプロトタイプはしばしば作ります。Interface Builder.appが如何に手軽にUI部品をレイアウトでき即テストランすることができても、一度で完全なものは作れず試作時も修正時も機能だけでなく見かけにもついこだわってしまうため手間と時間がかかりがちです。
また第三者にプロトタイプの評価を求めると、こちらの意図する本題とは別の些末な指摘に終始しがっかりすることが少なくありませんでした。
 この点ラフな手書きのペーパープロトタイプが効率良く本質に迫れるとの説明には説得力を感じました。

 副題は「最適なユーザインタフェースを効率よくデザインする」ではありますが、最適なUIがどのようなものであるかを説明した本ではありません。『操作してみればすぐにわかる』類の誤った設計をコストをかけて作ってしまう前に発見し改善するための具体的ノウハウ満載の本です。ユーザー中心設計を考えるにも最適な教科書と言えそうです。

▼出版社のweb (詳細目次と翻訳済みテンプレートpdfあり)
http://ssl.ohmsha.co.jp/cgi-bin/menu.cgi?ISBN=4-274-06566-9

◇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.