さてさて「ユニコード文字を羅列するフォームに単純にツールバーを貼る」というスタイルはダサイので,「フォーム上にツールバーとユニコード文字表示コントロールが兄弟関係として存在する」というスタイルをとることにしました。ツールバーの上にはコンボボックスがあります。
作業はすんなりと進んだわけですが,アプリケーションを起動,マウスホイールでスクロールさせようとしてもスクロールしません。コードを見直しても,確かにスクロール処理は怠っていません。スクロールバーのノブを引っ張れば確かにスクロールが可能です。
どうやら WM_MOUSEWHEEL メッセージは,フォーカスを持つウィンドウに送られるのが原因な模様。アプリケーションの開始直後はフォーカスは親ウィンドウにあるようで,いくらホイールを回してもユニコード文字表示コントロールには WM_MOUSEWHEEL は届きません。
解決策はこんなの:
親フォームが WM_MOUSEWHEEL を受け取った時,::SendMessage() でユニコード文字表示コントロールにメッセージを横流しします。・・親が子にメッセージを送るんだから「横流し」じゃないじゃん。
ただし,今回はフォントを選択するためにコンボボックスを取り付けていますが,こいつはクリックするとフォーカスを積極的に奪って行きます。親フォームにフォーカスを戻すために面倒なコードを書かなければならなくなるので萎え。この案はダメかも。
ユニコード文字表示コントロールを生成した後で,::SetFocus() でコントロールにフォーカスを割り当てれば万事解決なのです。それからコンボボックスがフォーカスを積極的に得ようとするので,そうですね,コントロールが WM_LBUTTONUP を受け取った時にも再度自分に ::SetFocus() をしてはどうでしょ?
この問題はこれで解決。明日は,貼り付けたコンボボックスにフォントを列挙させてみます。
2003-04-17 を最後にすっかり忘れてましたが,Unicode Viewer を作ってたんですね。ツールバーの表示まではイケたんで,ここにフォントを選択できるコンボボックスを乗っけましょう,と。
ていうかその前に,これではダサいのですよ。
ツールバーが右端に行き着く前に,スクロールバーでさえぎられています。対して,常識的なアプリケーションはこう。イメージは「サクラエディタ」のものです。
イメージの Unicode Viewer は「メインのフォームに直接ユニコードを描画し,そのフォームの上にツールバーが存在する」というカタチでしたが,それではダサいのです。「メインのフォームの上にツールバーコントロールと“ユニコード表示コントロール”がある」という感覚でなければなりません。
うし。これを踏まえてコードを構成しなおす事にします。
※日付が変わってしまったというか,むしろ今は 30 日の昼に近いので多少反則気味ですが気にしないー。
また犯ってしまいましたよ車輪の再発明。しかも今回は XML という最新技術に踏み込みいかなり過激な方向に進んでいます。もう誰にも文句は言わせません。
今回作ってみたのは DOM インスペクタ。内部ではちゃんと DOM レベル 1 をそこそこ実装している本格的な DOM オブジェクトを保持しており,その構造をツリービューにコピーしています。
->スクリーンショット;
これは indexj.htm を読み込んでみた図。ちゃんと木構造が反映されているのが分かります。#text ノード内の文字列が化けていますが,これは EUC-JP に対応していないからですね。文字列変換ルーチンはいくつか存在するので,あとで組み込んでみようっと。
この DOM インスペクタには意外な使い道が。私が作っているサイトはほぼ全部 xhtml なので,この DOM インスペクタに放り込む事が出来ます。うまく木構造が反映されていなければ,十中八九,その xhtml ファイルの方で問題が発見されます。実際に今まで書いてきた雑記ファイルを食わせてみると,かなーり大量に記述ミスを見つける事ができました。我ながらすげぇ。
深夜なのにゲラゲラわらた ( 困る
プログラマ界隈ではなんとなく「宇宙のステルビア」というアニメ番組が話題の模様。中でも作品中のちょっとした台詞「バグが無いんです」には全国 100 億人のデスマーチャーたちが大興奮。「あり得ない!」「スタッフが暴挙に出た!」「これは架空の話だ!」「しーぽん萌え!」「あれは伝説だ!」「おめーら甘ぇ! バグ 0 を目指せ!」などと各人が言いたい放題なので,私にはそっちの方が笑えました。
次のコードにはバグがあります ( "[EOF]" は無視してくらさい )。
#include <stdio.h>
int main( int argc, char *argv[] ) {
printf("hello, world!");
return 0;
}[EOF]
実行するとコンソール画面に "hello, world!" と表示され,プロセスは戻り値として 0 を返します。ここには次のバグが挙げられます。
# ./a.out hello, world!# <- ここにプロンプト実行結果の直後にプロンプトが来るのはナンセンス。
printf() は実行コストが高い。puts() を使うこと。どうしても printf() を使いたいならば printf( "%s", "hello, world!" ); とするべき。CreateProcess() を用いる必要がある )。無くて七バグ。バグじゃなさげな項目もありますが,mozilla なんかでは「これを実装すれば cool じゃないか?」てな要望さえも bugzilla に登録される ── ユーザの要望を満たしていないのは "バグ" だと考える ── らしいんスけど。だからバグが数十万もカウントされるんスよね?
25 日付けだったのかー。カタカナ語の言い換え提案が発出されました。おつかれさんだす>国語研究所の人たち。とりあえずこの雑記では気にしませんが,大衆向けの文章を書く場合は少しは気を使った方がよさげ?
そもそも昨今の奇妙なカタカナ語の氾濫は ( /. でも何人かの匿名さんが近い事を指摘していますが ) "偉い人" に対してプレゼンテーションを行う際に,ヘンに難しい英単語を使って "なんか分からんけど凄そうだ" という雰囲気を出し,最終的に "よし ( よく分からんが ) 分かった。キミに任せる!" なんてな展開を狙ったものではと。
少なくとも学問の世界ではなるべく新しいモノを理解するのが重要であって,日本語に容易に置き換える事が可能ならば躊躇無く置き換えらます。
std::mem_func を使って C# のようなデリゲートが可能との事なんで,後で遊んでみようかなあ。。。・・・うーん,日本語に訳しても,理解できないモンは理解できんのです。偉い人にはそれが分か以下略。
無理な日本語訳には抵抗を感じますが,「無理して外来語を使う」てな姿勢にも疑問。まあ,使いたい言葉を使えばいいってのがファイナルアンサーですかね,ソシュール大先生。
昨日のコードを動かしてみました。
:-( :-( :-( :-( :-( :-( :-( :-( :-( :-( :-( :-( :-( :-( :-(
ファッキュ。
・・当たり前ですわな。いくら static_cast<A::F*> したとは言え,this は this。メソッド func() は B::F にオーバーライドされているため,あのコードではいつまでも B::F::func() を呼び続けます。
何を考えたか ( 自分でもなぜこんな発想が出てきたのか分からない ) こんなコードを書きました。
#include <iostream>
struct A {
struct F {
virtual void func(){ std::cout << ":)" << std::endl; }
};
};
struct B {
typedef A::F super; // やはり super を定義してみる
struct F: public A::F {
void func(){
std::cout << ":-(" << std::endl;
A::F *a_f = static_cast<A::F*>(this);
a_f->super::func(); // ここ! a_f->A::F::func() ではコンパイルできない
}
};
};
int main( int ac, const char *av[] ) {
A::F *f = new B::F();
f->func();
delete f;
return 0;
}
これで OK。お疲れ様でした自分。
ちなみに a_f->super::func() を a_f->F::func() と書いてもなぜか期待通りの動作をします。が,これはさすがに VC++ のバグの範疇かなあ。。。
昨日のコードをデバッガ上で実行すると,こんなんなっちゃいます。
->VC++ の断末魔;
B::F は "super" と別名をつけられた A::F を認知せず,一生懸命に自分自身を呼び出しています。健気なんだけど一発かましてやりたく存じますよ?
ここでなんとなく神が光臨。こういうトリックを思いつきました。もう typedef には頼りません。
#include <iostream>
struct A {
struct F {
void func(){ std::cout << ":)" << std::endl; }
};
};
struct B {
struct F: public A::F {
void func(){
A::F *a_f = static_cast<A::F*>(this);
a_f->func();
}
};
};
int main( int ac, const char *av[] ) {
B::F f;
f.func();
return 0;
}
なんと,これできちんと A::F::func() を呼び出してくれました。OK,キミは最高だよ。
・・おっと,ここで大切な事を忘れてた:本来,func() は virtual にしたかったんスよー。A::F *f と宣言された変数に new B::F() をかますと,きちんと B::F でオーバーライドされた関数が呼ばれるのです。多態ってやしです。
#include <iostream>
struct A {
struct F {
virtual void func(){ std::cout << ":)" << std::endl; }
};
};
struct B {
struct F: public A::F {
void func(){
std::cout << ":-(" << std::endl;
A::F *a_f = static_cast<A::F*>(this);
a_f->func();
}
};
};
int main( int ac, const char *av[] ) {
A::F *f = new B::F();
f->func();
delete f;
return 0;
}
まず B::F::func() が呼ばれて欲しいんだけど,その中から「デフォルトの実装」である A::F::func() も使いたいって事でこんなコードになりました。さて,実行。
どうなるのか,勘の鋭い人ならもう分かりましたよね? とりあえず続きます。
昨日の続きなんだけど,一つトリックを思いつきました。これならどうだ。
struct B : public A::F {
typedef A::F super;
void func(){
super::func();
}
};
ん,これなら成功。コンパイルも通り,期待通りの動作をします。しかし実は私がやりたかったのは次のようなコード。B::F の中から A::F の関数を呼びたいのです。一度 B を脱出して,しかるのちに A の中に突入です。
#include <iostream>
struct A {
struct F {
void func(){ std::cout << ":)" << std::endl; }
};
};
struct B {
typedef A::F super;
struct F: public super {
void func(){
super::func();
}
};
};
int main( int ac, const char *av[] ) {
B::F f;
f.func();
return 0;
}
おしっ,コンパイルが通った! 迷わず実行するぜ気合じゃをりゃぁああ!!1
・・あ,続きます。
そもそも C++ の仕様が複雑さを極めているため,完全に把握してる“人”でさえあまりいないんじゃないかな。いわんや,コンパイラをや。例え仕様を把握できたとしても,それを実装するのは至難の業ってわけですね。
VC++6.0 でもこれには対応していませんでしたが,こんなコード:
#include <iostream>
struct A {
struct F {
void func(){ std::cout << ":)" << std::endl; }
};
};
struct B : public A::F {
void func(){
A::F::func(); // error C2352: 制的でないメンバ関数の中で ...
}
};
int main( int ac, const char *av[] ) {
B::F f;
f.func();
return 0;
}
コンパイルできません。コンパイラの出力するエラーは「制的でないメンバ関数の中で呼び出しが正しくありません」と日本語がちょっとアレですが,MSDN によるとつまり,static なクラス関数の中で何かしらのインスタンスが必要であるはずのメンバ関数が呼ばれたよという意味。
class C {
void method() {} // 非 static なメンバ関数
static void staticMethod() {
method(); // error C2352
}
};
このコードでこのエラーなら当然なのですが,ではなぜ前述のコードでこのエラーが? ・・要するにクラス B が「クラス A の中のクラス F」という,名前空間を一つまたいだ継承をしたからの模様。VC++6.0 ではまだこのあたりが弱いようで,少し残念だけど,クラス F を A から出してやる必要があります。VC++7.0 では大丈夫なのかなあ?
んで,実はこのネタは続きます。
選挙戦が始まりました。いたるところで街宣車が走り回り,大声でなんかわめいてます。これこそが激しい馬と鹿のぶつかり合い。イイ事でも言ってるんならその存在意義を認めなくもないですが,彼らが街中で叫んでいるのはこう。
「foo 山 bar 男でございます」
「みなさんに支えられてきました」
「一生懸命,闘ってまいります」
「今こそうんたら制度の見直しを」
・・ンな抽象的な。Abstract な要求をするのは市民であって,それを適切に Implement するのが政治家の仕事ではないのかと。
「ご声援ありがとうございます」
うるせえよ。
そういえば,今までの仕事でソートを用いる事はほどんとありませんでした ( 去年の今頃にちょこっとだけ・・去年の雑記を参照 )。大抵はデータベースを扱う仕事だったんだけど,そんな場合は SQL でソートをかけちゃいますもんね。
そんなんじゃプログラマとして恥ずかしいので,ちょっとつまんでみました。
#include <algorithm>
struct Comp {
int operator () ( const int& a, const int& b ) const
{ return 0; } // ここが笑うところ
};
int main() {
int arr[] = { 9,4,0,57,3,4,92,34,7,3,9,8,414,0,9,5,872,4,510,65 };
int count = sizeof arr / sizeof arr[0];
std::sort( arr, arr + count, Comp() );
for (int i=0; i < count; ++i )
printf("%d,", arr[i] );
return 0;
}
出力:
65,510,4,872,5,9,0,414,8,9,3,7,34,92,4,3,57,0,4,9,
ん。
昨日の続きです。テンプレート引数の識別子に _N という文字列を使いましたが,g++ では大抵のヘッダファイルをインクルードするとコンパイルできなくなります。
原因は ctype.h ファイルで見つかりました。
#define _U 01
#define _L 02
#define _N 04
#define _S 010
#define _P 020
#define _C 040
#define _X 0100
#define _B 0200
こんなモノを定義してくれちゃっています。邪魔なんでボク undef しちゃいますよ?
・・・はい残念でした。このマクロはこんな風に使用されていて
#define isdigit(c) (_ctype_+1)[(unsigned)(c)]&_N)
私がもし isdigit() を使いたくなった場合に,どうしても識別子 _N が必要になってしまいます。undef する事はご法度。要するに先頭がアンダースコアであるような識別子は,私たちが定義,使用するのはナンセンスだという事でファイナルアンサーの模様です。K & R でも書いてましたっけ?
C++ のテンプレートは C のマクロ機能をほぼ置き換えるほどの威力なので萌え要素。他の言語でも搭載しちゃえばいいのに ( 悪魔の囁き )。
template< int N >
struct Array{ char array_[ N ]; };
上のコードはテンプレート引数に指定された個数の要素の配列を宣言できます。Array<12> a; てな具合にね。この方法は 2002-07-02 で作ってみた "SolidSequence" クラスでも使っています。
では,こんなコードはいかが?
template< typename T, T N >
struct Array{ char array_[ N ]; };
N の型を int ではなく,その前のテンプレート引数で指定するようにしてみました。これでもコンパイラは文句も言わずに仕事を行います。
じゃあ,こんなのも。
template< typename T, T N >
struct Array{ char array_[ N + 1 ]; };
N + 1 個の要素の配列を保持してもらいましょうか。・・ g++ では難なくコンパイルが通りましたが,我が "愛コンパイラ" VC++5.0 では 「C1001 内部コンパイラ エラー」が出てしまいました。
ここまでのコンテキストで「なぜ?」を考えるのはもはやナンセンス ── コンパイラでさえ,何が悪いのかを明示できていないのです。よって回避策のみを考えます。こんなのはどうでしょ?
// 注意;VC++5.0 ではコンパイル可能だけど,
// g++ 3.2 ではコンパイルできません
#include <iostream> // g++ でコンパイルできなくなる原因 ( 後述 )
template< typename T, T _N >
struct Array{
enum { N = _N };
char array_[ N + 1 ]; };
enum ハックの威力ですねー。なんとか VC++5.0 のご機嫌をとり,コンパイルに成功します。しかし今度は g++3.2 でコンパイルが通らなくなりました。実はエラーメッセージを見てもなかなかわかり難いのですが,原因はテンプレート引数の識別子に使った _N にありそうな気配。
ああ,丁度時間が良さげなので,今日はこのへんで。