忘れないうちにメモをしろ 2000-9〜11 


 プログラミングにまつわることで、思い出したこと、考えたこと、気になること など、とにかく中途半端でいいからメモを取ろう、と思うページ……




 2000-09-29 いいわけ

 当初は、もっと適当にホームページに並べて、さらにメモ書きは "気が向いたら〜"に何でもかんでものつもりだったのが、 かってに気おもなって書きづらいので、新たに。
"サブルーチン〜"や"winゲーム〜"とか書きさしのままだけど、 すでに自分が何書く気だったか……興味の大きいうちにメモらないと駄目ね。 分類とか整合性とかは素材がたまってから考えたほうがええもんなんや。 もの無いうちから分類考えたかて破綻するもんな。 っと開きなおろー(って開き直ってはじめたつもりがこれやもんな)。 適当に、ネタがたまったらカット&ペーストしてまとめられたらいいや、で (さて、たまるのか)。



 2000-09-30 CでシフトJIS(\のこと)


■文字コードの話(問題)は、気にしだすと頭がいたくなるんで 深く考えないけれど、シフトJISでMSDOS/WIN系OSでC系言語で プログラミングしていると、どうしても全角 2バイト目の \ に ついては目をつぶるわけにもいかない。 Unix系/EUC系で生活してる人ならシフトJIS(MS全角)ぅなんかケッです ませられそうだし、 この先はまあ10年もすれば普通どこでもUnicode系って ことにもなってそうな気もするけれど、当面は。
 とりあえず(日本の)文字コードについてようわからん方は、こちら

文字コードの話従来の文字コードとUnicodeの対応に関する諸問題
日本語と文字コード
全学ゼミ講義ノート・文字コード
とかを参照してみるのいいかも(て、よけいわからん場合もあるやもしれんけれど)。


※何度もすぐ忘れるんだけれど、シフトJIS文字は、現状追認の形で JIS規格(1997年?)になっているんですね。
 10数年前登場して幾年かはシフトJISとだけ呼ばれていたけれど、JIS規格でもないのに JIS という文字が 入るのを嫌がる人がいたのか、また、MS-DOSの普及とともに MS発祥てことで MS全角 って 呼び方も増えてたんですが(ライブラリのマニュアル書籍とかでそう記入されていたし)、 JIS規格になってからは、またシフトJISのほうが多くなってきたのかなあ、って気がします(無責任)。 少なくとも自分では"シフトJIS"のほうを使うことがまた多くなってきたかな(まあ昔からだし)。 もっとも"MS全角" のほうが文字数少ないんで書きやすい場合もある……ってシフトJISは SJISて 書くからいいか^^;


■問題の \ (コード 0x5c) だけれど、

なんてことになっており、さらに、¥なのは日本の都合だしで ASCIIコードだと \ (バックスラッシュ)だったりする ……比較的普通の文では使わないから他の役割にまわされやすい文字なんだろうなあ。 他でもいろいろな役割を持つことあるし(まあ記号は\に限らずだけど)。

 で、英語圏(1バイト文字)生まれのシフトJIS対応していないプログラムに、 下位バイトが\(0x5c)のシフトJIS文字を与えると、ファイル名としてなら誤った ディレクトリ(フォルダ)に化けてしまい、ファイルが見つからなかったり
    "例\其ノ十ニ.TXT"
なんてパス名だと十=0x8F5Cのため、ディレクトリ名を除いたファイル名として
    "ニ.TXT"
を選んでしまったりするなんてことになったりする。

 またC系ソースを処理するツールだと、 \ は \n(LF) や \t(TAB) などの表現で特別な文字コードを 表現したり、文字列中に \001 や \xfc のようにコードを直接埋め込むために 用いられ、\自身は \\ と書いて \ が生成され、また \の直後に特別の役割をしない 文字がきたらその文字のまま(\が無視される)というようになっており、このため ファイル名だとディレクトリ区切りとして \\ と書かねばならず誤って \ だけだと おかしくなる(でも #include については、特別に扱われたりすることもある)。

 "文字列"中でシフトJIS文字を使うと
    "命令表\n" が "命令表n"
として扱われて表示が意図したようにならなかったり(表=0x955c)、
    "文字表" が "文字\x95\x22
のように扱われて "が無いとしてコンパイラエラーになったり、運悪くすると
何処かで辻褄があってバグに化けたりする。

 コメントも同様なことがあって
    /*命令表*/ だと /*命令\x95/
と扱われてエラーになったり、運が悪いと
    p = &odr_tbl[n];  /*命令表*/
    foo(p);       /*コメント*/
の場合
    p = &odr_tbl[n];  /*命令\x95/
    foo(p);       /*コメント*/

のように、次の行がコメントとして扱われ foo(p) が実行されなくなってしまう。 "の場合はあまりないかもだけれど、こちらは結構ありえそうで怖い。 もっともたいていのコンパイラは /*コメント*/ 中に /* が現れると警告 (コメントのネストはできないよ、とか)を出してくれるだろう(と思う)。

 また // コメントの場合も
    p = &odr_tbl[n];  //命令表

    p = &odr_tbl[n];  //命令\x95\
となり、行末の \(改行) は行連結指定で \(改行)自体は空白に化けて複数行を 一行に見なす機能があるため、次の行もコメントに化けてしまう……場合もある かもしれない。 //コメント中の\(改行)に関しては 動作未定義(不定)/処理系依存項目 だったようにも気がするし、これも、 たいていのコンパイラは警告を出してくれる(と思う)。

 あと ' による文字コード指定もあるが、これは L'表' とかしない限り 1バイトし書けないし 文字列のように複数行にまたるバグには化けないのでマシだろう (そもそも L'表'のような書き方を覚えていない人もいるだろうし、以外と記述が面倒だし余り 見やすくもないので、結局文字コード直接書いたり、いっそ文字列として処理したりするかも しれない)。

 もちろん、上記のようなことは、シフトJIS(マルチバイト)文字対応のツール/コンパイラとかだと 気にしなくてよいだろう。
 けれどパソコン用でなく、コンシューマゲーム機や組み込み系の仕事だと シフトJIS未対応の海外製コンパイラを使う場合もあるわけで 上記のような事情は知っておかないと不味いかもしれない。

 いくら警告が出ていても、見るほうが事情を知らなければ、でもってソースを見ても 一見正しく問題のないソースなので余計に、無視される可能性が高くなる (実際、後輩に尋ねられたことの原因がこの手のことだったことは何度か、ある)。


■実際問題になる文字なんだけれど、コード表見れば……てのは面倒なので

/*-----------------------------------------*/
#include 
int main(void)
{
    int i;
    for (i = 0x81; i <= 0x9F; i++)
        printf("%c%c", i, 0x5c);
    printf("\n");
    for (i = 0xE0; i <= 0xFC/*0xFF*/; i++)
        printf("%c%c", i, 0x5c);
    printf("\n");
    return 0;
}
/*-----------------------------------------*/
を実行してみれば
―\ソЫ\\\\噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃 濬畚秉綵臀藹觸軆鐔饅鷭\\\\\\\\\\\\\\\\\\
ということだった。機械的に出力したものなので、何が見えているかは あなたの環境しだい、だけれど。


■自衛の策として、Cのコメントの記述に関しては、 上記問題文字を覚えきるのは面倒なので日ごろから /* 文字表 */ のように必ず */ の直前に空白を置いたり // コメントは行末の直前には空白や . など半角文字を置いたり、 いっそ使わず/*コメント*/だけにするのが手かもしれない。

 で、"文字列"に関しては、いくつか方法があるけれど、 上記問題になる全角文字の直後に\ を差し込む のが一番安全かもしれない。

 見た目に中途半端で、かつ、逆にシフトJIS 対応コンパイラに食わせると \が余分についた問題のあるソースになってしまうので、さけて\x?? を 使って問題になる文字コードを "表" ならば "\x9f\x5c"のように書くほうが よさそうな面もあるのだけど、反面、たとば "表10" を "\x9f\5c10" にしてしまうと 今度は \5c10 の部分をでかすぎる文字コードとコンパイラが解釈してエラーに なってしまう場合もあったりする。

 10も\x31\x30のようにしたり、いっそすべてを\x?? にしてしまうのも手かもだが、 それだと人が直接読むには辛すぎるソースだし、作業的にも手作業でなく多分ツールで 対処することになり、それだと記述のソースを別に残しておくだろうで、 結局対処ソースを直接触れないならソースの文字数の多くなりすぎない \ の挿入だけの ほうがリスクが少ないかもなあ、と思えてしまう。

 "\x9f\x5c" のようにしてるとソース上の文字数は4倍になってしまい、すべてを 変換しているとモノによっては一行が長くなりすぎてコンパイラの制限にひっかかる 可能性がなくはないかも、という懸念もあるし。

(なお実は "表10" の類は "\x9f\x5c""10" のように ANSI-C の文字列結合 機能を使っての回避もできたりもする)


■ちなみにぼく自身は、自作ソフト墓場に置いている

    csstob  (//コメントの/*コメント*/化)
    czenyen  (\が問題になる文字の検索や\の挿入/削除、\x??化)

のようなプログラムを作って対処したり(他の自作ものもシフトJIS対応に作ったり)、 場合によっては GNU indent のようにソース公開のツールなら弄って シフトJIS対応したりしてしのいでいます:-)

(※ 16ビットDOS が主流の時代は、UNIX 系のツールが移植される場合、 日本のDOS環境にあわせて シフトJIS 対応にされたり標準エラー出力を なんとかしたり8.3ファイル名対策などされる方もいたりしたけれど、 今では、NTやCyWin,いっそUnix系, にコマンドラインを常用するような 方々は移っちゃっているようで、DOS向き(シフトJIS対応)化、なんてする人は 減ってるようです)


■ C プログラムでのシフトJIS文字への対応ですが、MS(DOS/WIN)系コンパイラで 日本で販売(移植)されているものでは、大昔だと(ms-c5?,ms-c6当たり)
    jstring.h  jctye.h
というヘッダのライブラリが、その後日本語に限らず他の国でのシフト文字も ひっくるめてマルチバイト文字といって扱う
    mbstring.h  mbctype.h
がついていると思います(現在のvc,bccでは jstring系は付属していない)。

 でもGNU-C 系とかにはついてないかな。ひょっとしたら、どっかに フリーのシフトJISライブラリとかありそうな気はするけれど、あいにく知らない ……マルチバイトはちょっと手間としてもシフトJIS限定とかならば、 そんな難しいもんじゃないとは思う (昔os9/09用にjstring.hな関数を汗かいたことはあった^^;)。 (追記: 結局、自分で試しに書いてしまった。こちら
 jstring.h のほうは、結構使えそうで使えない関数とかあり、というか シフトJIS対応化の方向性が実際の用途とずれていたりして結構いまいちだったかも。 たとえば jstrupr は全角アルファベットを対象に全角アルファベットを 大文字化するという関数だったけれど、実作業しているとそれよりも 全角2バイト目を誤認せずに半角アルファベットを大文字化する関数のほうが 用途として多いと思う(で、この辺は mbstring ではそのように変更されている ...追記: 実は、jstring系の関数の挙動は(ver違いも含め)コンパイラごとに微妙に 違っていたかも)。

 と書いたけれど、自分では、それらのライブラリはほとんど使っていなかったり する。

 ターゲット環境用にそれらの関数がない、ってことが多いってのもあるけれど、 使い勝手がイマイチそう、てか、自前でポインタで処理していると あまり出番がないってのもある(ポインタ処理と合わない関数は いまいち使いづらい……str系標準関数(strspn,strcspnとか)でも何故か ポインタでなく相対位置(バイト数)を返すものあるし)。

 で、ぼくが実際よくやるのは jstring.h にあった iskanji(c), iskanji2(c) と同等のマクロ(のみ)を用意することかな。 て、ぼくに限らず jstring.h の時代からCでシフトJISを扱う人はよくやる方法だと思う。

#define iskanji(c)       ((unsigned char)(c) >= 0x81 && ((unsigned char)(c) <= 0x9F || ((unsigned char)(c) >= 0xE0 && (unsigned char)(c) <= 0xFC)))
#define iskanji2(c)      ((unsigned char)(c) >= 0x40 && (unsigned char)(c) <= 0xfc && (c) != 0x7f)
(実際に自分で使うときは、大文字化していたり。自前だしマクロだからってだけど)
あと、iskanji(c)のほうは、
#define iskanji(c)       ((unsigned)((c)^0x20) - 0xA1 < 0x3C)
なんてちとトリッキーな書き方をする人もいる。


 実際に使ってみる例としては、たとえば

/*-----------------------------------------*/
char *basename(char *name)
{
    /* パス名のディレクトリ以外のファイル名の部分へのポインタを返す */
    char *p = name;
    while (*p != '\0') {
        if (*p == ':' || *p == '/' || *p == '\\')
            name = p + 1;
        p++;
    }
    return name;
}
/*-----------------------------------------*/
という関数を、シフトJIS対応にすると、
/*-----------------------------------------*/
char *basename(char *name)
{
    /* シフトJIS対応でパス名のディレクトリ以外のファイル名の部分へのポインタを返す */
    char *p = name;
    while (*p != '\0') {
        if (*p == ':' || *p == '/' || *p == '\\')
            name = p + 1;
        if (iskanji(p[0]) && iskanji2(p[1]))
            p++;
        p++;
    }
    return name;
}
/*-----------------------------------------*/
のようにしたりします。
場合によっては iskanji2(c)を使わず単に
if (iskanji(p[0]) && p[1] != '\0')
のようにして済ます場合もある。
 下手に正確にチェックしてハジくよりも 全角の1バイト目が現れている以上それは全角だろうし2バイト目が違う場合は 壊れた文字であって単独の1バイト文字ではない、と扱ったほうが 安全な場合もありそうだからです(ただし、終端 '\0'を取りこぼすと危険、 化けた文字だったとしても文字列の途中に'\0'があるのを許すのは Cの習慣や標準関数を 使う場合は問題なので、これだけはチェックする)。

 あと、さらにファイル名処理の場合は、

if (file_sjis_flag && iskanji(p[0]) && p[1] != '\0')
のようにして、file_sjis_flagをユーザオプションで指定できる要素にしたり OSに確認を取った値を使ったりして場合分けすることもあるかも。
 使用頻度が多くなりそうならば、
#define fname_iskanji(c)   (file_sjis_flag && iskanji(*p))
のような、ファイル名専用のマクロを用意するのもよいかもしれない。


■思ったよりも長文になってしまった。毎度語尾が不統一だし。  あと、何か書き忘れてるんだが、なんだろう^^; (思い出したらネタにするが)。

 あと、このページよりも、もっと簡潔にCでの\について扱ったページとしては

2バイト目に'\'(0x5c) を含むSJIS文字
があったりします(これ見る前から自分で書く気だったけれど、\を含む文字を列挙されてるのみて 先にやられててちょっと悔しかったり^^;)。

 上記以外にも漢字コード関係として(リンクのページにもはってあるけれど)

漢字コードとコーディング方法 題どおり
既存の日本語文字コードと Unicode の間のマッピングルール 題のとおり。Cで書かれた対UNICODE変換のソースあり
7ビット及び8ビットの2バイト情報交換用符号化漢字集合――第3水準及び第4水準 JIS X0213 制定のために運営されていたページ
漢字テーブルと文字集合テーブル 画像による文字一覧
など。


※ リンクをあさっていて多少忘れがちになってそうなこととして、一水ニ水漢字 旧JIS(78) / 新JIS(83) について、今は基本的に新JISを基準に話をするけれど、実のところ 旧JISを長いこと採用していたマシンは

NEC PC98x1 系
ってことは無視できない人には無視できないと思う(て、無視できる人は多いだろうけど)。
 一般ユースでは PC98x1系を現役で使っている人は少ないだろうけれど(エミュやゲームはともかく)、 仕事で使うソフトが(winとしてでなく) PC98x1 用しかないために、結構まだ稼動しているだろうと想像はできる (以外と 8ビット機や X68Kがヘンなところで交替できずに使われていた例は結構あったと思うんで。 もっともそういうところはインターネット等に縁があまりなさそうな気もするし、 新旧の差異が大問題になるかは別問題だけれど)。



 2000-10-01 CでシフトJIS 追記(charの符号)


■CでシフトJIS を使う場合で書き忘れていたこと……注意点、というか、型について。
 文字列の型は、基本的に char です。
 signed char でも unsigned char でもなく、char 型です。
 ANSI-C は型チェックレベルでは char は signed char でも unsigned char でもない 別の型として扱われます。もちろん実際の動作は、符号付/無 のどちらかになりますが。

 で当然 char を使って日本語(シフトJIS/半角カナ、EUCでも)を扱うのに問題になるのが 符号の問題です。

 char のデフォルトの符号はコンパイラによってまちまちで、大抵コンパイラオプションに よって変更はできますが、

charを使う場合、符号がどちらであっても正常に動作するようにプログラムする
ように書いたほうが保守しやすいですし使いやすいでしょう (コンパイラオプションに任すのは、自分以外の人がソースを使うときに以外にはまり易い要素です)。

 charの符号が違いで問題になるは、当然 0x80 以上の値が、マイナスの 値に化けてしまうことがある、と、いうことで、

    /*------------------------------*/
        char *s, *d;
        int  c, u;
        〜
        c = *s;
        u = *(unsigned char*)s;  //あるいは u = (unsigned char)*s; u = *s & 0xFF;
        〜
        if (*s == *d) a = 1;     // ok
        if (*s == c)  a = 1;     // ok
        if (*s == u)  a = 0;     // char型 依存
        if (*s >= *d) a = 0;     // char型 依存
    /*------------------------------*/
のような場合、ok となっているところのように、互いがchar型である限り(char型の値を いれたint変数)等号不等号は成立しますが、大小比較に関しては成立しなくなる 可能性があります。また、u の例のように、変数へ代入するときは符号無対策をしているのに 比較の段階でそのことを忘れて比較すると、当然char型依存してしまいます (このバグは以外にやってしまうと思います)。

また、シフトJIS系で以外にはまりやすいのが、 半角カナを'ア'のように表現した場合でしょうか。 'ア'の値はcharのデフォルトの型に依存するようで コンパイラによって

        int c;
        〜
        if (c >= 'ア') a = 1;    // アは半角のつもり
のようにあったとき、半角'ア'の値が 0xB1 とは限らず -79 の場合があり、 比較が成立しなくなる場合があるでしょう。 (コンパイラによっては、'ア'のように書けないモノもあります)。

 これらの対策としては、 日本語の文字(列)を扱う場合は unsigned char *型を用いるというのが一つの手で、 気をつけなければならないのは、ANSI-C標準関数の定義(ヘッダ)は、char で定義されて いるので、それらの関数を用いる場合、警告/エラーになるのを避けるためキャストが 必要になってくることでしょう。 全くライブラリ関数を使わないならば、お手軽な手段です。

 char のままでやる場合は、符号が問題になる個所、

のようなことが必要になります。

 どちらがいいかは、結局使われ方次第ですが、ぼく自信の傾向としては 符号が気になる比較を多用するような個所は unsiged char、それ以外はchar ってとこです(ライブラリ関数を使う場合にキャストしたくないというのと 符号を無視できるような文字列処理もママあるので、ならば1トークンの char のほうが使いやすい、って考え方です)。



■ コンパイラによって半角'ア'を受け付けないものがある、って件の蛇足ですが、 最近はそんなにないと思いますが、昔のプログラムとかだと、 英語専用ソフトの場合、入出力できる文字列として 7bitの範囲(0〜0x7f) の文字しか 許さないものもあります (Cのソースの場合単純に isprint(c) や isgraph(c)などの利用の影響の場合も あるかもしれませんが)。
 ものによっては最上位ビットを別の役割に内部で割り当てていたり(例えば 文字列の終わり)して、移植などをするのは、ちょっと面倒な場合もありえます。



 2000-10-02 フリーの(TTF)フォントがほしい


■思うところがあって あわよくば商用利用も含め自由にWindows9xで利用可能な フォントがないかなあ(できれば TureTypeFont か32x32以上のフォント) と思いつつ(仕様やツールについて)ネットを色々検索してしまいました (こちらのリンク集をかなり参考)。


■とりあえず、ネットで見かけるフォントデータですが、検索したり Vectorのフォント などをみると

とか,その他いろいろあります。

 bfntは WinGL系のゲームで採用されていたりするゲームキャラも想定の草の根のフォーマット、 win31の fonはなんか .exe形式のようで、フォーマットはようわかりません (DDKとか探せばあるのかなあ)。 でもって、PostScript/TeX関係は根本的に事情がよくわかっていません^^;

■fontx2 はパソ通発祥系の DOS/V の日本語表示ドライバの一種でフォーマットが 公開されており(作者のページFONTX Home Page)、 結構いろいろなフォントやツールが普及している(た)みたいです。

(VECTORとかでも)各種コンバータや調整ツールがいろいろあり、 各種フォント←→FONTX2形式をするふぉんと昆布や、 fontx2bdf(parl)のようなツールがあるので 一旦 fontx2にしてしまえば他に持っていける可能性がなくはないかも?、です。

 ただ直接(または比較的楽に) windows9x系で使うことはできなさそうで、  win31専用ならfontx2ドライバがあったり、 ttfでなくos付属の .fonファイルを書き換えてコンバートするツールがvectorにあるけど ( fnx2oem )ベースになる .fonファイルが入手しずらかったり 16x16フォントのみ だったりで、いろいろ手間かかるかもしれません(同様の手法のものをつくるのも手かも)。

 その他 vectorのdos>fontユーティリティ>変換とかにツールあり (nifとかにもまだあるかな)。

■bdf は、よくUnix(x)系で使われているフォントのようです。 結構fontやツールもあるかもしれません。 テキストで書かれ見た感じ結構単純そうにも見えます。
 元は Adobeのフォーマットなんでしょうか。 MS配布のTTFフォント用のツールで Adobeの .bdf ファイルを 入力するツール(sbit32)があったのですが、 そのドキュメントの説明をみるかぎり、同じフォーマットのように思えました (正確には、根っこが一緒で発展が違ったりする可能性もあるでしょうけど、そのへんは不明)。

こちらこちらに bdf の解説頁があります(2002-01追記)

■で、TureTypeFont(TTF) です。
 もともと Apple と MS が共同で作ったフォーマットらしいです。
 現在ではさらに拡張され、Adobe のポストスクリプトフォントも統括した Open Type Font(OTF) というものになっているみたいで、 電子著名などの機能も付加されているようです。

 また、Unix/Win等でTTFを使うためにフリーのライブラリ/ドライバを作られている ところがあります( The Free Type Project )。 TTFを自由に扱うにあたって問題になっているのが、 アウトラインンフォントの描画アルゴリズムの一部が アップルの特許になっているみたいで、さけたフリーのものを構築されてるようです。

 実際に普及しているTTFファイルは、MACとWinで微妙に差異があり、 それぞれ専用のファイルとなっています。ただし、それらを 調整してMac→Win,Win→Macするツールはあるようです。

 TTFのフォーマット資料としては、以下のようなところが参考になるかもしれません。

Microsoft Typography MicroSoft のTTF関係のページ.英語。 そのた MSDN Library(英語) の Specifications に OpenType Font Specification や TrueType 関係のページありです。
Microsoft/Typography/otspec/ OpenTypeの仕様関係。英語。 The OpenType Font File (OTF(TTF)フォーマット仕様書)などがある(それを圧縮ファイルにしたものもあり)。英語。
fonts.apple.com TrueType Reference Manual アップルの TureTypeFontのページ。英語。
Adobe Solutions Network:Developer Program アドビの OpenTypeFont のページ。英語。
デジタルフォントの話 FreeType Glyph Conventionsの和訳「ヒント」情報の一例の解説があります。
Windows95 のフォント事情 (仕様じゃないけれどWinでフォントを扱う場合の注意点)


 TTF で使う文字コードですが、Win9x では シフトJIS もありのようですが、Win2K では Unicodeオンリーで ダメみたい?です。今後のものでは Unicode を採用するのが普通になっていくみたいです。



■TTF 関係のツールは、ありそであまりないです。

まず、本家マイクロソフト配布のツールは
Microsoft/Typography/Developer information/tool
にあります。基本的にコマンドラインツールです

 ttfdump はttfファイルのヘッダ情報をダンプします.

 sbit32 は ttfファイルに (Adobeの) bdf形式のビットマップフォントを付加するツールです。 (ひょっとしたら Unix系のbdfもいけるのかな?... (2002-01追記) どうも文字コードがUnicodeじゃないと駄目なようで変換が必要らしい)。 docに bdfフォーマットの解説があります。

 ttoasm/ttodasm はアウトライン?を記述?するアセンブラ/逆アセンブラのようです (ようわからんです)。

 fastfont は普通ASCIIコード順にソートされているタグを、アクセスが早くなる ような順番にタグを並べ替えるツールです(資料のどっかに、必ずソート済みになる ようなこと書いてたような気もするけれど、このツールがあるということは、 タグはソート済みである、と、期待してツールを作っちゃダメってことですね)。

 casheTT は、ラスタライザーから得られた値を計算しキャッシュに入れることにより テーブルVDMX、hdmxおよびLTSHの1つ以上の生成/修正した ttfに変換するツールらしい (これも、ようわからん^^;)。

その他、もろもろ。


MS以外のところでは……

Ttedit は、 winでTTFを編集できるソフトです。3000円のシュアウェア。 いろいろと高機能のようです。 WinでTTFフォント作ってネットで配布している人ご用達のようです。

TrueTypeフォントエディターも、 winでTTFを編集できるフリーソフトです。対象とするのは Unicode のTTFのみのようです。 (2002-01追記)

wTTC は、 MAC の TTF を Win用に変換します。 (リンク切れ)

ttfmod は、 TTFのパラメータを変更できるソフトです。現在はヒンティングとスムージングに関して調整ができるらしいです。

TTModify は、 True Type Fontのテーブルの編集ツールのようです。

FntInfo は、 TrueTypeフォントの詳細情報を表示します.

jsfont は、 MSフォントの行間を空けたフォントを生成するツールです(IEとかで用いると便利かも)。

ttc2ttf は、 ttc ファイルのヘッダを弄って ttf ファイルに変換します。Dos/Win用ってわけではないけれど cppソースなので bcc32 等でコンパイル(単純なエラーが出る可能性あり。 for(j= をfor(int j=などにして対処。ただttfdumpに食わすとttcと認識される^^;)。


cb2ttj は、 Lin YawJen氏作の中国語圏用 TrueTypeFont作成ツール群 cb2tt を 日本向けに改造されたパッケージです。
(ただUnix向ttfを想定されているためか、半角カナには未対応)

 これはコマンドラインのツール群ですが、一旦 1文字 1024x1024 の ビットマップファイルを作成し、 その画像から輪郭を抽出してアウトラインフォントとしての TureTypeFont を作る、という代物です。
(ついでにこちらにcb2tt付属の Usage.doc を翻訳ソフトに食わしたものをリンク)。

 bmp を作成するツールさえあれば他のフォントからを TTF を作ることも可能で、実際 cb2ttj98というページでは、 cb2ttj を用いて NEC PC9801 のROMフォントから TureTypeFontを作成するツールを配布されていたりします(リンク切れ)。

 がんばってツール作れば fontx2 からも可能ってことですね(というか、bdfすればすでにいけるのかな?)。 ただ、ベースにするフォントはある程度大きくないとツライかなあ と思われますが。

 データの作成については、cb2ttj の ttfstub と MS の sbit32 で ビットマップフォントのみの ttf とか作れないかなあ、と想像します (試してなく、単に思っただけですが)
 MSの OpenTypeFont のフォーマット解説を見てるとビットマップフォント のみの(タグの)TTF はありに見え、実際 WinCE ではそのようなのですが Win9xでは未対応のようです(WinCE用の k12x10.ttf という フォントをインストールしてみたら、フォントの種類表示でビットマップフォント は出ますがポイントごとの表示はなく、他で使うと出なかったり化けたり する模様。ttfdump でヘッダをみたらマニュアル上必要最小限のタグしかなかった)。
Win9x では必ず何某かアウトラインデータがいるのかな?

12dotシフトJIS X 0213用フォント「こころ12」を作ろう の頁には bdf2ttf.exe というツールがソースとともにあります。ただし、12ドットフォント固定で、WinNT系は不可。 (2002-01追記)


その他、 ベクターのWinのフォント用ユーティリティ に win用のツールはあります。

また 理研のアーカイブ(font) とかに、ttfから別のフォーマットへ変換するツール(unix,ソース)があったりします。


それと、Winではないですが、Macでは、Macro Media「FontGrapher」というフォントエディタが よく使われてるらしいぽいです。



■データに関してですが……
 英語のttfフォントはいろいろありそうですが、日本語のフルセットのものに なるとあまりなさそうです
 フリーのアウトラインデータとしてはポストスクリプトですが、

和田研フォント (別サイト) や
渡辺フォント
というものがあるようです。

 これらを、ツールのところで紹介した cb2ttj を用いて TTF に変換可能みたいで実際、 Everything We Do is Linux というページで 渡辺明朝,和田研中角ゴシックをTrueTypeに変換したものを配布されています。

 Linux(Unix)で使用するために変換されたもののようですが、 これダウンして Win9x にインストールしたところ、 全角文字はでるのですが、半角文字が表示されませんでした。 と、いうことで、このままでは、通常利用には向きませんが、 グラフィックエディタ等で全角のみ参照する分には 利用できるんじゃないかなあ、って感じです (cb2ttj のページのサンプルでは サンプル画に半角文字が出ているので、そのように自分でデータを生成すればできるはず)。

(2002-01追記)
渡辺フォントと呼ばれるものにはいろいろなものがあるようで、 遍歴が入り組んでいるようです (こちらの頁を参照)。

こちらでは、 渡辺フォントを元にリメイク中の "東風明朝フォント" と 和田研フォントを元にリメイク中の "東風ゴシックフォント"を 作られています(Winで使ってみて半角カナも表示された)。 これらはさらにフリーのビットマップフォントを幾つか組み済みで 小さいフォント表示のときはそちらが使われます。

こちら の頁では、Watanabe明朝を元にリメイク中の "拡張Watanabe明朝フォント"があります。第3/4水準の対応等もされているようです。




 2000-10-03 漢字文字コード表


   JISコード 0x2121〜0x7e7e の文字を、シフトJIS(MS全角) または 日本語EUC で テキスト出力するツールを作ってみた( これ )。

 って、ぜんぜんたいしたもんじゃないけれど。昔作ったのに紛失してしまってて JIS->シフトJISの変換式さえあれば、ほんの十分もあれば済むようなもんだと わかっていても、作り直すのシャクでほっといたんですが……

以前、職場で別チームがやった作品で名前入力で入力できない(選択肢に現れない)文字がある、 ってバグ/クレームがあり、その抜け文字調査とかをしたら、実は自分が以前に担当した作品でも 入力できない文字があるのが発覚したという恥ずかしいことがあり^^;、 目の前の仕事でも要るかもしれないので、とにかくチェック用の元になる 文字コード一覧くらいあったほうがいいなあ、と。

なんで、どっかから、コード表拾ってくるだけでいいんだけれどね…… ホントーは、jis->シフトjisルーチンのチェックプログラムだったのが発端^^;

ちょっと弄ると ずるずる と肥大化するプログラム……機能は、

て、とこ。

 JISの各バイトが0x21〜0x7eならばすべて出力していて定義上の抜け領域の チェック等はしないのは、調べるの面倒くさがってるだけなんだけど 各種拡張の事情を思うとこのほうが無難なような気もするし(文章書いてるうちに 区の指定機能を追加してしまった:-)。

ついでに、サンプルの出力例(っつうか、これらがメインだけれど)を並べとくです。


 ブラウザ/フォントの設定によっては、がたがたに見えますね。^^; 固定幅フォントでもおかしくなるのは、 こちら に かかれているような事情もあるかも (で、IEとかだと表示/フォントの大/中/小などを代えてみると運よく揃ってくれたり)。

さらに、ついでに、JIS漢字(JIS X 208)の区の分類も載っけとこう。 単に別の方のページにあったやつにJIS/シフトJISコードを埋め込んでみただけなんだけど。

JISシフトJIS内容
1〜 2区 2121〜227e 8140〜81fc 各種記号
3 区 2321〜237e 8240〜829e 算用数字、(ラテン)アルファベット
4 区 2421〜247e 829f〜82fc ひらがな
5 区 2521〜257e 8340〜839e カタカナ
6 区 2621〜267e 839f〜83fc ギリシア文字
7 区 2721〜277e 8440〜849e キリル文字
8 区 2821〜287e 849f〜84fc 罫線素片
9〜15区 2921〜2f7e 8540〜889e JIS X 208 で未定義
16〜47区 3021〜4f7e 889f〜989e 第一水準漢字
48〜84区 5021〜747e 989f〜eafc 第二水準漢字
85〜94区 7521〜7e7e eb40〜effc JIS X 208 で未定義
※ 終わりは各区で0x7E(94)未満の場合もあるが、一律 ??7Eにしている。 ※ JIS X 208 での未定義エリアには機種依存文字や その後の規格で拡張された文字が入っている場合がある模様。



 2000-10-04 fontx2形式のフォントをTTFにしたい


 個人的にやりたいと思っていることは、fontx2 や bfnt,bdf のフォントデータ をアウトラインフォントでなくビットマップフォントとして ttf に変換したいなあ、 ってこと……のはず、とりあえず。


 前に書いたとおり MS の sbit32 を用いればいけるかなあ、と、思うわけで、 cb2ttj のttfstubで生成したのと拾ってきたX窓なbdf と、あとようわからん ので ppem 11 とだけ書いたマトリクスファイルを用意して試すも、やはし 適当すぎたかうまく生成できず。 (bdfやTTF)フォントに関する知識が少なすぎて、わかんないことが多すぎるのだな。 でも、苦労すれば、なんとかなるような気もしてくる…… (2001-01追記. sbit32はunicodeでないと駄目?)


 が、面倒なので、これはほっといて cb2ttj を試してみることにする。 とにかく何でもいいからフォントを生成してみたくなってしまったのだな^^;
テキストみてるとcb2ttjなら 時間はかかるみたいだけど、ベタに変換するだけなら なんなくできるように思えてくるし。

cb2ttj のテキストみてると vflibbmp では VFLib ってのを使っていて、 vflibをみるとベクタフォントはTTFやPSだけでなく日本のJGや書体倶楽部なんか のフォントにも対応していて、ビットマップフォントの bdf にも 対応している……(vflibbmp みると結構使いやすそー。 自分の目的を思うと VFLib ってむちゃほしかったライブラリそのもの やん、っで、興味ひかれはじめてる)。

とりあえず bdf でお験し。どのようにビットマップフォントが拡張されるかは別として とにかくチャレンジ……

基本的に cb2ttj / cb2ttj98 の説明を元に作業。
(2002-01追記. cb2ttj98の頁はリンク切れのようです)。

まず今回作業用の cb2ttj フォルダを用意して, 本家 cb2ttを展開。 次にcb2ttjを展開(上書き)。 さらにcb2ttj98 の方のところで配布している ttfstub.exe をとってきて 上書き(mfc40.dll がみつかんないんで)。 環境変数設定して vfontcap の中にテストのフォントを見よう見真似で追加して ……。

で、つまずく。vfontcap ファイルの設定。いろいろ、弄って何度か挑戦。 ファイル名の大小、パスの区切り、相対パス、など思いつくものを適当に代えて 試すも、ダメ。 vftest すると
open error; min 。
うう、よう、わからん。

 あっさりお験しフォントできるつもりでいたので、ちと、 悔しい。悔しい。悔しい。

 なんだか ttf を生成せんことには気がすまなくなったので、 vflibbmp を諦め、その交替として、fontx2 から cb2ttj で使う 1024x1024 のビットマップファイルを生成するツールを 自前で用意、っていうことにしてしまう。

なんせ fontx2 は別のツール作るつもりで調べてたわけだし構造単純だったし、 cb2ttj に混ぜるための出力ビットマップファイルも 単純な構造だかもんで、作ってしまったほうが早いだろうと思ってしまったのだな。


で作った。
(モノは最後にリンク)

 単純な拡大なんで(こんなカンジ)、 アウトラインフォントにするのは本末転倒なんだけど、お験しだし。

 あと cb2ttj のとこも cb2ttj98 のところも Win でやると遅いとかかれていたので とりあえず、batch.exe が、指定バッチを指定回system()関数で(つまりcommand.comで)呼び出す ものだったので、別途 指定バッチを指定回分展開したバッチを生成してsystem()を一回だけの 呼び出しになるような batch.exe を作ってみた(ついでに時間計測を行うように:-)。 command.com 呼び出しのオーバーヘッドは減るが、そのかわりバッチサイズは異様にでかく なるので、メリットがあるかは環境しだいだろうが、まあ普通はメリットあるだろう、と思う。

 試したフォントはなるべく大きいフォントのほーがいいだろー、て、ことで、 ベクターあたりから落としてきた jpnzn24x.x11 というもとは X11 の bdf らしいモノの 24x24フォント。

 雷鳥(800) で実行......25分ほどで終了。

 ※ Win9x系での時間. Nt系だと16ビットDOSツール起動のオーバーヘッドが大きくて倍ぐらい時間がかかる模様。

 できたフォントをインストールして、表示してみたところ、 一応、英数かな漢字は一通りでてるぽい。
が。記号関係がボロボロ出ていない(◇になってる)。 ちょっと、悔しい。

漢字コード表のテキストで確認してみると どうやら1区の文字のみが(すべてではないけれど大半の文字が)ダメの模様。
 fnx2dynaが吐き出すビットマップをみたところその形のデータが生成できているし、 ひょっとして ttfstub あたりで何か都合わるいことしちゃったのかなあ……

とりあえず、不完全ながらも表示できたので、今回は、無意味に終わらずにすんで、よしとしとこー。

(2002-01追記).
 実は自作のbatch.exeがバグってただけというおおぼけをかましてた。
そのバグがとれたら、ちゃんと、変換できたみたい。 てことで一安心 (一年以上たってから気づくのは間抜けだけど^^;)。
ただし、cb2ttjのttfstubの都合、半角カナには未対応。
(これをなんとかしようと、ttfstubをいろいろいじってみたが、 うまくいかなかった...半角カナ自体は出るんだが全体の文字幅が壊れてしまってる...)。


で、本質の難儀はほっといて、 fnx2dyna を弄って、 ちょっと気になってしまった、拡大方法に別バージョンを用意。単純な拡大だけでなく、 心持、見た目の改善を試し見る。って、昔ながらの単純なやり方だけど。

さっきのが、こんなカンジに。

 フォントとして、これがよいか、かえってわるなってんちゃうんか、とか、思わないでも ないけれど、まあ、実験やし。
(拡大に関しては、あまり懲り出すと本題からづれてしまうので、これでやめておこう、っと)。


■今回つくったものは、これ
かならず、 cb2ttj / cb2ttj98 に目を通して必要なものを入手してください(でないと、環境がつくれない)。

[補足]
fontx2形式のフォントをTTFにしたい(補足)
cb2tt のWin32再コンパイル



 2000-10-06 fontx2形式のフォントをTTFにしたい(補足)


 前回つくったツール、ファイル名の大文字小文字をへまってて ダウンロードできなかったです。うう、すみません(直しました)。

 だけではなんなので…… cb2tt でフォント取得に用いられる ヘッダがDYNAな画像ファイルを確認するための SUSIE プラグイン を作ってしまったので、リンク しておきます。

それと、(TTF)フォントに関するページの付け足し。

X window system と日本語フォント は、 ネットで流れている(フリーの)フォントやツールを紹介をされているページ。
(フリーのフォントって、以外にある、ような、でもないような)

X11 で使用できるビットマップフォントのリスト は、 ネットにあるビットマップ・フォントをフォントサイズ(ドット数)と規格範疇で整理された 配布元へのリンクの表になっています。こちらのリンク集や kappa 20dot fonts をサポートされている方です。

Yet Another fontx2bdfは cソースでfontx2形式のフォントをbdf形式に変換するツールを配布されてるページ。

Mobile Computer Software Gallery は、 WindowsCE関係のページですが、mkttf という fontx2形式フォントを WinCE用TTFに 変換するツールがあります(Win9x NT系では使えない形式ですが)。

 しかし、なんてか断片を寄せ集めておろおろしながら自分で書いたあとに きっちとしたページをみかけて脱力するというのはよくあるような気が しますが、何で最初から見つけられないんだろ−>自分。



 2000-10-29 ctype.h,string.h,jctype.h,jstring.hを書いてみた

なんかこちらの リンク集にリンクしてもらえてる上にありがたいコメントまでしてくださってる ……うれし/はずかし状態かも^^;
でもフォント関係のお験しは止まってる……なんとかせねば。 この間から弄っているtga関係のツールもかたつけないとだし。 やりさしが多いなあ。 いつのまにか本業が多忙になってるしなあ。 (時間が立つとネタにしようと思ってたこと忘れていくし……)。

 といいつつ、  なんとなく気が向いたらC標準関数のstring.h系や、MSな旧日本語関数 jstring.h系なんぞを書きためていたりする。 こんな感じ。
ctype.hctype.cstring.hstring.cjctype.hjctype.cjstring.hjstring.c

 いまさらながら(自分が)自由に使える(ShiftJIS限定の) mbstring系関数がほしくなってたので揃えようと思い、 その土台ととしてjstring/string系関数をまず用意してみた、 という具合。 ろくにチェックしていないんでバグはあるだろうけれど とりあえず。

 特定のライブラリ化はとりたてて考えてなくて、ってより サブルーチン集、必要なものをコピーペーストする、みたいな もの。 必要になればソース分割すればいいや、だし、string.c なんぞは inline 定義にしてヘッダ化しちゃうのもありだろうと 思っていたり。

 いままでに、ターゲット用のコンパイラに stricmp がない、とか、 古いソフトのソースをコンパイルしようとしたら jstring.h が 使われていた、とか、もろもろが積み重なってたというのも あるし、昔汗で書いたことあるんだしなあ、だし、 単に、本業の悪循環な忙しさの反動から……てのも あるかな。不毛なことをしてるような気もするけれど、 Cの文字列系の関数は、一つ一つは数行〜数十行なので、 ちょっとしたパズルを解いてるのと同じようなモノなんで 気分転換になったり。

 書きっぱでいい加減なのは当然として、 jstring系は、仕様はっきりせんまま書いているので とくにいい加減。ctype.c,jctype.c内のチェックルーチンや 適当なルーチンを自前のとコンパイラ付属のとで出力を ファイル比較しただけですが……j系は結構各社各様 (stringもそうだけれど)。 てか、(MSの)jstring系(qc2,msc6?)の関数は mbstring系(msc7)へ変化したときに同名だけれど若干動作が 違うような変更があったように思うんで、どのMS互換かの 違いだったりもするんでしょうが、結構、主目的以外の使い かたがされたときの挙動は作り手しだいのような (lsic86 v3.3c試食, watcom-c 10.5, borland-c 4.5/5.5等でお験し... 肝心のMSのjstring付きが手元にないんで未確認)。 文字数を返すのか バイト数を返すのか、とか。

 mbstring系はまだ手をつけていないけれど、これは jstringを変形して足りないもの足せばなんとかなるかなあと。 ただ、最近の、SJIS以外も対応とか、ロケールとか 考えだすと頭痛いし面倒なので手が出せない…… ロケール関係はよくわかんないや (mbstring自体もようわかってないけれど)、で、string系も strcoll/strxfrm系 は未実装だし(ロケール関係は 手を出すと地獄だろうなあ、で、やらないやらない)。


 と。
 プログラムの効率等は、string系の小さいものとかは 多少考えたけれど、あまり気合はいっていない、です。

 当初はどっちかというと読みやすさを優先して while 等 の条件中でも代入はしないとか思うも、ちょっと書きだすと ついオプティマイズ能力が低い場合などがちらついて 昔ながらのこすい書き方になってしまったけれど (ANSI-Cで書いてるけれど、あとで kran 書けやすいよう にとかも思ってしまうし)。

 でも結局想定する CPU のアーキテクチャ次第だし コンパイラの性能次第だし、すべてにおいて最適なんて 有りはしないんだし……でもよりマシになる可能性に 思いがいったり。

Cの場合まず int のサイズまで拡張されてしまうから、へたに char や short を使うと型拡張の命令が余分に生成されて返ってマズかったり、 でもオプティマイズが利いたりlsic86のような方針のコンパイラも あるので最適な型を指定したほうが有利な場合もあるだろうし。 型拡張も符号付/無でペナルティのない(扱いが同じ)CPU もあれば 68K/x86など古いマイコン系は符号無しのほうが有利だったり、 SHのように(Cの実情にあわしたのか)符号有りのほうが有利なCPU もあるし。で  *d == *(unsigned char *)s ならばバイトで比較してくれるのに *d == (unsigned char)*s だと intに符号拡張してから比較する コードを吐き出されたことがあったなあ、とか、(unsigned char)c なら専用の符号無し拡張命令を生成するのに同じ動作の c&0xff は 律儀にand命令になったのもあるなあ、とか (ちゃんとどちらも同じに最適にしてくれるのもある)。符号無しの c / 256 は c >> 8 に等価なコード生成するのに符号有りの c / 256 だと割算コード吐き出す奴がいたり(符号有りでも>>8にしてくれるのもある)。
 モトローラ系の ポストインクリメント(r++)、プレデクリメント(--r) があるCPUならば、c = *s++ のように処理して、最後に s - 1 したほうが いいだろうけれど、そーでないCPUならば、必要なときに足すだけのほうが いいし。
 nバイト指定時のループも、エンドアドレスを計算してそれとの 比較でループしたほうがよいCPUと、ループカウンタでデクリメントした ほうがよいCPUがあるだろうし。
 条件代入のあるCPUならば (a < b)&&(b < c)なんかは (a < b)&(b < c)のほうがいいかなあ、って気もしてくるし……
memcpy,memset,strcpy,strset などの単純なモノは long* など 幅広の型で処理できるところはしたほうがよさげなCPUも多いし。

などと、必要以上に思ってもしゃーないことがつい頭の中に 浮かんでしまうのだけど……結局あまり考慮されてない半端な 書き方になってしまうのだなあ(jstring系とか複雑に なってくるとハナッから考えないし)。

 しかし、思った以上に非互換で難儀だった。 もっとお手軽メモのはずがちょっとテストしては修正が必要になる…… 昔参考にしたQCな本が見当たらなくて, turbo/borland 系書籍/マニュアルを基準にしてたんですが 他と比較すると結果が違うし,bcのライブラリはマニュアルとも挙動 違う場合あるし(T T)( こちらが勘違いしてるだけなような気もするけれど)。 (なんとか手持ちのMSCを復旧したほうがいいんだろうけれど。 NEC98な3.5寸1.25Mを読む手立てをなんとかしないと駄目かなあ)。

まあ、jctype,jstring は現在では捨てられたライブラリだし、この 混乱ぶり思うと使わないほうがいいし。使うんなら、共通範囲を見極めるか 自前でルーチン用意するのが無難ってとこか。 (懸案は、mbstring系もこの状況引きずっているんだろうかな、てとこか)。



 2000-10-29 watcom-cライブラリでのNULL対策
 string/jstringを弄っていて思い出したのでついでに。

 watcom-c は(他と違って) 標準ライブラリで、たまーに NULL ポインタ対策されているものがあって free(0)やfclose(0)とかやっても大丈夫だったりする親切設計 (あとmalloc(0)も大丈夫)。 で string系でも strcmp だけは NULL を渡しても動いてくれる模様 (v10.5だけかもしれないが... strcpyなどはもちろんのこと stricmpやmemcmpなども NULL対策されず落ちるのに)。

 でも、これが、watcom-c用のソースを他のコンパイラ用に移植するときに ハマる原因の一つだったりする。watcom で正常に動いていたツールを bcc32 等でコンパイルして実行すると不正な処理で落ちたりするのは この手のことが原因の場合が多い。なので、そのようなときは strcmpやmalloc/free,fcloseなどの引数がNULLや0になっていないか 追っかけるはめになる。でもって、これら以外にも、 そのような関数はまだあるとも思えるので、結局,ライブラリ関数を 疑うはめになる^^;



 2000-11-04 標準関数の引数NULLチェック, あるいは s++ == s++ の互換性

 watcom-cのstrcmpの引数の件から、 連鎖的に以前にやった関数の引数(NULL)チェックマクロの ことをちょっと思いだした。
(「デバッガ使えばええやん」て言われそうだが、まあ、使えない環境もあるんだしで)。

 たんに assert の特化版を作るだけなんだけど。
 assert(式)はCプリプロセッサ機能の、 条件コンパイルと#defineマクロ、および、
__FILE__ は現在のコンパイル中のソースファイル名に、
__LINE__ はそれの使われた行の行番号に、
(C99 なら __FUNCTION__ はそれが使われた関数名に)
置き換わる機能、マクロ定義文字列中でマクロ引数 e が、#eのように指定されると "e" のように文字列に置き換わる機能を用いて、

#ifndef NDEBUG
#define assert(a)  do {\
        if ((a) == 0) {\
            printf("Assertion failed: %s, file %s, line %d\n", #a, __FILE__, __LINE__);\
            exit(1);\
        }\
    } while(0)
#else
#define assert(a)
#endif
のような感じで作れられて、#defineマクロ NDEBUG が未定義のときは assert(式)の式が真ならそのまま通過し偽ならばファイル名と 行番号付きでエラーメッセージを出してストップし、 でもってユーザによってNDEBUGが定義されれば(リリース版として) このエラーチェックを空の文にして何もしない、というふうに できる。

 で、自前の関数の場合、関数の入り口のところで表明がわりに assert で引数チェックをしたりするのだけれど、標準関数だとか 使用個所が多いく関数が実際に呼ばれた個所のファイル名/行番号が 知りたい場合とかは、自前のヘッダで細工したりする。

 ANSI(ISO)規格 C での#defineマクロ展開は、定義名自身が 定義文字列中に現れた場合、(以前に定義済みの)自身を 展開するのでなく、ネストせずその名前のままになる……ので、

#ifndef NDEBUG
#define FNC_ARG_ASSERT(f,e) \
	((e) ? 0 : (printf("%s %d : %sの引数で %s が偽\n" \
		, __FILE__, __LINE__, #f, #e), ((int(*)(int))exit)(1)))

#define strcmp(a,b)	(FNC_ARG_ASSERT(strcmp, (a) && (b)), strcmp((a),(b)))
#define strcpy(a,b)	(FNC_ARG_ASSERT(strcpy, (a) && (b)), strcpy((a),(b)))
#define free(a)		do {FNC_ARG_ASSERT(free, (a)); free(a);} while(0)
#endif
のようなマクロを作れる。
(※ printfの引数で __FILE__ やら #f やらを文字列連結機能に任して繋げてしまう 書き方もあるが、それをすると利用個所によっては文字列データが異様に増えてしまい かねないので同じ文字列を一つだけにする最適化を期待してマッチしやすいよう 上記のように書いたほうが無難に思う。11/20(追) 上記は __FILE__ 名を文字列連結のほうがお徳な可能性が多いかも^^;
 ※ exitへのキャストは書いてて気持ちが悪いんだけど…… マクロでの使用を思うと exitはvoidでないほうがありがたいなあ、とか、 void型は値を返さない、でなく、不定な値を返す、としてせめて警告にとどめて ほしいとかつい思ってしまうけど、利用頻度やデバッグ性を思うとキャストで 解決できる今の仕様が一番無難なんだろうなあ)

例えば、dst=NULL のときに strcmp(dst,src) をすると

	foo.c  101 : strcmpの引数で (dst) && (src) が偽
と実行時に使われた個所のファイル名/行番号で表示されることになる。 で、このようなマクロを、調べたい(怪しい)関数の条件に合わせて 記述してヘッダを作れば結構チェックできるかも……

でも、実んとこ、問題がある。
引数の副作用の問題。

 チェックする引数を複数回使用することになるので、副作用のある式が 引数になる場合、チェック時はバグってリリース時は正常、とかいうこと が発生してしまう。たとえば strcmp(a++,b++) の場合は
(FNC_ARG_ASSERT(strcmp, (a++) && (b++)), strcmp((a++),(b++))) のようになって、a,bの値がおかしくなる可能性がある。

 だもんで、(今回作成の)プログラムでは副作用のある式は関数の引数に しちゃ駄目と(コーディング規約)決めて徹底しないと意味がないどころか まずい。

 で、関数の引数に副作用のある式を書かないことにしたとしても、ただ それだけでは危険はあまり変わらないので、マクロのチェック条件を さらに増やして、

#define strcmp(a,b)    (FNC_ARG_ASSERT(strcmp, (a) && (a) == (a) && (b) && (b) == (b))\
                            , strcmp((a),(b)))
のようにしてみる。strcmp(a++,b) の場合 (a++) == (a++) となって、 a++ == a++ のような書き方をした場合の挙動は、 ANSI(ISO) C では未定義だったか処理系依存とされていたはずで、 バグの可能性を思えばエラーか警告くらいは出しそうに思う。 でもって、もちろんコンパイラによるが a++ == a++ は偽になる 可能性が結構あるように思う(が、さらに引数が a[i++] や *tbl++ の ような場合は、チェックされないなさそう……)。

 と、書いてて気になったのでちょっとチェックしてみた。

-----------------------------------------------------
#include 
#include 
int main(void)
{
    static char tmp[32];
    char *s, *src = "abcdefg";
    s = src;
    printf("s++ == s++ は偽になってほしい...%s\n", (s++ == s++)?"NG!(真だった)":"ok");
    s = src;
    if (*s++ == *s++)
        printf("比較 *s++ == *s++ で、s が同じアドレスで比較された\n");
    else
        printf("比較 *s++ == *s++ で、s が違うアドレスで比較された\n");
    strcpy(tmp, "abcdefg");
    s = tmp;
    *s++ = *s++;
    printf("s=\"abcdef\" で、 *s++ = *s++ をした結果は \"%s\"\n", tmp);
    return 0;
}
-----------------------------------------------------
を、複数のコンパイラでコンパイルして実行してみただけなんだが…… 結果は、

ありゃりゃ。思った以上にばらばら、だわ(泣)。
しかも普及度/使用度を思えば vc の s++ == s++ の結果が NG なのは、このマクロは致命的に失敗作ということになるなあ^^;。

でもって、期待はずれなことに、このような副作用に対する 警告はどうやらコンパイラにはなさそうだ (gcc,bcc32,clで警告レベルをきつくしても 警告すら表示されないか、警告の指定にない)。

 はあ、期待しすぎの思いこんだらだったんだなあ。
(これに限らず規格どうこうでなく、 ある種のバグ対策になるコーディングスタイルを支援するチェックを コンパイラはもっと付けてほしいものなんだけれどなあ。gccとかには いくつかそのようなものがすでにあるんだし……)。

 しかしこのテスト結果は結構面白いかもな。
lsi-c, watcom-c, lcc の結果はある種、予想通りというか単純だ。 項を評価した直後でインクリメントしているんだろう。

 vc も結果は違うけれど、式全体の評価が済んだ時点で インクリメントするという方針なんだろう。とくに
 *s++ == *s++
 *s++ = *s++;
とやったときに、同値になったり結果の文字列が元のままになる、というのは ある種の期待通りの動作ともいえる。 もちろん C で直接書いているときにこのようには普通書かないし 書いたらバグなんだけれど (だから怪しい挙動をしてくれたほうがバグは発見し易いのだけれど)、 マクロ展開(やひょっとしたらinline 展開の内部での都合とか?)の 結果として考えると成ってしまうこともありそうだし (これもホントはポインタのアドレスが同値かどうかのチェックを 記述すべきなんだけれど)、 膨大なソース量のときは文法的でなく見目どおりに期待した動作をしたほうが 納期内にプログラムが完成できるのかもしれない(コンパイラ依存なだけで 困るのは保守者/移植者:-)

(11/20追記 上記の、"期待した動作"というのは、寝ぼけてますね。 下記のs++ == s++ と同様 *s++ == *s++ でも *s++ = *s++ でも (どのコンパイラでも当然)ポインタ s は2つ進んでしまうので、 動作が違う…… その行のみならば一見辻褄があいそうな気もするけれど、
  while (*s++ != *s++) {}
  *s = 0;
のように(マクロ展開の結果が)なったとしたらばすぐ破綻しちゃうんだった)

 で、そう思うと bcc32 と gcc は特別に判断しているんかなあ、って 思える。とくに gcc は、用途的に有用そうなほうを選択しているように感じる。 私のようなマクロを書いちゃうものには s++ == s++ は偽になってくれてありがたいし、 でも、*s++ == *s++ や *s++ = *s++ はちぐはぐにならず 同じ値になるので とりあえず期待通りのことが多そうだし。
 bcc32 は、本来はlcc/wcl386/lsicとかと同様の単純なものだったのを VC互換?のためとか何かの都合で *s++ = *s++; だけは対策を講じたのかなあ って邪推してしまう(逆にいうと *s++ == *s++ に関してはまだ非互換。 って、あくまで *s++ == *s++ なんて書き方は C(たぶんC++も)の仕様からすれば 処理系依存で使うべきでないので非互換でもかまわないんだが)。

 と、vcの挙動でちょっと気になったので s++ == s++ 後の sのアドレスを 確認したんだけれどこれはさすがに 2つ値が進んでいた……ホっだし、これでいいんだけれど。
 もし一つの式中に一種類の変数への複数回のポストインクリメントがあった場合に、 式計算後に増える値自体も処理系依存にしていいならば、 いっそ処理系依存範囲でのポストインクリメントは1つしか インクリメントしないようにしてしまえば、 マクロ引数での副作用がなくせる場合が増えて便利になるかも、 とか、って思ってしまったのだった。


で、当初の標準関数の引数チェックだけれど、副作用を考慮するとカッコ悪い方法 だけれど,

------- ヘッダ
#ifndef NDEBUG
#define strcmp(a,b)	argchk_strcmp(__FILE__, __LINE__, a,b)
#endif
------- どっかのソース
char *argchk_strcmp(char *fname, int line, char *a, char *b)
{
    if (a == NULL) {
        printf("%s %d : strcmpの第1がNULL\n", fname, line);
        exit(1);
    }
    if (b == NULL) {
        printf("%s %d : strcmpの第2がNULL\n", fname, line);
        exit(1);
    }
    return strcmp(a,b);	// ヘッダを誤魔化して。あるいは、自前でコードを記述
}
のようにするかなあ…… て結局、面倒くさいのでこんなこと、したくないよなあ。
やっぱり、環境あるなら

「デバッガ使えばええやん」

が正義だろう。
とほほ。






[前] [次] [戻る]