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

プロフィール 

Author:高木信尚

ホームページ
ブログ

最近の記事 

最近のコメント 

最近のトラックバック 

月別アーカイブ 

カテゴリー 

友達申請フォーム 

この人と友達になる

ホーム 全記事一覧

 

2008/07/06 21:09|

 

<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フィード 

リンク 

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

Powered By FC2ブログ 

Powered By FC2ブログ
ブログやるならFC2ブログ

Copyright(C) 2006 TAKAGI Nobuhisa All rights reserved.
Powered by FC2ブログ. 無料ホームページ アフィリエイト レンタルサーバー FC2ブログ 一戸建て template designed by 遥かなるわらしべ長者への挑戦.