MOSA Multi-OS Software Artists

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

プログラマーに興味がある方なら誰でも入会いただけます。
MOSA Multi-OS Software Artists
===SINCE 1995===

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

MOSADenバックナンバー 2007年9月発行分

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

    2007-09-25

    目次

    • 「「Wonderful Server Life」    第55回   田畑 英和
    • 小池邦人のCarbon視点でCocoa探求
    • ターミナルの向こうから      第10回  海上 忍 

    「Wonderful Server Life」  第55回  田畑 英和

      〜NetBoot編〜

     さて、今回からNetBootの解説を始めたいと思います。まずはNetBootとは何かというところから始めていきましょう。通常コンピュータを起動するには、そのコンピュータのハードディスク(外付けディスクの場合もありますね)にあらかじめOSをインストールしておき、そこから起動(Boot)します。
     NetBootではその名前のとおり、ネットワークを利用したBootを行います。コンピュータに直接インストールしたOSを使用するのではなく、NetBootサーバ上に用意しておいたOSから、ネットワークを経由してコンピュータを起動します。これがNetBootなのです。

    ◇NetBoot環境の準備
     NetBootを利用するには、NetBootサーバとして使用するMac OS X Serverをインストールしたサーバが最低1台必要になります。そしてサーバにNetBootクライアントで使用するOSを用意します。このクライアント用のOSはディスクイメージとして用意します。OSのディスクイメージは標準構成でも数ギガ程度の容量になるため、サーバのディスクには十分な空き容量が必要になります。
     NetBootクライアントが複数あった場合でも、各クライアントはサーバ上の同じディスクイメージから起動することができます。また、異なる内容のシステムを使用したい場合は、サーバ上に複数のOSのディスクイメージを配置することもできます。
     例えば、Intel搭載MacとPPC搭載Macが混在した環境でNetBootを行うような場合は、サーバ上にIntel用のOSディスクイメージとPPC用のディスクイメージを配置するといった使い方ができます。Intel MacをNetBootする場合ですが、NetBootサーバがPPCのMacを使用している場合でも、Intel用のディスクイメージを用意することにより運用が可能になります。
     ディスクイメージの作成には、サーバ管理ツールの「システムイメージユーティリティ」を使用します。具体的なイメージの作成方法については次回解説する予定です。
     NetBootサーバを構築するには、NetBootサービスを使用するだけでなく以下のようないくつかのサービスも必要になります。

    ・DHCP
     NetBootクライアントはDHCPを使ってIPアドレスを取得するためDHCPサービスが必要になります。

    ・NFS
     サーバ上のOSのディスクイメージはNFSを使ってNetBootクライアントに公開します。NFSが利用できない場合はHTTPを使用することもできますが、HTTPよりもNFSのほうがパフォーマンスに優れているため、通常はNFSを利用します。

    ・AFP
     必ずしも必要ではないのですが、シャドウファイルと呼ばれるデータをサーバ上で管理するにはAFPサービスが必要になります。

     以上がNetBootサーバを構築するために必要になるサービスです。さらにこれらのサービスに加えNetBoot環境では通常、ネットワークユーザおよびネットワークホームを使用します。そこで、別途Open Directoryのマスターとネットワークホームのためのファイルサーバが必要になります。
     すべてのサービスを1台のサーバで運用することもできなくはないですが、これだけのサービスを1台のサーバで運用するよりも、複数のサーバを使用して負荷を分散させたほうが、システム全体のパフォーマンスを向上させることが出来ます。

     次にネットワーク環境ですが、NetBoot環境ではOSのデータをネットワークを経由してやりとりするため、なるべく高速なネットワークが必要になります。できればギガビットのスイッチを使用するのがよいでしょう。

     OSのディスクイメージを作成するための準備ですが、まずはNetBootのクライアントとして使用するMacを通常どおりセットアップしてください。既存のシステムからNetBoot用のディスクイメージを作成することができます。
     このとき注意が必要なのは、NetBootでは複数のクライアントが共通のディスクイメージを使用するということです。つまり最初にセットアップしたシステムが複数のNetBootクライアントで使用されるということです。ですので、セットアップのさいには特定のクライアントに依存するような設定はやらないようにしましょう。たとえばアプリケーションをインストールするさいに、1台のコンピュータでしか使用できないようなライセンスのシリアルナンバーを設定すると運用上問題が発生します。

     それでは、次回は具体的なOSのディスクイメージの作成方法について解説する予定です。
    つづく 
                                   

    小池邦人のCarbon視点でCocoa探求(2007/09/21)

    〜 File’s Owner 〜

    今回も引き続き、Cocoaプロジェクトに含まれるMainMenu.nibとMyDocument.nibの中身を調査します。File’s OwnerとFirst Responderアイコンは具体的にどんな役割を受け持っているのでしょうか?

    CarbonプログラマがInterface BuilderでCocoaプロジェクト用のNibファイルを編集しようとした時、最初に目に入るのがFile’s OwnerとFirst Responderの2つのアイコンです。
    Carbon用Nibファイルにはそのようなアイコンはありませんので「これは何だ?」というわけで、入門書を引っ張り出して調べ始めます。ところが、筆者もそうでしたが、これらのアイコンの名称が混乱を招き(First Responderは別の意味で検索されたりする)明確な答えが見つからずに悩むことになります。

    Interface BuilderのInstancesタブに並んでいるアイコンのほとんどは、それぞれが何らかのクラスから生成されたオブジェクトを表しており、アプリケーションにNibファイルが読み込まれた時にインスタンスが生成され実体化します。WindowとMainMenuアイコンは、それぞれがNSWindowクラスとNSMenuクラスのオブジェクト(View Object)です。ところが、File’s OwnerやFirstResponderはそれ自体からオブジェクト(インスタンス)が生成されることはなく、まったく別の役割を受け持っています。まず先んじて、File’s Ownerについて詳しく調べてみましょう。

    File’s Ownerは、Nibファイルが読み込まれた時に、その中で定義されているオブジェクトと、既にメモリ内に確保されている別オブジェクト「OwnerObject」とを接続する(関係づける)ために用意されています。つまり重要なことは、Nibファイルが読み込まれても、File’s Ownerに設定してあるクラスのオブジェクトは生成されないと言う点です。例えば、NSBundleクラスには、Nibファイルを読み込み、そこに定義されているすべてのオブジェクトを生成するloadNibNamedと言うクラスメソッドがあります。

    (BOOL)loadNibNamed:(NSString *)nibName owner:(id)owner;

    これを使い、自作MyObjectクラスのinitメソッドにおいて、MyObject.nibに定義されているオブジェクトをすべて呼び出す(生成する)には、以下のように記述します。

    - (id)init
    {
       self = [super init];
       [NSBundle loadNibNamed:@"About" owner:self];
       return self;
    }
    


    selfは、作成(alloc)されたMyObjectクラスのオブジェクト自身です。そしてメッセージとしてOwner Objectを渡している箇所が、owner:selfの部分です。

    selfは先んじて生成されており、MyObject.nib内にそのオブジェクトを定義する必要はありません。続いて、MyObjectのメソッドからMyObject.nib内に定義してあるオブジェクト(ウィンドウやコントローラ)を参照しなければいけないとすると、Interface Builderで2つのオブジェクト間を接続する(線を引く)必要があります。ところが、Nibファイル側にはMyObjectアイコンがありませんので、物理的に接続することは不可能となります。

    そのため、その代用として使われるのがFile’s Ownerアイコンなのです。上記の例では、InspectorでFile’s OwnerのCustum ClassにMyObjectクラスを指定してから、他のアイコンと同様にオブジェクト間の接続に利用します。アイコンがアプリケーション形状であることから分かるように、 MainMenu.nibのFile’s OwnerのCustum ClassにはNSApplicationが割り当てられています。つまり、File’s Ownerアイコンがアプリケーションが生成したNSApplicationオブジェクトの代用として使われるわけです。

    例を見てみます。MainMenuから「Quit NewApplication」アイテムを選び、InspectorでConnectionsのTraget/Actionを確認すると、アクションとしてterminate:がアサインされています。terminate:はNSApplicationのメソッドであり、アプリケーションを終了させる処理を実行します。Carbonで言えば、QuitApplicationEventLoop()やExitToShell()に相当します。InterfaceBuilderでオブジェクトの接続を見ると「Quit NewApplication」アイテムからFile’s Ownerアイコンへと線が引かれていることが分かります。

    それではと言うことで、試しにCustum ClassのNSApplicationをNSObjectなどに変更してみます。すると、何度も「接続が切れるぞ!」というアラートが表示されますが、その警告を無視して実行すると、File’s Ownerのアイコン形状がアプリケーションから青色立方体に変わります。この状態でアプリケーションを起動すると、もはやNSApplicationオブジェクトとの接続関係が消えてしまっているので、Quitメニューは働かず、アプリケーションを終了させることができません。

    Interface Builderでは、こうしたリンク切れの発生(不注意による)に対しては厳重な注意が必要なのですが、それはさておき、今度はMyDocument.nibの方のFile’s Ownerも見てみましょう。MyDocument.nibのFile’s Ownerアイコンには、NSDocumentのサブクラスであるMyDocumentクラスが割り当てられています。そして、ソースコードのMyDocument.hにはそのクラス定義が、MyDocument.mにはオーバーライド(書き換え)するNSDocumentメソッド(雛形)が記述されています。

    ソースコードの解説は省略しますが、MyDocument.nibはファイルメニューから「新規」や「開く」が選択される度に読み込まれて、そこに定義されているオブジェクトを生成します。MyDocument.nibにはNSWindowオブジェクトがひとつ登録されていますが、InspectorのConnectionsで調べると、File’s OwnerのwindowアウトレットにWindow(NSWindow)が、WindowのdelegateアウトレットにFile’s Owne(MyDocument)が接続されていることが分かります。これが、MyDocument.nibファイルが読み込まれた後に、既存オブジェクトとの接続関係を確立するための情報となっています。

    このような仕組みを見ると、どうも「File’s Owner」というアイコン名は不適切でユーザを混乱させているような気がします。単純に「Owner Object」とするか、Custum Classで選択されているクラス名(Owner Objectのクラス名)が表示される方が分かりやすいと思います。例えば、MainMenu.nibならNSApplcication、MyDocument.nibであればMyDocumentですね。ただし、後で作成したオブジェクトアイコンとは役目が異うわけですから、立方体の色を変えておけば(赤色とかに)より差別化しやすいのではないでしょうか?

    次回は、もうひとつの謎のアイコンFirst Responderの仕組みを調べてみましょう。こちらも、File’s Owner同様「線を引く相手がいないぞ、さてどうしよう?」と言う疑問点が重要なポイントとなっています(笑)。
    つづく                                

    ターミナルの向こうから      第10回  海上 忍

    〜 自動文書作成ツール「Doxygen」その1 〜

     今回は、ソースコードに添付する文書を自動作成してくれるツール「Doxygen」を紹介します。

    ・What’s Doxygen?
     Doxygenは、プログラマのための自動文書生成ツールです。各種ソースファイルにコメントとして記載されている文を抽出、再構成してHTMLやLaTeXの文書を作成します。起承転結や序破急といった文章の体裁や、いわゆる「てにをは」の使い方がチェックされるわけではありませんが、改めて文章を記述する手間が省けます。その意味では、文書生成ツールというよりコメント収集ツールと表現したほうが正確かもしれません。
     もう1つの特徴としては、クラスの派生やファイルの依存関係を調べてグラフ化する機能が挙げられます。人力で行うにはとても手間のかかる作業ですが、Doxygenに任せれば数十秒で完了します。
     Doxygenが対応する文書フォーマットは、HTMLとLaTeX、Man(roff形式)、リッチテキストとXMLの5種類です。生成される文書の内容に差はありませんが、広く配布する場合にはPDFへのコンバートが容易なLaTeXがお勧めです(LaTeXの導入方法については第7回を御参照ください)。オンラインでの公開が主目的の場合には、HTMLで十分でしょう。Doxygenは日本語をサポートしているので、どちらを選んでも支障はありません。

    ・Doxygenをインストール&セットアップする
     Doxygenのインストールは、以下のサイトで配布されているMac OS X版バイナリパッケージを利用します。本校執筆時点の最新版(v1.5.3)はユニバーサルバイナリ化されているため、PowerPC/Intelの別なく導入できます。作業はかんたん、ディスクイメージをマウントしてDoxygen.appをApplicationsフォルダへドラッグ&ドロップすれば完了です。

    http://www.stack.nl/~dimitri/doxygen/download.html

     なお、Mac OS X版にはGUIが装備されていますが、現行バージョンではバンドル内部に格納されているコマンドの呼び出しに失敗します。面倒でも以下の手順に従い、コマンド部分を/usr/local/binディレクトリへコピーしておきます。それでもDoxygen.appは設定ファイルの作成に役立つため、慌てて削除する必要はありません。

    - - - - -
    $ cd /Applications/Doxygen.app/Contents/Resources/
    $ sudo cp dot doxygen doxytag mscgen /usr/local/bin/
    - - - - -
    


    ・Doxygenを試す
     インストールが完了したところで、早速Doxygenの機能を試してみましょう。もちろん、皆さんご自身のソースコードを利用しても構いませんが、ここではDoxygen 1.5.3のソースコード(http://ftp.stack.nl/pub/users/dimitri/doxygen-1.5.3.src.tar.gz)を使うことにします。
     Doxygenを実行するときには、必ず設定ファイルが必要です。コマンド版では、オプションや引数を与えず「doxygen」とだけ実行すると、Doxygenという名称の設定ファイルが自動生成されるので、それを作業対象のソースコードにあうようテキストエディタで編集します。
     Mac OS X版の場合、この作業をGUIで行うことができます。Step 1にある[Wizard...]ボタンをクリックし、以下のとおり作業してください。

    1. Projectタブを開き、Project Name欄に「Doxygen」と入力する。

    2. 画面中ほどの[Select...]ボタンをクリックし、解凍しておいたDoxygen
      1.5.3フォルダを選択する。[Scan recursively]もチェックする。

    3. 画面下の[Select...]ボタンをクリックし、生成した文書を書き出すための
      フォルダを指定する(適当なもので可)。

    4. Modeタブを開き、使用している言語を選ぶ。Doxygen 1.5.3の場合は
      「Optimize for C output」を指定する。

    5. Outputタブを開き、生成する文書フォーマットの種類を指定する(複数指
      定可)。

    6. [OK]ボタンをクリックしてウィザードを終了する。

     続けてStep 2の[Save...]ボタンをクリックし、デスクトップへ「Doxyfile」の名で保存しておきましょう。Step 3の空欄には、一次作業用のフォルダとしてデスクトップ(たとえば/Users/shinobu/Desktop)を指定しておきます。これで、Doxygen.appは終了して構いません。
     最後は、次のコマンドラインをTerminalから実行します。しばらく待てばプロンプトが復帰するので、htmlフォルダ内の「index.html」を開くなり、latexフォルダ内のTeX文書をタイプセットするなりして、どのような文書が作成されたかを確認してください。

    - - - - -
    $ doxygen ~/Desktop/Doxyfile
    - - - - -
     


     これで完了……ならばいいのですが、日本語を含むファイルがある場合は、エンコーディングに即した処理を行わなければなりません。次回は、Doxygenで日本語を扱うときの注意点と対策を取り上げる予定です。

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

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

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

    2007-09-11

    目次

    • 「「Wonderful Server Life」    第54回   田畑 英和
    • 小池邦人のCarbon視点でCocoa探求
    • ターミナルの向こうから      第9回  海上 忍 

    「Wonderful Server Life」  第54回  田畑 英和

      〜環境設定編〜

     今回はモバイルアカウントのホームの扱いについて解説します。モバイルアカウントを使用すれば、ディレクトリサーバ上に登録したネットワークユーザのアカウント情報をローカルにコピーすることができることを前回解説しました。
     モバイルアカウントを使用した場合、ユーザのホームはローカル側に作成されます。もともとネットワークユーザに対してネットワークホームを作成していた場合には、ホームがサーバ上とローカル上の双方に存在することになります。

    ◇ホームの同期
     ホームがサーバとローカルの両方にある場合、それぞれの管理が必要になってきます。Pantherのころは手動でファイルの管理をおこなう必要がありましたが、Tigerになってからサーバとローカルのホームの内容を自動的に同期する機能が追加されました。
     まず、同期の設定方法ですが「モバイル環境」の環境設定で「ルール」の設定をおこないます。同期は次の2種類のタイミングでおこなわれ、それぞれについての同期の設定ができます。

    ・ログインおよびログアウト
    ・バックグラウンド(つまりログイン中)

     具体的な設定内容ですが、同期をおこなう対象/同期を行わない対象をそれぞれ設定することができます。例えばキャッシュファイルやゴミ箱の中身まではわざわざ同期する必要はないでしょう。また、大容量の画像やムービーファイルなど、同期の対象に含めるとファイルコピーに時間がかかるようなファイル/フォルダを同期の対象からは外しておけば、余分な同期が実行されなくなります。
     同期をスキップする項目には、デフォルトでいくつかの設定がなされていますが、Mac OS X Serverのバージョンによってデフォルトの設定が異なりますので、まずはデフォルトの設定をよく確認するようにしましょう。

    ・「モバイル環境」の「ルール」
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_MCX/WGM_MCX_Mobile02.png
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_MCX/WGM_MCX_Mobile03.png

     バックグラウンドで同期を行う場合はさらに、同期の間隔を設定することができます。「ルール」の「オプション」設定でバックグラウンド同期の設定ができます。デフォルトではログイン中は20分に1回同期が行われるようになっていますが、同期の間隔を短くしたり(最短で5分)長くしたり(最大で60分)できます。また自動ではなく手動で同期を行うように設定を変更することもでできます。

    ・「ルール」の「オプション」設定
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_MCX/WGM_MCX_Mobile04.png

     手動で同期をする方法ですが、モバイルアカウントを使ってクライアントコンピュータにログインしている場合、「システム環境設定」の「アカウント」にモバイルアカウント用の設定ボタンが表示されます。この設定画面から手動で同期を実行することができますし、あるいはメニューバーにモバイルアカウント用のメニューを表示していれば、そこから手動で同期を実行することもできます。モバイルアカウント用のメニューは、「アカウント」のモバイルアカウント用設定画面から表示することが出来ます。

    ・「システム環境設定」の「アカウント」設定
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_MCX/AccountPref.png

    ◇同期の詳細
     ホームの同期を実行すると、同期の対象に含めていた項目がコピーされるわけですが、このとき同期は双方向で実行されます。たとえばローカル上にファイルを新規に作成しますと、同期のさいにサーバ上にもコピーされます。一方サーバ上に新規にファイルを作成した場合、同期のさいにローカル上にもファイルがコピーされます。
     ファイルの削除や変更をした場合も、どちらかの削除/変更がもう一方に反映されて同期がおこなわれます。
     もし、ローカル上とサーバ上で個別に既存のファイルを変更した場合ですが、この場合はどちらかを自動的に優先しますと、片方の変更内容が失われています。そこで自動的に変更内容の違いが検出され、どちらのファイルを優先するか、あるいは両方を保存するかの選択をユーザに求めるダイアログが自動的に表示されます。

     さて、これで環境設定の解説は終了です。次回からはテーマを変えてNetBootについての解説を始めたいと思います。
     つづく                                

    小池邦人のCarbon視点でCocoa探求(2007/09/07)

    〜 Nibファイルの憂鬱 〜

    Cocoa Frameworkを用いると、ソースコードをあまり記述しなくても色々処理がこなせるようになります。その理由のひとつがNibファイルの仕組みにあります。今回から、新規プロジェクトで作られる2つのNibファイル、MainMenu.nibとMyDocument.nibの内容を詳しく調べてみたいと思います。

    具体的に中身を調べる前に、まずCarbonからCocoaへ移行する場合の問題点を復習しておきましょう。最初からアプリケーション開発をObjective-CとCocoaで行う場合は問題無いのですが、C/C++とCarbonや別Frameworkで開発作業をしてきた人にとって「既存アプリケーションをCocoaで作り直す」作業には、多数のハードルが存在します。そのうち、まず最初にぶつかるのは以下の2点です。

    ・リソースファイル依存からの脱却
    ・nibファイル自身のコンバート作業

    昔々、旧Mac OS 9用アプリケーション(CFM形式)をMac OS X用のCarbonアプリケーション(Mach-O形式)へと作り直す時に、リソースファイルに保存されていたダイアログやコントロールの雛形(Framework依存の雛形データ)をCarbon用Nibファイルへと変更する作業が発生しました。こうした作業には、アプリケーションが大規模になればなるほど膨大な時間が必要となります。現実に、この作業がネックとなり、随分長い間Mach-O形式にできず、結局Universal Binary化も遅れに遅れた大手メーカのアプリケーションもあります。つまり、ソースコードの移行とは別に、もうひとつの大仕事が存在したわけです。

    同様な事が、プログラミングモデルをCarbonからCocoaへ移行する時にも発生します。つまり、CarbonとCocoaのNibファイルはまったくフォーマットが異なるため、その変換を手作業で行う必要があるのです。またCarbonでは、文字列、カーソル、アイコン、画像などはリソースに保存されていることが多く、それらをCocoaで使える形式に変える作業も生じます。CFMからMach-Oへの移行では、各種リソースをNibファイルへと変換する機能(限定的)がInterfaceBuilderに用意されていたため、変換作業をそこそこ自動化することができました。しかし、今回はそうした仕組みもありません。

    試しに、Carbon用Nibファイルからウィンドウオブジェクトをコピーし、Cocoa用Nibファイルへペーストしても何も起こりません。まあ、ウィンドウを2,3しか使わない小規模なアプリケーションであれば、Nibファイルの変換も大した労力ではありません。しかし、ウィンドウ定義だけでも数百あり、その上に配置されたコントロールは千を越えるようなケースを想像してみてください。現実に筆者も幾つかそうしたプロジェクトを抱えていますが、NibファイルをCocoa用に作り直す作業に対し、利益に見合った時間と人件費を捻出することは容易ではありません。ましてや、これを1人や2人で行わなければいけないとすると「Carbonのままでイイや!」という結論が浮かぶのも至極当然なわけです。

    では「もっと賢く」という事で、CarbonとCocoaのNibファイルをXMLファイルに書き出して、それを解析(フォーマット未公開)することで自動変換ツールを作ろうかとも考えましたが、両フォーマットがあまりにも違うため、あっさりあきらめてしまいました。まあ、変換が容易であればApple社自身が提供しているはずですから、これは仕方のない結果でしょう。Apple社がCocoa普及を促進したいのであれば、これぐらいのツールを提供してもバチは当たらないと思うのですが…。ついでに、引き続き行うアクションやアウトレットをターゲットに接続する作業も考慮すると「Cocoaにするのは止めておこうか」という思考に拍車がかかります(笑)。

    Interface BuilderやNibファイル関連の問題については、実際に開発を行う過程でも言及していきます。それではさっそくプロジェクトに含まれるMainMenu.nibとMyDocument.nibの中身を見てみます。まず最初にMainMenu.nibをInterface Builderで開くと、Carbon用NibファイルのMenuBarやMainWindowに準ずるMainMenuとWindowの2つのアイコン(オブジェクト)が表示されます。それに加えて、File’s OwnerとFirst ResponderというCarbonにはないアイコンも見受けられます。次にMyDocument.nibの方を開くと、MainMenuがなくFile’s Ownerのアイコンの形状がMainMenu.nibのそれとは違うことが分かります。

    Carbon用のNibファイルは、Interface Builderで編集したメニュー、コントロール、ウィンドウなどの表示用オブジェクトをSetMenuBarFromNib()やCreateWindowFromNib()を使い実体化するために用意します。以前はこの役割をリソースとCreateWindowFromResource()が受け持っていました。こうした方法を取らないとすると、ウィンドウはソースコード上にCreateNewWindow()などのAPIを記述して作成することになります。CreateNewWindow()には、ウィンドウの表示枠サイズや表示位置、種類、各種の 属性(アトリビュート)などを設定する必要があり、ウィンドウタイトルを変えるにはSetWindowTitleWithCFString()などの別APIを呼ぶ必要もあります。

    こうした表示用オブジェクトをソースコードで記述するのは、イメージ化が難しく面倒な作業です。加えて、使うオブジェクトの量と共に記述すべきソースコードの量も増大していきます。Nibファイルを使えば、Interface Builderで編集した状態がそのまま表示されるわけですから、イメージ化困難等の問題は解消されます。ウィンドウやコントロールに使う文字列も、日本語と英語のものをNibファイルに用意し、それぞれJapanese.lprojとEnglish.lprojフォルダに入れておけば、多国語にローカライズされたアプリケーションを簡単に提供することが可能となります。

    しかし、現在の開発作業では、こうした表示用オブジェクトの編集に時間を費やすことも負担となっています。筆者がMac OS X用アプリケーションをCFM形式からMach-O形式へ速攻で切り替えた理由のひとつは、Interface Builderでの編集機能が、リソース編集ツール(ResEditやResorcerer)より強力で使いやすかったからです。これは直ちに開発時間の短縮につながります。CarbonからCocoaへの移行では、表示用オブジェクトの編集自体に大きな違いはありません。しかし、編集可能なコントロールとそのオプションの種類はCocoaの方が多く、アプリケーションの機能を充実させるためのメリットは増えます。

    最近のOOP(オブジェクト指向プログラミング)開発環境では、アプリケーションを形成するオブジェクトの役割分担を指して、モデル・ビュー・コントローラー(MVCモデル)と呼びます。詳しいことは本連載中に逐次解説する予定ですが、Carbon用のNibファイルはこのうちビュー(表示用)オブジェクトの雛形だけを保持できます。それがMenuBarやMainWindowであり、Cocoaの方ではMainMenuやWindowとなります。しかし、Cocoa用のNibファイルは、ビューに加えてコントローラーやモデルオブジェクトも部分的に保持可能となっています。こちらが、File’s OwnerとFirst Responderに関する仕組みとなります。ちなみに、この仕組みはCarbon用のNibファイルには存在しません。

    次回も引き続き、Cocoaプロジェクトに含まれるMainMenu.nibとMyDocument.nibの中身を調査します。File’s OwnerとFirst Responderアイコンは具体的にどんな機能を受け持っているのでしょうか? さらに詳しく調べてみましょう。
                                    つづく

    ターミナルの向こうから      第9回  海上 忍

    〜 MacでLaTeXを使うメリットとは・LaTeX編その3 〜

     前回は、TeXShopなどLaTeX入力作業支援ツールの紹介を中心に話を進めました。今回はLaTeX編の3回目、YaTeXの紹介とLaTeXの便利な機能について解説を行います。

    ・YeTeXの入力補完機能

     YaTeXの役割は、TeX文書の入力作業を支援することにあります。YaTeXの機能を使うと、環境(\begin{ }〜\end{ })やコマンドを入力する手間が軽減され、そのぶん文章を書くというもっとも重要な作業に集中できます。必要な設定はYaTeXのWebサイト(http://www.yatex.org/)などを参照していただくとして、ここでは具体例を示しつつその便利さを紹介してみましょう。

     まず1つが、入力補完機能。環境は必ず「\begin{環境名}」で始まり「\end
    {環境名}」で終わりますが、YaTeXを導入していればすべてを入力する必要はありません。たとえば、\begin{document}と必ず対をなす\end{document}は、以下の要領で入力できます。

    - - - - -
    \begin{    ←ここまで入力
     ↓ ↓ ↓
    \begin{} ←自動的に波括弧が閉じられる
     ↓ ↓ ↓
    \begin{document}  ←documentと入力してC-c eとタイプ
     ↓ ↓ ↓
    \begin{document}
    \end{document}    ←自動的に「\end{document}」が挿入
    
    ※:「C-c e」は[control]-[c]に続けて[e]を押すキー操作を意味します。
    - - - - -
    


     この作業は、YaTeXの「end補完」と呼ばれる機能を利用したものですが、「随時補完」を利用すればより効率的です。上の例では、\begin{document}を波括弧閉じ(})を除きすべて手入力しましたが、随時補完を利用すればさらに作業を短縮できます。具体的には、「\begin{」のあとに「d」と入力、C-cSPCとタイプすれば、2分割された画面に「d」から始まる環境名が候補として現れ、マウスでクリックすれば「\begin{」に続けて入力される、という流れです。

     もっと迅速に入力する方法もあります。こちらは環境名ごとに用意されたショートカットキーを覚える必要がありますが、これまで例に挙げているdocument環境でいうと、C-c b dとタイプするだけで、\begin{document}と\end{document}を入力できます。暗記が必要という意味ではヘビーユーザ向けの機能ですが、いつしか手癖のようになるため無理に覚える必要はありません。

    ・LaTeXにおける図版の扱い

     支援ツールにばかり内容が偏るのもどうかと思いますので、LaTeXに話を戻します。

     第7回では、LaTeXの文書はプリアンブルと本文から構成され……と簡単に説明したと思いますが、肝心なことには触れずじまいでした。「LaTeXでは文章のページ配分を考える必要はない」という原則です。EGWordなどのワープロソフトでは、ページレイアウトで出来上がり(印刷した状態)の画面を見ながら文書を作成していきますが、LaTeXではタイプセットするまでページ数はわかりません。見出しと本文のバランス、段落の字下げやインデントは”よしなに”処理してくれるのですが、2頁目の上から3cmに見出しを配置する、などといったレイアウトの絶対位置指定はかなり面倒です(不可能ではありませんが)。

     ワープロから移行してきたユーザが最初に戸惑うのは、表や画像の表示位置が思うようにならないことでしょう。LaTeXでは、表や画像といった”貼り付ける”性質のコンテンツは、基本的に「フロートオブジェクト」として扱われ、文章とは異なるレイアウトルールが適用されるからです。

     具体例で説明してみましょう。たとえば、A4用紙1枚分に相当する文章と画像1点、特に工夫もせず印刷すればA4用紙2枚分のLaTeX文書があるとします。そのとき、画像を1ページ目下部に配置しようと、数十行の文章のあとに図版を表示するコマンドを記述しても、画像が用紙に収まりきらなければ2頁目へと後回しにされ、そのぶん空いたスペースに本文が詰め込まれるのです。この場合、1頁目は文章だけ、2頁目は図版だけというレイアウトになります。

    ・得意技は目次/索引と参考文献の作成

     ”流し込み”が基本のため、思い通りの位置に表や画像を配置することが難しいLaTeXですが、目次や索引、参考文献に関しては、他の文書処理系の追随を許さないほどの実力を備えています。

     たとえば、索引に使用したい語句の直後に\indexコマンドを記述しておくと、タイプセットと同時に索引語が集められた一時ファイルが作成されます。一時ファイルをmendexコマンドで処理すると、五十音順に並べ替えできるほか、本文末尾に付け加えるのに適した体裁に整形できます。試しに、以下のリストを第8回で紹介したTeXShopへコピー&ペースト、[タイプセット]→[LaTeX]のあと[タイプセット]→[MakeIndex]、そしてもう一度[タイプセット]→[LaTeX]を実行してください。末尾のページ(といっても2ページしかありませんが)に、50音順に並んだ索引が追加されているはずです。

    - - - - -
    \documentclass{jarticle}
    \usepackage{makeidx}
    \makeindex
    
    \begin{document}
    隴西\index{ろうさい@隴西}の李徴\index{りちょう@李徴}は博学才穎、天宝の末年、若くして名を虎榜\index{こぼう@虎榜}に連ね、ついで江南尉\index{こうなんい@江南尉}に補せられたが、性、狷介、自ら恃むところ頗る厚く、賤吏に甘んずるを潔しとしなかった。
    
    \printindex
    \end{document}
    - - - - -
    


     次回は、いよいよ自動ドキュメント作成ツール「doxygen」の紹介です。LaTeXが導入済であることを前提に話を進めますので、あらかじめ御了承ください。

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

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

    2007-09-04

    目次

    • 「りんご味Ruby」       第10回  藤本 尚邦
    • 藤本裕之のプログラミング夜話   #121
    • 高橋真人の「プログラミング指南」  第119回
    • 書籍紹介            矢野りん著  デザインする技術

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

    前回は、Rubyのブロック付きメソッドによる繰り返しの抽象化の効果を示しました。今回は、I/O(ファイルやソケットとの入出力)への読み書きアクセスを題材として、ブロック付きメソッドによるシステム上のリソースへのアクセスの抽象化とその効果を示します。

    ■ (1) I/Oの開け閉めを直接プログラムする

    ファイルへの読み書きやTCPソケットによる通信では、ほとんどの場合、以下のような手順で処理が行なわれます。

    (1) まずI/Oを開く
    (2) I/Oを介してデータを読み書き処理する
    (3) 最後にI/Oを閉じる

    まず、この手順を直截的にプログラムしてみましょう。

    -----------------------------------------------
    require 'socket'
    require 'rss/2.0'
    io = TCPSocket.open("www.mosa.gr.jp", 80)   # I/Oを開く
    io.write "GET /?feed=rss2 HTTP/1.0\r\n\r\n" # HTTP GET要求を送信
    rss = RSS::Parser.parse(io, false)          # 受信しながらRSSをパース
    io.close                                    # I/Oを閉じる
    rss.items.each { |i| puts i.title }         # 結果を表示
    -----------------------------------------------
    


    このプログラムは以下のような処理をしています。

    ・モサのWebサイトにTCP接続する
    ・HTTPプロトコルのGETを送信し、RSSデータを要求する
    ・RSSデータを受信しながらパースする
    ・接続を閉じる
    ・パース結果の各エントリのタイトルをプリントする

    同時に開くことのできるファイルやソケットの数にはシステム上の制限がありますから、不要になった時点でそのI/Oは閉じるべきでしょう。しかし、上記のようなプログラムで I/O を閉じる部分を書き忘れてしまうというのはありがちな話だと思います。書き忘れなかったとしても、RSSパース時に例外が発生したりしたら、I/Oは開きっぱなしのままプログラムの実行は外に移ってしまいます。

    ■ (2) I/Oの開きっぱなしを避ける – ensure の導入

    書き忘れはどうしようもありませんが、例外の発生によるI/O開きっぱなし問題には解決策があります。例えば Java の finally

     

    try { ... } finally { ... }

    のようなものを使う方法が考えられます(何が起ころうともtry節から出るときには必ずfinally節を実行する)。Rubyにも、beginとensureを使う同様の構文があります。さきほどのプログラムを改良してみましょう。

    -----------------------------------------------
    require 'socket'
    require 'rss/2.0'
    io = TCPSocket.open("www.mosa.gr.jp", 80)     # I/Oを開く
    begin
     io.write "GET /?feed=rss2 HTTP/1.0\r\n\r\n" # HTTP GET要求を送信
     rss = RSS::Parser.parse(io, false)          # 受信しながらパース
    ensure
     io.close                                    # I/Oを閉じる
    end
    rss.items.each { |i| puts i.title }           # 結果を表示
    -----------------------------------------------
    


    begin節(beginとensureで挟まれた2行)の実行中に何が起きようと、ensure節(ensureとendで挟まれた1行)は必ず実行されます。つまり、ioは必ず閉じます。

    I/Oが閉じられるようになって少し安心しました。このように書いている時点でI/Oを閉じることを意識しているわけですから、それを書き忘れることはおそらくないでしょう。

    ■ (2) I/Oの開け閉めを抽象化 – ブロック付きのopen呼出し

    ところで、このプログラムは少し冗長な気がしませんか? とくに、I/Oの開閉に関する部分は、I/Oにアクセスするどんな目的のプログラムであろうとさほど変わるものではありません。言い換えると、TCPソケットで通信するプログラムには以下のようなパターンがあります。

     

    io = TCPSocket.open(ホスト名, ポート番号)
     begin
       I/Oアクセス処理 ...
     ensure
       io.close
     end
    


    何度も同じようなパターンが出てくるときは、それを出来る限り抽象化して繰り返さないようにするのが正しいプログラマの姿勢(DRY原則-後述)。ここでブロック付きメソッド呼出しの登場です。TCPSocketクラスのクラスメソッドであるopenは、I/O(ソケット)へのアクセス処理をブロックとして渡すことにより、上記のようなパターンのI/Oの開け閉めを裏でやってくれます。この場合、ブロックの実行結果がopenメソッドの返す値となります。

     TCPSocket.open(ホスト名, ポート番号) {|io| I/Oアクセス処理 … }

    では、ブロック付き呼出しでさらにプログラムを書き換えてみましょう。

    -----------------------------------------------
    require 'socket'
    require 'rss/2.0'
    rss = TCPSocket.open("www.mosa.gr.jp", 80) { |io|
     io.write "GET /?feed=rss2 HTTP/1.0\r\n\r\n"
     RSS::Parser.parse(io, false)
    }
    rss.items.each { |i| puts i.title }
    -----------------------------------------------
    


    とてもすっきりした上、I/Oの閉じ忘れも書き忘れも起こりえないプログラムになりました。

    ここまで、TCPSocketを実例としてI/Oへの読み書きアクセスの抽象化を説明しましたが、これはIOクラス(TCPSocketの上位クラス)でサポートしている機能です。ファイルI/Oに関しても同様に使うことができます。もちろん、このような抽象化をサポートするブロック付きメソッドをプログラマが定義することもできます。

    ■ [補足] HTTPアクセスについて

    今回、説明の都合からTCPSocketを直接使いましたが、実際のRubyプログラムでHTTPプロトコルを使う場合には、Rubyに付属しているライブラリの net/httpあるいは単純な GET だけであれば open-uri (RSSリーダで使っているもの)を使うのが手軽かつ安全で良いでしょう。

    ■ [補足] DRY原則

    DRY = Don’t Repeat Yourself の略。「同じコードの繰り返しは抽象化してひとまとめにしよう」というプログラミング上の原則のこと。「達人プログラマ」(ISBN-13: 978-4894712744)という本などで紹介されています。

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

     相変わらず暑い日が続くが皆さん元気にハタライておられますか。さて本連載は今回から新シリーズ、タカハシ編集長と協議の結果、ウインドウに付属している「ツールバー」というのはどうやって出すのだ? というのをやることになった。ツールバーって分かるよね? タイトルバーのすぐ下にあって、例えば Finder のウインドウならSpotlightの検索ボックスとか表示切り替え(アイコン/リスト/カラム)ボタン、Xcodeのプロジェクト・ウインドウなら「ビルド」とか「ビルドして実行」とかのアイコンが表示されているあそこのことである。

     まずはXcodeで新規にプロジェクトを作成し、MeinMenu.nib をダブルクリックして Interface Builder を起動してみていただきたい。デフォルトで用意されるウインドウを選択し、Inspector で Attributesタブを選ぶとこれに関係ありそうな項目は一個しかない。下から2番目にある「Unified title/toolbar look」というやつで、NSWindowの時に説明したと思うがこれは「タイトルバーとツールバーを境目のない遠近両用眼鏡のような外見にするかどうか」のスイッチである。……早い話があるウインドウがツールバーを持つかどうかはここでは指定できない。
     次にやはり Inspector で Connectionsタブを選んでみる。するとここのOutlet のなかに、おおありますね「toolbar」というのが。つまりなんらかのオブジェクトをこの Outlet に連結すればこのウインドウに突如ツールバーが出現する、仕掛けになっているのだな。

     ところが、だ。やってみると分かるがこの Outlet に何か(例えばツールバーくらいのサイズにしたNSViewとか)を連結しようとすると無情にも「toolbar must be of type NSToolbar」ちうメッセージが出て繋がってくれないのである。ほんぢゃその NSToolbar というタイプのオブジェクトはどうやって作るのか? 普通この手のオブジェクトはパレット・ウインドウからドラッグ&ドロップすれば作れるもんなのだが、パレットのどこを探してもツールバーらしきものはないんですな。
     というわけで結論、現在のところツールバーは Interface Builder を使って作ることができない、のである。あ、わざわざ「現在のところ」と断ったのは、この10月にリリースされる新OS、Leopard に付属する開発ツールでは、Interface Builder もバージョンアップされ、もしかするとそれが可能になる
    かも知れないから、ね。つうかオレとしてもなって欲しいんだけどさ……。

     ではどうするか。しょうがないから全部コードで書くのである。そう嫌な顔
    をしてはいけない。昔はみんなそうだったでしょ? とりあえず恒例、
    MainMenu.nib のウインドウで NSApplication のサブクラス MyApplicationを作り、こいつの Outlet に「window」(クラスはNSWindow)と、「report」(クラスはNSTextView)を追加する。デフォルトで用意されているウインドウに NSTextView をひとつパレットからドラッグ&ドロップして適当な大きさに拡げ、これを MyApplication の「report」に、ウインドウ自体を「window」に連結したら保存してひとまず Interface Builderは終了。
     最初の目論見としてはツールバーにアイコンを一個表示し、それがクリックされたらreportに「クリックされたぞ」というレポートを表示したい。あ、アイコンを用意しなくちゃいけないか。インターネットから適当な絵を探してきてもいいが、面倒くさかったら /Libraries/User Pictures/ の下にユーザアカウントを作るときに選べる tiff ファイルが入ってるからネコとか犬とか好きなのを選んでプロジェクトに追加すればいい(「ファイル」タブの「Resources」の下にドラッグする)。
     これで材料はそろったので MyApplication.h を開き、ツールバーを管理するのに必要なインスタンス変数を以下のように定義する。クラスはNSMutableDictionary。

       

     NSMutableDictionary*          toolbarItems;

     あとやることは「アプリケーションが無事にラウンチされたよメッセージ」であるfinishLaunching: のタイミングでツールバーを生成し、それをウインドウのOutlet にセットする……ってもそれがなかなかの難物なんだけどね。以下は次回といたします。ほんぢゃ。
                                (2007_08_30)

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

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

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

     こんにちは、高橋真人です。早速始めます。
     今回は、前回の最後に載せたコードについての解説です。
     実際に打ち込んだコードを走らせてみましたか? アプリケーションの動きを確認して、前のものと比較してみるといくつかの違いがあるのに気付くと思います。
     いちばんの違いは、メニュー項目の表示部分です。File、Editの各メニューを開いて確認してみると分かりますが、NewとClose(いずれかのウインドウが開いている場合)以外の項目は、すべて無効化されています。
     前回までのものは、FileメニューとEditメニューですべての項目が選択できる状態になっていました。しかし、上記の2項目以外について、今のアプリケーションでは実際にそれらを実行する処理はどこにも存在しないので、本来なら無効化しておく必要があります。

     余りに身近な存在であるために、とかく軽視されがちなメニューの表示処理ですが、コンテキストに応じて表示状態を頻繁に変更する必要があるため、案外煩雑なものなのです。
     Cocoaでは、フレームワークの柔軟性を生かして、なかなかに巧みなメニューハンドリングの仕組みが用意されています。私もオブジェクト指向、非オブジェクト指向の様々なシステムでメニューハンドリングを見てきましたが、Cocoaの処理のスマートさは群を抜いていると思います。

     ご存知のようにCarbon自体はオブジェクト指向のフレームワークでないため、PowerPlantXのようなフレームワークは、もともとCarbonが持っているメニューの仕組みをうまくオブジェクト指向でくるんでスマートにする使命を負っています。オリジナルPowerPlantでは、Mac OSの複雑な歴史的背景のために複数のアプローチを用意していましたが、PPxでは、Carbonイベントモデルが用意するコマンドイベントをそのまま利用する形を取っています。具体的には、前回説明したPPxCommandEvents.hに定義してあるいくつかのクラスでこれらを解決しています。
     前回も触れましたように、このヘッダに定義してあるクラスは、PPxのコマンドハンドリングの中核とも言うべきところなので、全部を説明しようとすると、どうしても話が深く、複雑になってしまいます。そこで、当連載では使い方の部分に徹して説明をします。動作原理の部分に興味のある方は、別途C++の解説書などを参照してください。

     さてそれでは、再度クラスの定義を見てみましょう。MyApplicationの定義部分を再掲します。

    class MyApplication : public PPx::Application,
                         public PPx::CommandConverter,
                         public PPx::SpecificMenuCommandEnableDoer,
                          public PPx::SpecificMenuCommandDoer
    {
    public:
                       MyApplication();
    
    protected:
       virtual OSStatus    DoSpecificCommand(
                               PPx::CommandIDType,
                               PPx::SysCarbonEvent&    ioEvent);
    };
    


     MyApplicationが継承しているクラスの二番目にCommandConveterがあります。これは、これから使うクラスを使うための裏方のクラスでして、「とりあえず、加えておくものなのだ」と覚えておいてください。
     さらにそのあと,似たような名前のクラスが二つ続いています。 SpecificMenuCommandEnableDoerとSpecificMenuCommandDoerです。
     まず、SpecificMenuCommandEnableDoerの方ですが、これはその名の通り「特定のメニューコマンドを有効にするもの」ということで、つまり、クラス名の直後にある<...>の部分で指定したコマンドが割り当てられているメニュー項目を有効にする機能を持っています。
     たとえば、今回の一つ前のPPxForXcode02でのMyApplicationの定義は以下のようになっていました。

    class MyApplication : public PPx::Application,
                         public PPx::CommandProcessDoer
    {
    (残りは省略)

     もう忘れてしまったかもしれませんが、このアプリケーションを走らせた時にメニュー項目はほとんどすべてが有効になっていました(OSが管理してくれるWindowメニューは除く)。この部分を「適切に」するために、このSpecificMenuCommandEnableDoerを使うのです。
     理屈としては、MyApplicationが継承するクラスに、たとえば

    public PPx::SpecificMenuCommandEnableDoer

    というのを加えることで、New…の項目が有効になります。
     「え? もともとNew…は有効だったでしょ?」と思うと思いますが、それは結果的な話でして、あくまでそれ以外のメニューが有効であったことの方が、ここでは「普通ではない」のです。
     その証拠に、Interface Builderですべてのメニュー項目を無効にした状態にしてから走らせてみれば、New…だけが有効になっているのが分かるはずです。
     ここでちょっと面白い実験をしましょう。
     PPxForXcode02をフォルダごと複製して(前のものを残しておく必要がなければ、複製しないでそのままいじっても構いません)、MyApplication.hのクラス定義の部分を少し書き換えます。
     もともと、

    class MyApplication : public PPx::Application,
                         public PPx::CommandProcessDoer
    {
    


    こうなっているのを

    class MyApplication : public PPx::Application,
                         public PPx::CommandConverter,
                         public PPx::SpecificMenuCommandEnableDoer
    {
    


    と変えてください。
     目的は、SpecificMenuCommandEnableDoerを加えるだけのことなのですが、以下の理由から上記のようにます。

    ・先ほど触れた裏方のCommandConveterを加える必要がある
    ・もともとあったCommandProcessDoerは、SpecificMenuCommandEnableDoerも
    含めて、これらのコマンドやメニューハンドリングをするクラスの継承元として含まれているので、取ってしまった方がよい(ただし、残しておいてもコンパイル時に警告が出るだけで、動作的には支障はない)

     「以上でOK」と言いたいところなのですが、あと少しだけを手を加えます。
     MyApplication.cpで、MyApplicationのコンストラクタに2行加えて以下のようにします。

    MyApplication::MyApplication()
    {
       EventTargetRef targetRef = GetSysEventTarget();
       PPx::CommandConverter::Install(targetRef);
       PPx::SpecificMenuCommandEnableDoer::Install(targetRef);
    }
    


     先ほども言いました

    PPx::CommandProcessDoer::Install(targetRef);

    は、

    PPx::SpecificMenuCommandEnableDoer::Install(targetRef);
    


    から間接的に呼ばれていることになるので、外します。
     では、これで実行してみてください。先ほどの説明通りに、ほとんどのメニュー項目が無効になるとともに、アプリケーションメニューのAboutPPxForXcode01の下にPreferences…という項目が表れたはずです。もちろん有効になっています。これは、コード変更前にはなかった項目です。
     今回の変更では、単にこの項目を「有効にせよ」と指示しただけなのですが、システムが環境設定項目が必要だと判断して、用意してくれたのだと思います。

    書籍紹介       矢野りん著  デザインする技術

     解説担当:高橋政明

    デザインする技術
     矢野りん
     インプレスコミュニケーションズ ISBN978-4-8443-5858-8  2,000円+税

     アプリケーションに限らずwebページなど「デザイン(視覚的美術的な意味でのデザイン)」は不可欠です。自分でデザインしなければならない場合はもちろん専門家の力を借りることができる人もデザインの素養は必要でしょう。
     才能はどうすることもできないとしても、基礎的な技術を習得し改善されるならと本書を手にしました。

     ものづくりの手がかり 「考」の技法
     点と線で家が建つ   「図」の技法
     身近だから知らない  「文字」の技法
     情報の舞台装置    「面」の技法
     目に見えることすべて 「色」の技法
    の五つの章に分類された、見開きまたは1ページ完結の技法が載っています。それぞれカラーの図(実例)と簡潔な解説に加え、注、この技法の用途、デザインテイストが載っています。縦組の本文が新鮮でコラムで取り上げている話題も親しみやすく、全体も読みやすい本です。

     『無難にまとまっているけど○○に似ている』とか『ユニークだけどダサイ』とか自分のデザインにはなかなか満足できないものです。本書でこんな悩みがすべて解決するわけではありませんが、原因の分析や対策一助にはなりそうです。

     より広く、設計の意味でのデザインでも「考」の技法が参考になりそうです。寸法単位の解説や参考文献など「デザイン」関連情報の出発点として開発の現場に一冊どうぞ。

    ▼出版社のweb (詳しい目次あり、pdfで立ち読み可能)
    http://www.mdn.co.jp/Books/Contents/5858M.htm

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