C99に対応した標準Cライブラリの実装レポートを行っていきます。

プロフィール 

高木信尚

Author:高木信尚

ホームページ
ブログ

最近の記事 

最近のコメント 

最近のトラックバック 

月別アーカイブ 

カテゴリー 

ブロとも申請フォーム 

この人とブロともになる

ホーム 全記事一覧

 

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
--/--/-- --:--|スポンサー広告

 

<locale.h>で宣言される関数のうち、もう一つはsetlocaleです。この関数は、特定のカテゴリを指定したロケールに設定します。何度も書いてきましたが、とりあえずは"C"ロケールだけを実装しますので、今回は"C"ロケールにのみ対応したsetlocaleを扱うことにします。""を指定した場合も"C"ロケールになります。

#include <locale.h>

char *setlocale(int category, const char *locale)
{
  char *result = NULL;

  if (0 <= category && category <= 5)
  {
    if (locale == NULL)
    {
      result = "C";
    }
    else
    {
      if (*locale == 'C')
        ++locale;
      if (*locale == '\0')
      {
        __lconv = &__lconv_C;
        result = "C";
      }
    }
  }
  return result;
}

"C"ロケール以外に対応させるには、おそらく全面的な書き直しになりますが、当面これで済ませたいと思います。
スポンサーサイト
2006/02/02 00:48|文化圏固有動作TB:0CM:0

 

今回はlocaleconv関数の実装に関するお話です。localeconvは、現在のロケールに応じた、数値や金額の変換方法に関する情報を取得するための関数です。

今回、とりあえずは"C"ロケールにのみ対応することにしましたが、他のロケールに対応する余地を残すために、現在のロケールに応じたlconv構造体へのポインタを、いったん大域変数__lconvに格納することにします。そして、__lconvは、"C"ロケール用のlconv構造体__lconv_Cを指すように初期化します。

/* __lconv_C.c */
const struct lconv __lconv_C = {
  ".", "", "", "", "", "", "", "", "", "",
  CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX,
};

const struct lconv *__lconv = &__lconv_C;

そして、効率化のため、__lconvおよび__lconv_Cは<locale.h>の中で宣言し、localeconvはインライン関数として実装することにします。

#ifdef __cplusplus
extern "C" {
#endif
extern const struct lconv __lconv_C;
extern const struct lconv *__lconv;
#ifdef __cplusplus
}
#endif

static __inline__ struct lconv *localeconv(void)
{
  return (struct lconv*)__lconv;
}

ここで、"C"ロケールしか扱わない場合は、いったん__lconvを介して間接参照するのは無駄ですので、

#ifdef __ONLY_C_LOCALE
#define __lconv  (&__lconv_C)
#endif

として、"C"ロケール限定の場合は、__lconv_Cを直接アクセスするようにします。

ここで一つ大きな問題が発生します。それは、__lconv_Cの初期化に使用しているCHAR_MAXです。これは規格上、値がCHAR_MAXに定められているためにそのようにしているのですが、CHAR_MAXの値はコンパイルオプションによって、すなわち、char型の符合の有無によって異なってくるのです。

仕方がないので、上記の定義はデフォルト(=符号なし)の場合のものとし、符号付の場合は、__lconv_Cの代わりに__lconv_C_sを、__lconvの代わりに__lconv_sを使用するようにせざるを得ないようです。結局のところ、

#ifndef __CHAR_UNSIGNED__
#define __lconv_C  __lconv_C_s
#define __lconv    __lconv_s
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern const struct lconv __lconv_C;
extern const struct lconv *__lconv;
#ifdef __cplusplus
}
#endif

#ifdef __ONLY_C_LOCALE
#define __lconv  (&__lconv_C)
#endif

static __inline__ struct lconv *localeconv(void)
{
  return (struct lconv*)__lconv;
}

のように、条件付きコンパイルが必要になりそうです。

ところで、規格上は<locale.h>をインクルードせず、ユーザーが自分でlconv構造体の定義と、localeconvの関数原型を記述して使用してもよいことになっていますが、さすがにその場合は対処の方法がありません。

今回は、厳密には規格に反するものの、<locale.h>ヘッダを必須とした方が有益であると判断し、localeconvの外部定義は行わないことにします。

なお、他の標準ライブラリ関数は、見かけ上、localeconvを呼び出さないかのような実装を行うことが要求されています。通常、localeconvを用いて取得する情報(小数点文字など)には、__lconvを使用することにします。


▽続きを読む▽
2006/01/30 20:35|文化圏固有動作TB:0CM:0

 

今回も地味なライブラリ要素ですが、ロケールを制御するための<locale.h>です。ロケールは、標準Cライブラリのいろいろなところで顔を出しますので、先に片付けてしまうことにします。ただし、今回は"C"ロケールのみを対象とし、それ以外については拡張の余地を残すだけにしておきます。

ライブラリが"C"ロケールしか扱わないことを明示的に宣言するために、__ONLY_C_LOCALEというマクロを規定し、これが定義されている場合は、"C"ロケール以外は考慮しないことにします。このマクロが定義されていなくければ、とりあえずは"C"ロケールしか使えませんが、他のロケールのことも考慮することにします。

まず、<locale.h>でもNULLマクロを定義する必要がありますが、内容は<stddef.h>のものと同じですので、そちらを参照してください。次に、LC_~で始まるロケールのカテゴリを表すマクロですが、整数定数式であればよいので、適当に連番を振ります。

#define LC_ALL      0
#define LC_COLLATE  1
#define LC_CTYPE    2
#define LC_MONETARY 3
#define LC_NUMERIC  4
#define LC_TIME     5

そして、lconv構造体を宣言します。

struct lconv {
  char *decimal_point;
  char *thousands_sep;
  char *grouping;
  char *int_curr_symbol;
  char *currency_symbol;
  char *mon_decimal_point;
  char *mon_thousands_sep;
  char *mon_grouping;
  char *positive_sign;
  char *negative_sign;
  char int_frac_digits;
  char frac_digits;
  char p_cs_precedes;
  char p_sep_by_space;
  char n_cs_precedes;
  char n_sep_by_space;
  char p_sign_posn;
  char n_sign_posn;
};

最後は、localeconvおよびsetlocaleの関数原型です。

#ifdef __cplusplus
extern "C" {
#endif

struct lconv *localeconv(void);
char *setlocale(int, const char*);

#ifdef __cplusplus
}
#endif

関数原型は、今回始めて登場しましたので、簡単に書き方について解説します。普段からC言語を使われている方にとっては、今さら関数原型の書き方なんかと思われるかもしれませんが、標準ライブラリの場合には、少しだけコツがあります。

というのは、上記を見ていただければお分かりのように、関数原型では仮引数名を記述していません。これは、仮引数に一般識別子を使うと、ユーザーが同名のマクロを定義した際に問題が生じるからです。予約済み識別子以外はユーザーに開放しなければなりませんので、敢えて仮引数名を省略しています。

インライン関数のように、どうしても仮引数名が必要な場合には、仮引数名は予約済み識別子を使用しなければなりません。同様に、インライン関数の内部で使用する自動変数やラベル名等も、すべて予約済み識別子でなければなりません。

長くなるので、今回はヘッダの記述だけの解説だけにしておきます。それぞれの関数の実装は次回以降にお話します。


▽続きを読む▽
2006/01/30 17:12|文化圏固有動作TB:0CM:0

ホーム 全記事一覧

ブログ内検索 

お勧め書籍 

RSSフィード 

リンク 

このブログをリンクに追加する

Copyright(C) 2006 TAKAGI Nobuhisa All rights reserved.
Powered by FC2ブログ. template designed by 遥かなるわらしべ長者への挑戦.
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。