|
|
|
| 2008/07/06 21:09||▲
|
|
|
今回は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を使用することにします。
▽続きを読む▽
"C"ロケール以外に対応させるには、setlocale関数を用いて__lconvに異なるlconv構造体を参照させるようにするか、あるいは、"C"ロケール以外の単一のロケールに固定するのであれば、__lconv_C.cを必要なロケールに対応したファイルに置き換えてしまうか、どちらかの方法で対応することになるでしょう。後者の方法(__lconv_C.c)は正当な方法ではありませんが、メモリの少ない環境では有効です。
|
| 2006/01/30 20:35|文化圏固有動作|TB:0|CM: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言語を使われている方にとっては、今さら関数原型の書き方なんかと思われるかもしれませんが、標準ライブラリの場合には、少しだけコツがあります。
というのは、上記を見ていただければお分かりのように、関数原型では仮引数名を記述していません。これは、仮引数に一般識別子を使うと、ユーザーが同名のマクロを定義した際に問題が生じるからです。予約済み識別子以外はユーザーに開放しなければなりませんので、敢えて仮引数名を省略しています。
インライン関数のように、どうしても仮引数名が必要な場合には、仮引数名は予約済み識別子を使用しなければなりません。同様に、インライン関数の内部で使用する自動変数やラベル名等も、すべて予約済み識別子でなければなりません。
長くなるので、今回はヘッダの記述だけの解説だけにしておきます。それぞれの関数の実装は次回以降にお話します。
▽続きを読む▽
ところで、<locale.h>も<stddef.h>と同様、C++では、ヘッダ内で宣言・定義する識別子はstd名前空間に入れなければ成りません。したがって、いったん<clocale>を記述し、<locale.h>から<clocale>をインクルードして、各識別子をusing指令で大域的な前空間に導入することになります。
/* locale.h */ #include <clocale> using std::lconv; using std::localeconv; using std::setlocale;
|
| 2006/01/30 17:12|文化圏固有動作|TB:0|CM:0|▲
|
|
|
今回からホスト環境用のライブラリの実装に入ります。ホスト環境用といっても、最初はやはり地味なライブラリ要素が続きます。今回はエラー番号のためのマクロを定義する<errno.h>の実装です。
標準規格で、<errno.h>の中で定義することを要求されているマクロは四つです。すなわち、errno、EDOM、ERANGE、およびEILSEQです。このうち、errnoを除く三つはエラーの状態を表す正の整数値に定義します。値は勝手に決めてもよいのですが、一応Cygwinに合わせて
#define EDOM 33 #define ERANGE 34 #define EILSEQ 138
としておきます。
さて、肝心のerrnoですが、最も簡単な実装は、int型の外部変数にすることです。シングルタスクの環境であればこれで十分ですが、マルチタスクの場合にはいろいろ厄介な問題がついて回ります。
errno以外にも、strtokやasctimeなど、静的変数をアプリケーションに参照させるタイプのものは、排他制御だけではマルチタスク対応になりません。タスクごとに、それぞれ異なる記憶域を設けて、実行中のタスクに応じた記憶域を参照させなければならないからです。
タスクに対して局所的な記憶域の扱いは、後ほどまとめて対応することにし、とりあえず現時点では最小限の実装にとどめておきます。ただし、ヘッダ自体はこれでフィックスしたいので、errnoの実体を関数呼び出しにしておきます。
#ifdef __cplusplus extern "C" #endif int *__errno(void);
#define errno (*__errno())
関数__errnoの定義は、現段階では
int *__errno(void) { static int e; return &e; }
とするにとどめておきます。
|
| 2006/01/30 01:22|プログラム診断|TB:0|CM:0|▲
|
|
|
前回に引き続き<stdint.h>についてです。今回は、<stdint.h>内で定義されるマクロに関してお話します。
まずは、intN_t型、uintN_t型、intmax_t型、およびuintmax_t型の定数を作るためのマクロです。実際には、それらの型に完全に合致するわけではなく、それらの型を汎整数拡張した型にするものです。これらのマクロは、C++では、__STDC_CONSTANT_MACROSマクロが定義されている場合のみ定義されるので、
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) /* 内容 */ #endif
のようにしなければなりません。そして、実際のマクロ定義は、
#define INT8_C(n) (n) #define UINT8_C(n) (n) #define INT16_C(n) (n) #if __INT_MAX__ == 32767 #define UINT16_C(n) (n##U) #else #define UINT16_C(n) (n) #endif #define INT32_C(n) (n##L) #define UINT32_C(n) (n##UL) #define INT64_C(n) (n##LL) #define UINT64_C(n) (n##ULL) #define INTMAX_C(n) (n##LL) #define UINTMAX_C(n) (n##ULL)
のようになります。ここで、UINT16_Cに関しては、int型が16ビットの場合には、unsigned int型にしなければなりませんので、添え字の U を付加しています。
次は、最大最小値に関するマクロです。これらのマクロは、C++では、__STDC_LIMIT_MACROSマクロが定義されている場合のみ定義されるので、
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) /* 内容 */ #endif
のようにしなければなりません。それでは、実際のマクロ定義を見ていきます。<stdint.h>で定義した型に関しては、定義に用いた基本型の最大最小値と同じになるので、<limits.h>のときと同様に、
#define INT8_MAX 127 #define INT8_MIN (-128) #define UINT8_MAX 255 #define INT16_MAX 32767 #define INT16_MIN (-32767-1) #define UINT16_MAX 0xffff #define INT32_MAX __LONG_MAX__ #define INT32_MIN (-__LONG_MAX__-1) #define UINT32_MAX (__LONG_MAX__*2UL+1) #define INT64_MAX __LONG_LONG_MAX__ #define INT64_MIN (__LONG_LONG_MAX__-1) #define UINT64_MAX (__LONG_LONG_MAX__*2ULL+1)
#define INTMAX_MAX __LONG_LONG_MAX__ #define INTMAX_MIN (__LONG_LONG_MAX__-1) #define UINTMAX_MAX (__LONG_LONG_MAX__*2ULL+1)
#if defined(__H8300__) || defined(__NORMAL_MODE__) #define INTPTR_MAX __INT_MAX__ #define INTPTR_MIN (-__INT_MAX__-1) #define UINTPTR_MAX (__INT_MAX*2U+1) #else #define INTPTR_MAX __LONG_MAX__ #define INTPTR_MIN (-__LONG_MAX__-1) #define UINTPTR_MAX (__LONG_MAX*2U+1) #endif
と定義することができます。
<stdint.h>では、他のヘッダで定義される整数型の最大最小値も定義する必要があります。まずは、size_t型の最大値です。<stddef.h>では、size_t型を__SIZE_TYPE__マクロを使って定義しましたが、#if指令で使用できる定数にするためにはキャストが使えないため、ここではsize_t型の実際の型を知る必要があります。そこで、
echo __SIZE_TYPE__ | h8300-hms-gcc -E - [オプション]
といったコマンドを使って、__SIZE_TYPE__の定義内容を調べました。すると、H8/300、およびH8/300HとH8Sのノーマルモードでは、unsigned int型、それ以外(H8/300HとH8Sのアドバンストモード)ではunsigned long型でした。したがって、
#if defined(__H8300__) || defined(__NORMAL_MODE__) #define SIZE_MAX (__INT_MAX__*2U+1) #else #define SIZE_MAX (__LONG_MAX__*2UL+1) #endif
と定義することができます。
次に、ptrdiff_t型の最大最小値ですが、同様の方法で__PTRDIFF_TYPE__の定義内容を調べると、やはり、H8/300、およびH8/300HとH8Sのノーマルモードでは、int型、それ以外ではlong型でした。したがって、
#if defined(__H8300__) || defined(__NORMAL_MODE__) #define PTRDIFF_MAX __INT_MAX__ #define PTRDIFF_MIN (-__INT_MAX__-1) #else #define PTRDIFF_MAX __LONG_MAX__ #define PTRDIFF_MIN (-__LONG_MAX__-1) #endif
のように定義することができます。
次は、wchar_t型の最大最小値ですが、やはり同様に__WCHAR_MAX__の定義内容を調べると、オプションに関わらずunsigned short型でしたので、
#define WCHAR_MAX 0xffff #if __INT_MAX__ == 32767 #define WCHAR_MIN 0U #else #define WCHAR_MIN 0 #endif
のように定義することができます。WCHAR_MINは、int型が16ビットの場合はunsigned int型に、32ビットの場合はint型に評価されなければならないため、上記のようにしています。
最後が、sig_atomic_t型の最大最小値です。sig_atomic_t型は、その型のオブジェクトに対する読み書きがアトミックオペレーションで行われなければなりません。これはプロセッサの命令セットに依存します。
H8/300の場合、汎用レジスタのサイズは16ビットなので、sig_atomic_t型も16ビット以下でなければなりません。H8/300HおよびH8Sは汎用レジスタのサイズが32ビットありますので、32ビット以下の型にすることができます。したがって、H8/300では、sig_atomic_t型をshort型に、それ以外はint型に定義することにしました。したがって、
#ifdef __H8300__ #define SIG_ATOMIC_MAX 32767 #define SIG_ATOMIC_MIN (-32767-1) #else #define SIG_ATOMIC_MAX __INT_MAX__ #define SIG_ATOMIC_MIN (-__INT_MAX__-1) #endif
と定義することができます。
これで<stdint.h>の実装も終わりです。そしてこれにより、C99に対応した、フリースタンディング環境で必要とされる全ライブラリの実装が終わったことになります。次回からは、ホスト環境互換のライブラリに手を付けていきます。
|
| 2006/01/27 01:46|処理系の特性|TB:0|CM:0|▲
|
|
|
<stdint.h>は、特定の幅を持つ整数型と、それらに対応するマクロを定義しているヘッダです。このヘッダもC99から新しく導入されました。int16とかint32とかいったマクロは、既存の多くのソースコードで使用されていますが、それらに標準的な仕様を与えることが目的のようです。
まずは、ちょうどその幅を持つ整数型の定義です。"h8300-hms"ターゲットでは、char型は8ビット、short型は16ビットですので、
typedef signed char int8_t; typedef unsigned char uint8_t; typedef short int16_t; typedef unsigned short uint16_t;
というところまでは確実です。 32ビットの整数型は、int型が32ビットかどうかにもよりますが、規格を読む限りでは、int型とlong型の両方が32ビットの場合には、どちらを使ってもよさそうなので、long型にしておきます。
typedef long int32_t; typedef unsigned long uint32_t;
64ビットの整数型ですが、2つ問題があります。ひとつは、gcc-3.3.x以前のバージョンでは、"h8300-hms"ターゲットではlong long型が32ビットしかなかったことです。これについては、旧バージョンに対応するとなると影響範囲が大きいので、今回は見送ることにします。
もうひとつは、-std=c89オプションと-pedanticオプションの両方を指定した場合、long long型を使うと警告が出る問題です。この問題は__extension__を付けることで回避可能です。したがって、
__extension__ typedef long long int64_t; __extension__ typedef unsigned long long uint64_t;
とすることで解決します。
次に、最小幅を指定した整数型です。それらの型は、int_leastN_t の形式をしていますが、今回の場合、各整数型は全て 8×2n ビットですので、対応する intN_t に定義すればよさそうです。実例として8ビットのものだけを挙げますが、他も同様です。
typedef int8_t int_least8_t; typedef uint8_t uint_least8_t;
今度は、指定幅以上を持つ最も高速な整数型です。規格では、「通常、最も早く処理できる」型となっていますが、通常とはどんな場合かについて書かれていません。"h8300-hms"ターゲットの場合、汎整数拡張のコストが結構大きいので、ここでは、int型未満のものはint型として扱う方針で定義したいと思います。
typedef int int_fast8_t; typedef unsigned int uint_fast8_t; typedef int int_fast16_t; typedef unsigned int uint_fast16_t; typedef long int_fast32_t; typedef unsigned long uint_fast32_t; __extension__ typedef long long int_fast64_t; __extension__ typedef unsigned long long uint_fast64_t;
こんな感じです。
次は、最大幅を持つ整数型です。これはlong long型ですので、
__extension__ typedef long long intmax_t; __extension__ typedef unsigned long long uintmax_t;
となります。
最後は、オブジェクトを指すポインタの値を保持できる整数型です。ポインタのサイズは、H8/300およびH8/300HとH8Sのノーマルモードが16ビットで、それ以外は32ビットですから、
#if defined(__H8300__) || defined(__NORMAL_MODE__) typedef int intptr_t; typedef unsigned int uintptr_t; #else typedef long intptr_t; typedef unsigned long uintptr_t; #endif
とすることができます。
各種マクロの定義については次回に回したいと思います。
一部間違いがありましたので訂正しました。
typedef __extension__ long long int64_t; ではなく、
__extension__ typedef long long int64_t; が正解です。__extension__を用いた他の型定義も同様です。
|
| 2006/01/26 22:57|処理系の特性|TB:0|CM:0|▲
|
|
|
C99からは、C言語にも真理値型が導入されました。本来であれば、その型名はC++と同じ「bool」にしたかったのでしょうが、既存のソースコードの多くは、「bool」型を独自に定義していたため、混乱を避けるために、予約済み識別子であった「_Bool」が真理値型を表す予約語となったようです。
ところが、やはり使い勝手からすると「_Bool」より「bool」の方がよいので、「bool」を使えるようにするために用意されたのが<stdbool.h>です。今回は、この<stdbool.h>についてです。
まず、標準Cライブラリが提供する「bool」(および「true」、「false」)が使用されているかどうかを判別するためのマクロを定義しなければなりません。そのマクロは、__bool_true_false_are_definedです。今回は、このマクロを二重インクルードガードの目的にも流用します。したがって、
#ifndef __bool_true_false_are_defined #define __bool_true_false_are_defined /* 内容 */ #endif
というのが、ヘッダの枠組みになります。
次に、boolマクロを定義します。boolをtypedef名ではなくマクロとしているのは、typedef名にすると、ブロック有効範囲内で同名の識別子が再定義されてしまうためかと思われます。
#define bool _Bool
そして最後に、真理値定数であるtrueおよびfalseをマクロ定義します。
#define true 1 #define false 0
C++とは異なり、trueおよびfalseは単なるint型になります。これは、否定演算子、等価演算子、関係演算子の結果が、C++ではbool型であるのに対して、C99ではint型であることにも関係していると思われます。
▽続きを読む▽
C++に<stdbool.h>を対応させるには、単にbool、true、およびfalseの定義を無効にするだけで可能です。具体的には、
#ifndef __cplusplus
#define bool _Bool #define true 1 #define false 0
#endif
のようにすることで、それらのマクロはC++では定義されなくなります。
|
| 2006/01/26 12:35|その他のライブラリ|TB:0|CM:0|▲
|
|
|
地味な記事が続いていますが、今回お話しする<stdarg.h>で、C95のフリースタンディング環境で必要な標準Cライブラリは全てそろうことになります。この後は、もう少し地味なものが続きますが、C99のフリースタンディング環境で必要なヘッダを済ませてから、C90→C95→C99と実装を進めていきたいと思います。
まずは、va_list型の定義からです。このva_list型をどんな型にするかは、関数のコーリングコンベンションと深く関わります。参考までに"h8300-hms"ターゲットのコーリングコンベンションについて書かれたページをご紹介しておきます。
実際には、gccには<stdarg.h>の中の型やマクロの定義は、ビルトイン定義されているようです。
typedef __builtin_va_list va_list;
マクロに関しても同様です。
#define va_start(ap, last) __builtin_va_start(ap, last) #define va_end(ap) __builtin_va_end(ap) #define va_arg(ap, type) __builtin_va_arg(ap, type)
そして、C99で追加されたもう一つのマクロ、va_copyも同様です。
#define va_copy(d, s) __builtin_va_copy(d, s)
▽続きを読む▽
<stdarg.h>をC++に対応させるには、<stddef.h>がそうであったように、まずは<cstdarg>に上記の内容を記述し、<stdarg.h>からインクルードする必要があります。<cstdarg>では、va_listをstd名前空間の中で定義しなければなりません。
namespace std { typedef __builtin_va_list va_list; }
そして、<stdarg.h>では、std::va_listを大域的な前空間に導入します。
#include <cstdarg> using std::va_list;
これで、CにもC++にも対応した<stdarg.h>が出来上がります。
|
| 2006/01/25 20:51|その他のライブラリ|TB:0|CM:0|▲
|
|
|
今回は、浮動小数点数の特性を表すマクロを定義する<float.h>についてお話します。"h8300-hms"ターゲットの場合、double型もlong double型も、すべてfloat型と同じ精度しか持ちませんので、DBL_〜マクロもLDBL_〜マクロも、定義すべき値は、対応するFLT_〜マクロと同じになります。ただし、正しい型として評価されるように、FLT_〜マクロには 'F' を、LDBL_〜マクロには 'L' を添え字として付ける必要がありそうです。
と、このように考えていたのですが、gccでは、それらのマクロを定義するために、"__"を前後に付けたマクロが予め定義されているようです。例えば、FLT_MAXの場合、それに対応する__FLT_MAX__が予め定義されているようなのです。
これで、<float.h>の実装は非常に簡単になります。
#define FLT_DIG __FLT_DIG__ #define FLT_EPSILON __FLT_EPSILON__ #define FLT_MANT_DIG __FLT_MANT_DIG__ #define FLT_MAX_10_EXP __FLT_MAX_10_EXP__ #define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ #define FLT_MAX_EXP __FLT_MAX_EXP__ #define FLT_MIN_EXP __FLT_MIN_EXP__ #define FLT_MAX __FLT_MAX__ #define FLT_MIN __FLT_MIN__
それぞれのマクロの意味については、(C++用ではありますが)こちらを参照してください。DBL_〜マクロやLDBL_〜マクロについても、上記の同じように定義できますので、ここでは省略します。
他にもいくつかのマクロを定義する必要があります。
#define FLT_RADIX __FLT_RADIX__ #define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ #define DECIMAL_DIG __DECIMAL_DIG__
このうち、FLT_EVAL_METHODとDECIMAL_DIGは、C99から新たに導入されたマクロです。<limits.h>のLLONG_MAXなどもそうだったのですが、C99より前のC言語では、これらのマクロをヘッダ内で定義するのは厳密には規格に反しているのですが、機能を制約して厳密に規格合致させるより、そのまま提供した方が便利なので、そうしています。__STRICT_ANSI__マクロを調べて場合分けしてもよいのですが、とりあえずはやめておきます。
FLT_EVAL_METHODは、浮動小数点の演算をどのような範囲と精度で行うかを表すもので、以下のような値をとります。
-1 不定 0 演算や定数の評価を、それぞれの型の範囲・精度で行う。 1 floatとdoubleはdoubleの、long doubleはlong doubleの範囲・精度で行う。 その他 上記以外の負の値は処理系定義。
DECIMAL_DIGは、処理系がサポートする最も大きい浮動小数点型の値を、いったん n 桁の10進数にしてから、また元に戻したときに、情報が失われない最小の n を表します。
もう一つだけマクロを定義しなければなりません。それはFLT_ROUNDSです。これは、gccの浮動小数点演算用のサブルーチンの仕様に依存しますが、もっとも近い値となる方向へ丸めるようなので 1 に定義します。
#define FLT_ROUNDS 1
これで、<float.h>の内容は一通り網羅できたと思います。
|
| 2006/01/25 12:02|処理系の特性|TB:0|CM:0|▲
|
|
|
今回は、標準Cライブラリにおける共通定義を行う<stddef.h>についてお話します。<stddef.h>は、標準Cライブラリのヘッダの中でも、最も処理系に依存するものです。さらに、C++にも対応するとなると、いろいろ面倒なことをやらなければなりません。
まずは、size_t型からです。size_t型はsizeof演算子の結果の型ですので、意味的には、gcc拡張である__typeof__を使って、
typedef __typeof__(sizeof(int)) size_t;
とするのが妥当かと思います。ただ、__SIZE_TYPE__というマクロが予め定義されていますので、今回は、
typedef __SIZE_TYPE__ size_t;
としたいと思います。なお、size_t型は、<stddef.h>以外のいくつかのヘッダでも定義されていますので、次のように二重定義のガードを行います。
#ifndef _SIZE_T #define _SIZE_T typedef __SIZE_TYPE__ size_t; #endif
次に、ポインタどうしの減算の結果の型であるptrdiff_t型ですが、__SIZE_TYPE__と同様に、ptrdiff_t型のために__PTRDIFF_TYPE__というマクロも予め定義されているようですので、
typedef __PTRDIFF_TYPE__ ptrdiff_t;
とします。ptrdiff_tは<stddef.h>のみで定義されるので、二重定義のガードは必要ないでしょう。
同じく、ワイド文字を表す型であるwchar_t型も、__WCHAR_TYPE__マクロが予め定義されていますので、
#if defined(_WCHAR_T) && !defined(__cplusplus) #define _WCHAR_T typedef __WCHAR_TYPE__ wchar_t; #endif
のように書くことができます。wchar_tは、C++では予約語ですので、__cplusplusマクロを用いて、C++のときは定義しないようにしています。
次は、空ポインタ定数を表すNULLです。NULLは、CとC++では定義を変える必要があります。C言語では、void*型から他の型へのポインタに暗黙の変換ができますは、C++では明示的なキャストが必要なため、
#define NULL ((void*)0)
とすることができません。通常、C++ではNULLを 0 または 0L に定義しますが、gccではNULLを定義するために__nullというマジック値が提供されています。したがって、
#ifndef __cplsuplus #define NULL ((void*)0) #else #define NULL __null #endif
とすることで、CおよびC++の両方に対応することができます。
さて、最後はoffsetofです。このマクロは構造体や共用体のメンバの先頭からのオフセットを調べるためのものです。これも、CとC++では、定義を変更する必要があります。 Cでは、
#define offsetof(type, member) ((size_t)&((type*)0)->member)
と書けば問題ありません。しかし、C++では、&演算子が多重定義されていると期待通りに動作しません(C互換型でも、非メンバ関数として多重定義できます)。したがって、ちょっとした工夫が必要になります。
#define offsetof(type, member) \ ( __offsetof__ ( reinterpret_cast<std::size_t> \ (&reinterpret_cast<const volatile char*&> \ (static_cast<type*>(0)->member) ) ) )
のようにしなければなりません。ここで__offsetof__は、それに続く括弧内に、本来であれば整数定数式に含まれてはならない構文要素があった場合でも、整数定数式とすることができるgccの拡張機能です。いったん、const volatile char&型にキャストすることで、例え、&演算子が多重定義されていたとしても、無視することができます。また、size_t型はstd::size_t型としています。
▽続きを読む▽
ここまでの内容で、C言語に関しては問題ないのですが、C++に対応するためには、もう少し手間がかかります。なぜなら、<stddef.h>の中で定義される識別子は、いったんstd名前空間の中で定義され、その後、大域的名前空間に導入しなければならないからです。
最も簡単な方法は、これまでの内容を<stddef.h>に記述するのではなく、いったん<cstddef>に記述して、それを<stddef.h>からインクルードするというものです。
まず、<cstddef>は次のようになります。
/* <cstddef> #ifndef _CSTDDEF #define _CSTDDEF
#ifdef __cplusplus namespace std { #endif ... /* 型定義 */ #ifdef __cplusplus } #endif ... /* マクロ定義 */ #endif
次に、<stddef.h>から、<cstddef>をインクルードします。
/* <stddef.h> */ #ifndef _STDDEF_H #define _STDDEF_H
#include <cstddef>
#ifdef __cplusplus using std::size_t; using std::ptrdiff_t; #endif
#endif
これで、C++にも対応することができるようになります。
多くの標準ライブラリの実装では、<stddef.h>で定義される型名が、std名前空間ではなく、大域的名前空間で直接定義されていますが、それは正しくありません。
|
| 2006/01/24 19:45|処理系の特性|TB:0|CM:0|▲
|
|
|
今回は、処理系の特性を把握する目的を兼ねて、<limits.h>の実装についてお話します。<limits.h>は、主として、整数型の限界値を表すマクロを定義するためのものです。それぞれのマクロは、#if指令で使用するのに適した形式でなければなりません。すなわち、マクロの定義内容に、キャストやsizeofといった演算子を使うことはできません。
それでは、MB_LEN_MAXからです。MB_LEN_MXマクロは、処理系がサポートしている最大の多バイト文字のバイト数を表します。gccではUTF-8がサポートされていますので、本来であれば6に設定すべきですが、UCS-2の範囲で十分なので3とします。なお、gccはISO-2022-JPもサポートしていますが、シフトシーケンスに依存するエンコーディングは、利用価値が低い上に実装も面倒なので非サポートとします。というわけで、
#define MB_LEN_MAX 3
ということにします。3あれば、シフトJISやEUCを扱う上でも十分です。
次に、CHAR_BITですが、これはchar型を構成するビット数を表します。C言語では、char型のサイズが1バイトですので、sizeof演算子で得られる値も、このchar型のサイズが基準となります。"h8300-hms"ターゲットのgccでは、char型は8ビットですので、CHAR_BITは8に定義します。
#define CHAR_BIT 8
さて、いよいよ整数型の最大最小値に入ります。まずは、signed char型の最大最小値を表すSCHAR_MAXとSCHA_MINです。signed char型もchar型同様8ビットであり、負の値は2の補数を使って表現しますので、
#define SCHAR_MAX 127 #define SCHAR_MIN (-128)
となります。SCHAR_MINの定義は括弧でくくっておかないと、1-SCHAR_MINのように記述したとき、コンパイルオプションによっては、1--128のように展開されるので、"--"が減分演算子だとコンパイラが誤認してしまいます。
続いて、unsigned char型の最大値を表すUCHAR_MAXです。符号なし整数型の最小値は 0 に決まっているので、マクロはありません。unsigned char型も8ビットですので、UCHAR_MAXは255になります。
#define UCHAR_MAX 255
次は、char型の最大最小値を表すCHAR_MAXとCHAR_MINですが、char型は、コンパイルオプションによって、符号付きにも符号なしにもなりますから、どんなコンパイルオプションが指定されたかを知る必要があります。gccの場合、char型が符号なしの場合には、__CHAR_UNSIGNED__というマクロが予め定義されます。#ifdefまたは#ifndef指令を使って、判別することができそうです。
#ifndef __CHAR_UNSIGNED__ #define CHAR_MAX SCHAR_MAX #define CHAR_MIN SCHAR_MIN #else #define CHAR_MAX UCHAR_MAX #define CHAR_MIN 0 #endif
小さい型から順に解説していってますので、次はshort型の番です。"h8300-hms"ターゲットのgccでは、short型は16ビットです。ただし、int型が16ビットか32ビットかはコンパイルオプションに依存します。short型の話をしているのに、なぜint型が関係あるのかと思われるかもしれませんが、実は大ありなのです。
<limits.h>で定義すべき最大最小値のマクロは、その型のオブジェクトのみで構成される式が汎整数拡張されたときの型と同じ型に評価されなければなりません。例えば、SHRT_MINを安易に(-32768)と定義してしまうと、int型が16ビットの場合、32768はint型の範囲を超えているので、long型になってしまいます。long型の定数に符号演算子 - を付けてもlong型のままです。これでは規格に合致しません。
というわけで、SHRT_MAXおよびSHRT_MINは次のように定義します。
#define SHRT_MAX 32767 #define SHRT_MIN (-32767-1)
(-32767-1)とすることで、例えint型が16ビットでも32767はint型の範囲におさまりますからint型になります。1もint型ですので、int型どうしの演算もやはりint型になります。
同様の理由で、unsigned char型の最大値であるUSHRT_MAXも65535とすることはできません。そこで、
#define USHRT_MAX 0xffff
とします。10進定数は、int型の範囲を超えるとlong型になりますが、8進または16進定数は、int型の範囲を超えると、まずunsigned int型になります。したがって0xffffは、int型が16ビットであればunsigned int型に、int型が32ビットであればint型に評価されることになります。
ところで、unsigned short型が汎整数拡張された場合ですが、int > shortの場合はunsigned int型ではなくint型になります。汎整数昇格が起きた場合、値は保存されますが、型の符号の有無は保存されないのです。
続いてint型ですが、これもchar型と同じく、コンパイルオプションに依存しますので、普通なら場合分けが必要です。しかし、"h8300-hms"ターゲットのgccでは、__INT_MAX__というマクロが予め定義されます。これを利用すると、
#define INT_MAX __INT_MAX__ #define INT_MIN (-__INT_MAX__-1)
と書くことができます。
unsigned int型の最大値を表すUINT_MAXにも__INT_MAX__を使うことができそうです。ただし、最終的な型をunsigned int型にする仕組みも必要です。
#define UINT_MAX (__INT_MAX__*2U+1)
簡単に解説すると、__INT_MAX__が32767の場合、2倍することで、65534になります。ここで、単なる 2 ではなく、2U を掛けているので、その時点でunsigned int型になります。そして、65534U+1で65535Uを作っています。
long型とlong long型についても、__LONG_MAX__および__LONG_LONG_MAX__というマクロが予め定義されていますので、これらを使って、
#define LONG_MAX __LONG_MAX__ #define LONG_MIN (-__LONG_MAX__-1) #define ULONG_MAX (__LONG_MAX__*2UL+1)
#define LLONG_MAX __LONG_LONG_MAX__ #define LLONG_MIN (-__LONG_LONG_MAX__-1) #define ULLONG_MAX (__LONG_LONG_MAX__*2ULL+1)
と書くことができます。
|
| 2006/01/24 14:48|処理系の特性|TB:0|CM:4|▲
|
|
|
さて、準備ばかりでも退屈なので、そろそろ実際の実装に入っていきたいと思います。といっても、最初は、標準Cライブラリの中でも最もくだらない<iso646.h>です(準備以上に退屈かもしれません)。こういうくだらないものは最初に片付けておいて、楽しみは後に取っておくことにしましょう。
<iso646.h>は、ISO-646のように、C言語の基本ソース文字集合として必要な文字を欠いている文字コードを使わざるを得ない処理系のための代替表現マクロを定義するものです。言葉にすると、何か難しいのですが、要するにこのヘッダを使うことは実際にはまずありません。
用途としては、このヘッダは全くの役立たずですので、今回は<iso646.h>を題材として、ヘッダを記述する上での作法を解説したいと思います。
まず、最初にお断りしておかなければならないのですが、C言語の標準規格では、標準ヘッダはファイルであるとは限りません。#include指令を使ったときに、必要な宣言や定義がなされたものとして扱うだけで、実際にはファイルを取り込まないような実装でも構わないわけです。
しかし、そのような処理系では、標準ライブラリを自作することができません。gccをはじめ、ほとんどの処理系ではヘッダはファイルとして実現されています。今回の実装でも、それを前提として進めていきたいと思います。
まず、ヘッダを記述する際には、そのヘッダを複数回インクルードされても、一度しか内容が取り込まれないようにするために、二重インクルードガードを施します。具体的には次のようにします。
#ifndef _ISO646_H #define _ISO646_H
/* ヘッダの内容 */
#endif
これは、最初にインクルードされたときには、まだ_ISO646_Hマクロが定義されていませんから、ヘッダの内容はそのまま反映されます。しかし、初回のインクルード時に、2行目の#define指令で_ISO646_Hマクロが定義されますので、再びこのヘッダをインクルードしようとしても、1行目の#ifndef指令によって除去されます。
ここで注意点が二つあります。
まずは、_ISO646_Hというマクロ名です。このマクロ名を具体的にどんな名前にするかという決まりはありませんが、下線(アンダースコア)で始まり、大文字が続いていることが重要なのです。あるいは二重の下線から始まってもかまいませんが、二重の下線で始まる名前はC++コンパイラが内部的に使っている場合があるので、避けた方が無難です。
下線で始まり、下線または大文字が続く識別子は「予約済み識別子」と呼ばれます。予約済み識別子は、コンパイラと標準ライブラリに予約されているため、ユーザーが定義することはできません(定義した場合の動作は未定義です)。
今回は、標準ライブラリの実装ですから、規格で定められた識別子以外はユーザーに開放しなければなりません。したがって、通常とは逆に、ライブラリが内部的に使用する識別子は、全て予約済み識別子でなければなりません。
もう一つの注意点は、ファイルの末尾にあります。#endifの後ですが、ここは必ず改行文字がなければなりません。空でないソースファイルが改行文字で終わらなかった場合の動作は未定義だからです。
未定義の動作というのは、何も実行時だけのものではありません。コンパイル時の動作にも関わるのです。すなわち、コンパイルエラーが発生するならまだよいのですが、何事もなかったかのようにコンパイルが完了し、でたらめなコードを生成していても、文句は言えないのです。
▽続きを読む▽
今回、お話すべき内容はもう終わりなのですが、一応、ヘッダの中身についても触れておきましょう。 <iso646.h>の中で定義されるマクロは、C++では予約語として扱われます。したがって、ヘッダをC++にも対応させるには、 #ifndef __cplusplus
/* 内容 */
#endif
としておかなければなりません。つまり、C++では<iso646.h>は何一つ実体がないのです。 いよいよヘッダの中身ですが、次のように演算子に対応するマクロが定義されます。 #define and && #define and_eq &= #define bitand & #define bitor | #define compl ~ #define not ! #define not_eq != #define or || #define or_eq |= #define xor ^ #define xor_eq ^=
といった具合です。マクロの意味は見ての通りで、特に説明は要らないでしょう。
|
| 2006/01/24 00:11|その他のライブラリ|TB:0|CM:0|▲
|
|
|
今回は、"h8300-hms"ターゲットのgccの癖について調べてみたいと思います。コンパイラの癖を予め知っておくことで、コードの最適化がやりやすくなります。
癖1 浮動小数点型は単精度しかサポートされていません。float型は一般的な処理系と同様、IEC 60559の単精度浮動小数点フォーマットですが、double型やlong double型もfloat型と同じで単精度しかありません。これは、厳密には標準規格に合致していないのでしょうが、用途を考えるとこれでも十分な気はします。
癖2 バイトオーダーはビッグエンディアンです。Windowsにせよ、Linuxにせよ、Intelが主流になってしまった今日ではビッグエンディアンのプロセッサに触れたことがない方もおられるかもしれませんが、H8はビッグエンディアンなのです。
癖3 H8シリーズは、1ビット単位のシフト命令しかないため、シフト演算は決して速くありません。シフトを使うか、乗除算を使うか、微妙なケースもあるかもしれません。
癖4 wchar_t型は16ビットで、ワイド文字の内部表現はUTF-16です。組み込み用途にはオーバースペックかもしれませんが、<wchar.h>や<wctype.h>の関数は、そのつもりで実装する必要がありそうです。
癖5 CPUキャッシュ、パイプライン、スーパースカラといった近代的な機能はありません。それらの影響を考慮する必要はなさそうです。
癖6 文字列定数への書き込みはできません。昔のgccでは可能だったようですが、gcc-3.4.xやgcc-4.0.xではエラーになります。
癖7 -mint32を指定した場合、H8/300では、int型の読み書きがアトミックオペレーションになりません。sig_atomic_t型を決めるときには注意が必要そうです。
何か、強引に七癖にしてしまった感がありますが、他にも癖と呼べる特性が結構あります。それらについては追々お話しすることにしたいと思います。
|
| 2006/01/23 19:13|処理系の特性|TB:0|CM:1|▲
|
|
|
開発環境の構築の話もそろそろ飽きてきました。本来なら、ターゲットボードの接続、モニターの書き込み、実際のプログラムのダウンロードから実行までをここでやっておくべきかもしれませんが、実際に使う段階まで先送りにして、しばらく処理系の特性を見ていきたいと思います。
今回は「コンパイルオプション」を知るということで、gccの主なコンパイルオプションを押さえておきたいと思います。まずは、ターゲットが"h8300-hms"の場合に特有のものから見ていきましょう。
まずはプロセッサの種類および動作モードを指定するオプションからです。
-mh, -ms, -mn これらのオプションがなければ、プロセッサはH8/300とみなされます。-mhオプションを付けるとH8/300Hを、-msオプションを付けるとH8Sを指定します。-mnオプションは、-mhまたは-msと一緒に使用し、ノーマルモードであることを指定します。gcc-4.0以降では、H8SX用のオプションも加わっていますが、今回は扱わないものとします。
次に、C言語の仕様に関するオプションです。
-mint32 これはint型が32ビットであることを指定します。このオプションがなければ、int型は16ビットしてコンパイルされます。
-std=<standard> <standard>で指定した標準規格に従います。指定できる主な値は、"c89", "c99", "gnu89", "gnu99"です。文字通り、"c89"を指定すればANSI X3.159-1989に、"c99"を指定すればISO/IEC 9899:1999に従います。"gnu89"および"gnu99"は、それぞれ"c89"と"c99"にGNU拡張を加えたものです。このオプションがなければ、"gnu89"が選択されます。
-fsigned-char char型が符号付きであることを指定します。このオプションがなければ、("h8300-hms"がターゲットの場合)char型は符号なしになります。
今回、ターゲットとして使用するAKI-H8/3069FフラッシュマイコンLANボードでは、コンパイルオプションとして、"-mh -mint32"が必須となります。ただし、可能な限り"h8300-hms"の範疇での移植性は持たせようと思います。とはいえ、今回扱うボードに対応するもの以外は、最低限かつ無難な実装に収めると思います。
|
| 2006/01/23 13:30|処理系の特性|TB:0|CM:0|▲
|
|
|
色々と準備が多くて、なかなか本題に入れませんが、すべて必要な過程ですので、省略するわけにはいきません。このブログでは、筆者の実作業に基づくレポートであると同時に、読者の皆さんが同じことを試みられるよう、皆さんが行う場合を想定した書き方をしています。予めご了承ください。
前回までで、クロス開発ツールの準備が整いました。今回は、実行環境でもあるTOPPERS/JSPカーネルの構築を行います。TOPPERS/JSPカーネルは、μITRON 4.0仕様のスタンダードプロファイルに準拠したオープンソースのリアルタイムカーネルです。まずは、TOPPERSプロジェクトのウェブサイトから、JSPカーネルをダウンロードします。
JSPカーネルには、.tar.gz形式で圧縮されたものと、.lzh形式で圧縮されたものの二種類があります。.tar.gz形式のものは、文字コードにEUC-JPを、改行文字にLFを使用したものです。.lzh形式のものは、文字コードにシフトJISを、改行文字にCR-LFを使用したものです。今回は、.tar.gz形式で圧縮されたものをダウンロードしてください。
ダウンロードしたファイルは、例によって、"~/src"ディレクトリに格納します。そして、bashから次のコマンドを入力して、圧縮ファイルを展開します。
cd ~/src tar zxvf jsp-1.4.2.tar.gz
圧縮ファイルが展開され、"jsp"ディレクトリが現れたと思います。次に、カーネルのコンフィギュレータの構築を行います。コンフィギュレータは、μITRONの静的APIを解析して、C言語のソースに翻訳するためのツールです。次のコマンドを入力してください。
cd jsp/cfg make depend
処理の完了を待って、次のコマンドを入力してください。
make
これで、エラーが発生しなければ、コンフィギュレータの構築は終わりです。次に、カーネル本体の構築に入ります。構築されたファイルの格納場所として、"~/build/jsp"ディレクトリを作成します。次のコマンドを入力してください。
cd ~/build mkdir jsp
続いて、カーネルの構築を行います。まずは、configureからです。次のコマンドを入力してください。
cd jsp ../../src/jsp/configure -C h8 -S akih8_3069f
configureが完了すれば、次のコマンドを入力してください。
make depend
ここで、先ほどのコンフィギュレータの構築を忘れていたりすると、エラーが発生します。エラーが発生しなければ、次のコマンドを入力してください。
make
これまでの過程でエラーが発生しなければ、カーネルとsample1というサンプルプログラムの構築が完了します。"~/build/jsp"ディレクトリの中に、"jsp.srec"というファイルが生成されていることを確認してください。これは、ターゲットに転送するためのSレコード形式のファイルです。
|
| 2006/01/23 01:28|開発環境|TB:0|CM:0|▲
|
|
|
binutilsに引き続き、今回はgccの構築を行います。gccは、GNU Compiler Collectionの略で、Cの他にも、C++, Objective-C, FORTRAN, Ada, Javaが含まれます。今回は、そのうちCコンパイラだけを構築することにします。
Ring Serverの詳しい使い方は前回解説しましたので、今回は要点だけに絞って解説したいと思います。まずは、Index of /archives/GNUからgccを選んでください。そして、その中からgcc-3.4.5/というディレクトリに入ります。
Index of /archives/GNU/gcc/gcc-3.4.5というディレクトリの中から、gcc-core-3.4.5.tar.gzというファイルをダウンロードしてください。binutilsと同様、拡張子が.tar.bz2になっているものも圧縮方法が異なるだけで、同じものです。
ダウンロードしたファイルは、binutilsと同様、"~/src"に格納してください。そして、圧縮されたファイルを次のコマンドで展開します。
cd ~/src tar zxvf gcc-core-3.4.5.tar.gz
圧縮ファイルが展開されれば、gcc-3.4.5というディレクトリが現れたと思います。次に構築によって生成されるファイルの格納先ディレクトリを作ります。次のコマンドを入力してください。
cd ~/build mkdir gcc
そして、いよいよgccの構築を行います。まずはconfigureからです。
cd gcc ../../src/gcc-3.4.5/configure --target=h8300-hms --prefix=/usr/local/h8300-hms
しばらくするとconfigureが完了すると思いますので、次にmakeを行います。
make
ここでエラーが発生した場合、多くの原因はbinutilsのツールにパスが通っていないためだと思われます。前回の記事を参考に、もう一度パスの設定を確かめてください。
makeが完了すれば、"/usr/local/h8300-hms"にインストールを行います。次のコマンドを入力してください。
make install
ここまででエラーが発生しなければ(警告が出る場合はありますが、無視して構いません)、gccのインストールは成功です。念のため、次のコマンドを入力して、正しくインストールされていることを確認してください。
h8300-hms-gcc -v
このとき、
Reading specs from /usr/local/h8300-hms/lib/gcc/h8300-hms/3.4.5/specs Configured with: ../../../src/gcc-3.4.5/configure --target=h8300-hms --prefix=/usr/local/h8300-hms Thread model: single gcc version 3.4.5
と出力されれば、gccのインストールは正しく行われています。
PCのCコンパイラを使われたことのある方なら、ここで"Hello, World!"でも出力させたいところでしょうが、残念ながら、"Hello, World!"には、まだはるか遠い道のりが待っています。
標準出力に文字列を出力するputs関数は、これから実装すべきライブラリの機能ですし、main関数を呼び出すスタートアップルーチンもまだありません。リンク時にプログラムやデータを配置するためのリンカスクリプトもまだないのです。
|
| 2006/01/23 00:32|開発環境|TB:0|CM:0|▲
|
|
|
前回はCygwinをインストールしましたので、今回はCygwin上で動作するクロス開発ツールの構築を行います。クロス開発ツールというのは、H8/3069Fマイコンで動作するプログラムを、Windows上(実際はCygwin上)で、コンパイル・アセンブル・リンクするためのツール群です。
まずは、必要なファイルをRing Serverからダウンロードします。慣れないとちょっと使いにくいかもしれませんが、ページの真ん中より少し上あたりに、「毎日更新! ソフトウェア ライブラリ」と書かれたところがあると思います。そこからソフトウェアライブラリのページに入れます。
そして、「GNUツール群」からライブラリ一覧に入り、GNUを選べば、Index of /archives/GNUにGNUツール群のディレクトリ一覧が現れます。その中から、binutilsを選んでください。たくさんファイルが並んでいますが、binutils-2.16.1.tar.gzです。拡張子がtar.bz2のものも同じですが、圧縮方法が異なります。今回はtar.gzの方をダウンロードしたものとして解説します。
ダウンロードしたファイルは、"~/src"ディレクトリに格納するものとします。ここで、"~/src"というのはCygwinにおけるディレクトリの指定方法です。~(チルダ)はホームディレクトリの意味で、Cygwinを"C:\cygwin"にインストールした場合、"C:\cygwin\home\<アカウント名>"がホームディレクトリになります。また、bashを立ち上げたとき、最初に移動する先がホームディレクトリです。
"~/src"ディレクトリを生成するには、Windowsのエクスプローラで行っても構いませんが、bash上で次のように入力することでも生成できます。
mkdir src
binutils-2.16.1.tar.gzを"~/src"に格納したら、今度は、圧縮されたファイルを展開します。bashのコマンドラインから、次のコマンドを入力してください。
cd ~/src tar zxvf binutils-2.16.1.tar.gz
これで圧縮ファイルが展開され、binutils-2.16.1ディレクトリが現れるはずです。
このbinutilsというのは、アセンブラやリンカのほか、色々な開発ツールが含まれたパッケージです。コンパイラを構築する前に、必ずbinutilsを構築する必要があります。 それでは、これからbinutilsの構築を開始します。
まず、構築結果として生成されるファイルを格納するためのディレクトリを作成します。ここでは、"~/build/binutils"とします。次のコマンドを入力してください。
cd ~ mkdir build cd build mkdir binutils
ディレクトリができたら、そのディレクトリに移動し、構築を開始します。まずはconfigureからです。configureは、構築のための色々な設定を行います。例えば、どんなターゲット用のツールを生成するのかとか、生成後のツールはどこにインストールするのかなどです。次のコマンドを入力してください。
cd binutils ../../src/binutils-2.16.1/configure --target=h8300-hms --prefix=/usr/local/h8300-hms
ここでは、ターゲットとして"h8300-hms"を指定し、構築されたツールを"/usr/local/h8300-hms"というディレクトリにインストールするように設定しました。configureには少し時間がかかります。configureが完了したら、今度はmakeの番です。次のコマンドを入力してください。
make
makeもしばらく時間がかかります。気長に待ってください。makeが完了すれば、構築されたツールを、先ほど設定した"/usr/local/h8300-hms"にインストールします。次のコマンドでインストールが始まります。
make install
これで、途中でエラーが発生しない限り、binutilsのインストールは完了です。ただし、このままではツールにパスが通っていませんので、パスを設定しておきましょう。次のコマンドでパスの設定が行えます。
export PATH=$PATH:/usr/local/h8300-hms/bin
Windowsのパス設定とは微妙に異なるので注意してください。特に、パスの区切りには ;(セミコロン)ではなく、:(コロン)を使いますので要注意です。これで、次のコマンドを入力すれば、リンカのバージョンが表示されると思います。
h8300-hms-ld -v
ここで、
GNU ld version 2.16.1
と表示されれば、正しくパスが通っています。
ところで、このままではbashを立ち上げるたびに、毎回パスの設定を行わなければなりません。そこで、"/etc/profile"(Windowsでのファイル名は"C:\cygwin\etc\profile")をエディタで開いて、その中の適当なところに先ほどの設定コマンドを書いてしまいましょう。パスの設定を行っている箇所がどこかにあると思いますので、その直後にでも追加するとよいでしょう。
|
| 2006/01/22 23:47|開発環境|TB:1|CM:0|▲
|
|
|
標準Cライブラリを実装するには、まずは開発環境を構築しなければなりません。今回は、h8300-hmsをターゲットとしたGNU開発環境をCygwin上に構築します。まずは、Cygwinのインストールについて、解説することにします。
Cygwinは、Windows上でUnix環境をエミュレートするためのツールです。Cygwinをインストールするには、まずはCygwinの公式サイトから、setup.exeをダウンロードする必要があります。
 上の画像が、ページのあちこちに見つかると思いますので、それをクリックしてsetup.exeをダウンロードしてください。ダウンロードしたsetup.exeは、どこか専用のフォルダに格納した方がよいでしょう。たとえば、"C:\cygsrc"といったフォルダを作成して、そこに格納するようにしてください。
setup.exeがダウンロードできたら、そのアイコンをダブルクリックしてセットアップ・プログラムを起動してください。大体デフォルトの設定のままで構いませんが、次の画面
 が現れたら、CategoryがDevelのDefaultとなっているところをクリックして、Installにしてください。ハードディスクに余裕がある場合は、CategoryがAllのところをInstallにしておいた方が無難です。
そのまま先に進めば、自動的にダウンロードとインストールが行われます。結構時間がかかりますので、気長に待ってください。インターネットへの接続環境によっては、途中で止まってしまったりしますが、その場合は、いったん中断してから再スタートすれば、途中からダウンロードを再開することができます。
インストールが無事終われば、デスクトップにアイコン が現れますので、これをダブルクリックしてください。黒いウィンドウが起動するかと思います。
 これは、コマンドプロンプトによく似ていますが、bashというCygwinの標準的なシェルです。コンパイラなどは、ここからコマンドを入力することで利用します。
試しに、次のように入力してみてください。
gcc -v
ここで、いろいろな文字が表示された後、最後に
Thread model: posix gcc version ...
といった表示があれば、インストールに成功しています。(...の部分はgccのバージョンによって変わります)
|
| 2006/01/22 19:21|開発環境|TB:0|CM:0|▲
|
|
|
標準Cライブラリを実装するにあたって、予め大まかな方針を決定しておきたいと思います。- 実行環境
- 秋月電子通商製AKI-H8/3069FフラッシュマイコンLANボード
- 翻訳環境
- gcc-3.4.5 + binutils-2.16.1 + TOPPERS/JSPカーネル-1.4.2
このような環境を選んだのは、PC上のネイティブコンパイラで標準Cライブラリを今更実装しても、余り面白みがないからです。gccの場合、newlibという標準ライブラリが利用できるわけですが、結構大きいライブラリであるのと、マルチタスク対応になっていないことから、よりよいライブラリを目指せればと思います。
ただ、あまり環境に依存しない部分に関しては、Cygwinのネイティブコンパイラを使用する予定です。その方が、簡単にテストが行えるので、効率的だからです。
他に決めておくべき方針として、今回は移植性より、処理系に特化することで、より効率的な実装を目指そうと考えています。特に、小さい関数に関しては、可能な限りの最適化を施してみたいと思います。大きな関数については、メンテナンスの都合もあるので、ほどほどになる可能性が高いと思います。
|
| 2006/01/22 15:07|未分類|TB:0|CM:0|▲
|
|
|
はじめまして。
これから、このブログを使って、C99 (ISO/IEC 9899:1999) に対応した標準Cライブラリの実装を行っていきたいと思います。実装の終わったプログラムは、ある程度きりのよい所でダウンロードできるようにしたいとも考えています。
標準Cライブラリには、結構な量の関数が含まれていますので、かなり時間がかかると思いますが、末永くお付き合いいただけると幸いです。
|
| 2006/01/22 00:14|未分類|TB:1|CM:2|▲
|
|
|
|
|
ホーム
全記事一覧
|