読者です 読者をやめる 読者になる 読者になる

インターフェイスの向き

設計

 Win32API をがりがり使ってプログラムしてるといやーんなことがあります。
 例えばウィンドウを列挙する API にこんなのがあります。

EnumWindows( WNDENUPROC, LPARAM )

 こいつを使って、「最も長いウィンドウタイトルを取得する処理」を書いてみます。

#include <windows.h>
#include <iostream>
#include <string>

using namespace std;

string get_window_text( HWND hwnd )
{
    string title( ::GetWindowTextLength( hwnd ) + 1, '\0' );
    ::GetWindowText( hwnd, & title[ 0 ], title.size() );
    return title;
}

BOOL CALLBACK search_most_long_title( HWND hwnd, LPARAM lParam )
{
    string &     most_long_title = * reinterpret_cast< string * >( lParam );
    string const title           = get_window_text( hwnd );
    
    if( most_long_title.length() < title.length() ) {
        most_long_title = title;
    }
    
    return TRUE;
}

int main()
{
    string most_long_title;
    EnumWindows( search_most_long_title, reinterpret_cast< LPARAM >( & most_long_title ) );
    cout << most_long_title << endl;
}

 なんとも長ったらしいですが、長いのはあまり重要じゃないです。そんなことより、「集合の中から最も○○なものを取得する」ということをするために標準アルゴリズムが使えないってーのが何ともいやーんなわけです。
 それもこれも、「ウィンドウの順次アクセス」にかかるインターフェイスが「C++iterator」になってないからなのです。順次アクセスのインターフェイスが iterator になってさえいれば、標準アルゴリズムが使えるのに...。というか、インターフェイスが独特だからというだけで、ウィンドウへの順次アクセスを要する処理全般において標準アルゴリズム全般が死ぬわけです。

 「今更 Win32API なんざ dis ってんじゃねーよ、vector に突っ込むようにラップすりゃーいーじゃん、アホが。

 と言いたい気持ちはわかりますが、Win32API を dis りたいんじゃないんですねー。順次アクセスのインターフェイスの "向き" を見つめたいんですよ。

 EnumWindows は、各要素へのアクセスを EnumWindows というAPI 呼び出しの内側で外部のプログラムに展開させる、という性質があります。一方で iterator は各要素へのアクセスをiterator の外側のプログラムで展開させるという使い方をします。
 順次アクセスのインターフェイスが、内側を向いているか外側を向いているかっていう違いがあるわけです。同じようなことをしたいのに、真逆のベクトルのものが両立しているっていう、このモヤモヤ...。

 EnumWindows のようなインターフェイスを内向き
 iterator のようなインターフェイスを外向き、としましょう。

 こーゆーのはどっちかに統一しようぜ!というのも無理があります。なぜなら一長一短なんです。 内向きのインターフェイスは、要素へのアクセスを局所的に封じ込めるので、開始と終了のタイミングをライブラリ側は確実に捕捉できるという利点があります。例えば開始と同時にファイルを開いて終了と同時にファイルを閉じる、というようなことをライブラリ側に隠蔽できるのです。一方で外向きのインターフェイスは汎用的なライブラリを作りやすいです。処理の遂行に必要な変数や関数を、ライブラリ側が比較的自由に定義できます。

 インターフェイスの向きについて、統一したコンセプトが決められないわけです。

 「あーもう堅い事言わず、適材適所でいーじゃないの、ボケが。

 と言いたい気持ちは分かりますが、ライブラリを書くときは、そのライブラリがどういう使われ方をするのかという可能性を可能な限り狭めたくないと思ってしまうのでやっぱり悩ましいのです。

 で、ここから言語 dis りを始めたいところですが、長くなっちゃったのでこの辺で閉じます。

※ コルーチンにして yield してやれば EnumWindows みたいなものも外向きのインターフェイスにできます。
※ Scala の ローンパターンなんかは巧い落としどころ。
※ 「ファイルはデストラクタで閉じたりするやないか。イテレータイテレータで別に考えるものでしょ!」ってゆーのはちょっと話がずれちゃうので置いときました。
※ 関数型とか詳しくないのでこーゆーのはうまいことやってるのかしら?わからない。