蝉は、やがて死ぬる午後に気づいた。ああ、私たち、もっと仕合せになってよかったのだ。:2010年03月22日分

2010/03/22(Mon)

TR14652 extension for LC_TIME

ISO/IEC TR14652[pdf]のダメなところは、せっかく新しいフィールド追加したのに、それを使うためのAPIが
nl_langinfo(3)に指定する nl_item 定数を含めてまったくどこにも定義されてないことだよな。

たぶん唯一の TR14652 実装である glibc2 では nl_item 定数として

[nl_item定数]			[locale(1) -k]
_NL_TIME_WEEK_NDAYS		week-ndays
_NL_TIME_WEEK_1STDAY		week-1stday
_NL_TIME_WEEK_1STWEEK		week-1stweek
_NL_TIME_FIRST_WEEKDAY		first_weekday
_NL_TIME_FIRST_WORKDAY		first_workday
_NL_TIME_CAL_DIRECTION		cal_direction
_NL_TIME_TIMEZONE		timezone

が定義されてるんだけど、これ将来的にどーなるかわからんので、NetBSD/Citrusの
_locale_t(=_locale_impl_t) に用意したキャッシュ(=items)には含めたくないなぁ、と。

struct _locale_cache_t {
	const unsigned char *ctype_tab;
	const short *tolower_tab;
	const short *toupper_tab;
	size_t mb_cur_max;
	struct lconv ldata;
	const char *items[ALT_DIGITS + 1];
};

struct _locale_impl_t {
        struct _locale_cache_t cache;
...

ということで他の normal な nl_item定数と区別するために、MSB立てるとかしときたいのだけど

typedef long nl_item;

と signed の MD な型だったりするので微妙に書きづらい、LONG_MINとか使うのはevilだしなぁ。どしよ。

そもそもglibc2レベルでnl_itemを増やすと、全部のitemに対してcacheを用意するのは無理があるのよな。

	wchar_t *ws;
	ws = (wchar_t *)(char *)nl_langinfo(_NL_WABDAY_1);

みたいにchar*にキャストしてワイド文字返したりもする、かなりフリーダムな実装なんで非常に頭が痛い。
# ただしこの方がwprintf(3)なんかの実装に性能的に有利ではある、なんというパラノイア。

ま、どっちにしろ今後 struct lconv のフィールドや nl_item の数が増えることを想定して
struct _locale_cache_tの実装は直しといた方がいいよな。

--- setlocale_local.h   2 Dec 2009 08:53:03 -0000       1.3
+++ setlocale_local.h   21 Mar 2010 19:24:05 -0000
@@ -40,12 +40,12 @@
        const short *tolower_tab;
        const short *toupper_tab;
        size_t mb_cur_max;
-       struct lconv ldata;
-       const char *items[ALT_DIGITS + 1];
+       struct lconv *ldata;
+       const char **items;
 };

 struct _locale_impl_t {

↑の方が後々よさげ、去年実装したとき手抜き過ぎた。

といっても locale_t はコンストラクタありなので後方互換は問題にならないし
別に今の実装のままで増やしてしまってもいいのだけど。でもしかしこの部分って

__inline struct lconv*
localeconv_l(locale_t l)
{
	return ((struct _locale_impl_t)l)->cache.ldata);
}
__inline char *
nl_langinfo_l(nl_item item, locale_t l)
{
        const char *s;
        s = NULL;
        if (item >= D_T_FMT && item <= ALT_DIGITS) {
                
                s = ((struct _locale_impl_t)l)->cache.items[(size_t)item];
        }
        if (s == NULL)
                s = "";
        return __UNCONST(s);
}

みたいにinlineとして提供し性能を稼ぐ必要あるかもなーという想定で実装してたかんね(俺もパラノイア)。
そうすっと struct lconv のフィールドや nl_item の数が増えると後方互換が壊れるので
回避にあんまり美しくないコードを書く羽目に。

うし、この変更だけは少なくとも 6.0 にはつっこもう。

[C1X] *cpy_s in TR24731-1

_tcscpy_sネタ。
ISO/IEC TR24731-1[pdf]では以下の場合、set_constraint_handler_s(3)でセットした例外ハンドラを呼ぶことになってますな。
(手元のNetBSD用に 以前書いた実装をみて書いてるので最新は知らんがな)

TR24731-1では例外ハンドラはデフォではNULLなのでそのまま処理続行のはず。
まぁVC++だとデフォでabort_handler_s(3)がセットされてるのじゃないでしょうか。

まぁそれはおいといて、元の質問見たけど

 numberOfElements

    コピー先の文字列バッファのサイズ。

をバイト数と勘違いする人がいるのだなぁと、あくまでコピー先の文字型の配列長でんがな。
これを誤植とかもうね(以下略

int bytes = ( ( _tcslen(c) + 1 )*sizeof(TCHAR) );
if ( fn = (TCHAR*)malloc( bytes ) ) _tcscpy_s( fn, bytes, c );

じゃなくて

int len = ( ( _tcslen(c) + 1 ) );
if ( fn = (TCHAR*)cmalloc( len, sizeof(TCHAR) ) ) _tcscpy_s( fn, len, c );

だぁね、つか長さ調べてその分アロケートするなら_tcscpy_s使う意味ないし、この関数は

TCHAR buf[BUFSIZ];
_tcscpy_s(buf, BUFSIZ, c); 

のように、固定長バッファをあふれさせないように使う関数だかんね。
リンク先の回答にもあるとおり動的割り当ておkなら _tcsdup() 使えと。