C++ 移行ガイド ホーム目次前ページへ次ページへ索引


第 4 章

入出力ストリームとライブラリヘッダーの使い方

この章では、C++ 5.0 コンパイラに実装されたライブラリとヘッダーファイルの変更について説明します。この変更は、C++ 4 コンパイラ用に記述したコードを Sun WorkShop 6 C++ コンパイラ用に変更する際に考慮する必要があります。

入出力ストリーム

C++ 4.2 コンパイラには、これまで正式な定義ではなかった「従来型」の入出力ストリームが実装されています。この実装形式は、Cfront (1990 年) とともにリリースされたバージョンと互換性があり、いくつかの問題点が解決されています。

標準 C++ では、新しい入出力ストリームと拡張された入出力ストリーム (標準入出力ストリーム) が定義されています。新しい入出力ストリームは、綿密に定義されており、機能が豊富で、多言語化対応のコードの記述に利用できます。

互換モードでは、C++ 4.2 コンパイラと同じ従来型の入出力ストリームが提供されます。互換モードでコンパイルしたとき (-compat[=4])、4.2 コンパイラで動作していた既存の入出力ストリームコードはまったく同じように動作します。


注 - コンパイラが提供する従来型の入出力ストリーム実行時ライブラリには 2 つのバージョンがあります。一方のバージョンはコンパイラの互換モードでコンパイルされるもので、C++ 4.2 で使用されるライブラリと同じです。もう一方のバージョンは、ソースコードは同じですが、コンパイラの標準モードでコンパイルされます。ソースコードのインタフェースは同じですが、ライブラリのバイナリコードには標準モードの ABI が使用されています。詳細については 「バイナリ互換の問題」を参照してください。

標準モードでは、デフォルトで標準の入出力ストリームが使用されます。標準形式のヘッダー名 (「.h」なし) を使用すると、標準ヘッダーが使用されます。その場合、すべての宣言は名前空間 std にあります。

標準ヘッダーには「.h」で終わる形式が用意されているものもあります。次の 4 ファイルがそうです。これを使用すると、すべてのヘッダー名が using 宣言によって大域名前空間に存在するようになります。

これらのヘッダーはサンの拡張であるため、これに依存するコードは移植性がない可能性があります。これらのヘッダーを使用すると、従来の入出力ストリームの代りに標準の入出力ストリームを使用している場合でも、既存の (簡単な) 入出力ストリームコードを変更せずにコンパイルできます。たとえば、表 4-2 のコードは、従来の入出力ストリームでもサンの標準入出力ストリームの実装でもコンパイルできます。

表 4-1   標準の名前形式の iostream を使用
#include <iostream>
int main()
{
    std::cout << "Hello, world!" << std::endl;
}


表 4-2   従来の名前形式の iostream を使用
#include <iostream.h>
int main()
{
    cout << "Hello, world!" << endl;
}


すべての従来の入出力ストリームコードが標準入出力ストリームと互換性があるとは限りません。従来の入出力ストリームコードをコンパイルできない場合は、コードを変更するか、従来の入出力ストリームだけを使用する必要があります。

従来の入出力ストリームを標準モードで使用する場合は、コンパイラオプション
-library=iostreamCC コマンド行に指定します。このオプションを使用すると、従来の入出力ストリームのヘッダーファイルが入った特別なディレクトリが探索され、従来の入出力ストリーム実行時ライブラリがプログラムとリンクされます。このオプションは、プログラムに必要なすべてのコンパイルだけでなく、最後のリンク段階でも使用する必要があります。そうしないと、一貫性のない結果となります。


注 - 古い形式と新しい形式の入出力ストリーム (標準の入力ストリームと出力ストリーム cincoutcerr を含む) が同じプログラムに混在していると、重大な問題が発生することがあるのでお勧めできません。

従来の入出力ストリームを使用すると、入出力ストリームヘッダーの 1 つをインクルードする代わりに、入出力ストリームクラス用の独自の前方宣言を作成できます。

表 4-3   従来の入出力ストリームによる前方宣言
// 従来の入出力ストリームでのみ有効
#include <iosfwd>
using std::istream;
using std::ostream;
class MyClass;
istream& operator>>(istream&, MyClass);
ostream& operator<<(ostream&, MyClass);

従来の名前 (istreamofstreamstreambuf など) が標準の入出力ストリームのクラスの名前と同じでないため、この方法は標準の入出力ストリームには適用できません。従来の名前はクラステンプレートの特殊化を型定義 (typedef) したものです。

標準入出力ストリームを使用すると、入出力ストリームクラスに対して独自の前方宣言は作成できません。正しく前方宣言が行われるようにするには、代わりに標準ヘッダー <iosfwd> をインクルードします。

表 4-4   標準の入出力ストリームによる前方宣言
// 標準の入出力ストリームにのみ有効
#include <iosfwd>
using std::istream;
using std::ostream;
class MyClass;
istream& operator>>(istream&, MyClass);
ostream& operator<<(ostream&, MyClass);

標準型と従来型の両方の入出力ストリームで機能するコードを作成するには、前方宣言を使用する代わりに、ヘッダーファイル全体をインクルードします。次に例を示します。

表 4-5   従来型と標準型の両方の入出力ストリームに有効なコード
// Sun WorkShop C++ の従来型と標準型の両方の入出力ストリームで有効
#include <iostream.h>
class MyClass;
istream& operator>>(istream&, MyClass);
ostream& operator<<(ostream&, MyClass);

タスク (コルーチン) ライブラリ

<task.h> ヘッダーを介してアクセスするコルーチンライブラリはサポートされていません。コルーチンライブラリに比べて、Solaris のスレッドと、言語開発ツール (特にデバッガ) およびオペレーティングシステムとの間の統合が改善されています。

RogueWave Tools.h++

Sun WorkShop 6 C++ コンパイラには Rogue Wave の Tools.h++ バージョン 7 が含まれています。RogueWave Tools.h++ バージョン 7 ライブラリは、従来の入出力ストリームで構築されています。したがって、RogueWave のツールライブラリを標準モードでインクルードする場合は、入出力ストリームもインクルードする必要があります。古い形式と新しい形式の入出力ストリームを同じプログラムで使用しないように注意が必要です。Tools.h++ を標準モードで使用するときは、プログラム全体に含まれるすべての部分をコンパイルおよびリンクする際には、-library=iostream オプションを使用してください。

Tools.h++ へのアクセス方法の詳細については、『C++ ユーザーズガイド』または
CC(1) マニュアルページを参照してください。

C ライブラリヘッダー

互換モードでは、従来どおり C のヘッダーを使用できます。標準ヘッダーは、 使用中のリリースの Solaris に含まれる /usr/include ディレクトリにあります。

C++ 標準では、C のヘッダーの定義が変更されています。

ここで説明する C ライブラリヘッダーとは、ISO C 標準 (1990 年の ISO 9899) と、それ以降の追補 (1994 年) で定義されている以下の 17 のヘッダーです。

<assert.h> <ctype.h>  <errno.h>  <float.h>  <iso646.h> <limits.h> 
<locale.h> <math.h>   <setjmp.h> <signal.h> <stdarg.h> <stdio.h>  
<stdlib.h> <string.h> <time.h>   <wchar.h>  <wctype.h>

/usr/include ディレクトリとその下位ディレクトリに存在する、その他の数百のヘッダーは、C 言語標準に規定されていないため、この言語の変更による影響を受けることはありません。

これらのヘッダーは、旧リリースの Sun C++ と同様の方法で C++ プログラムにインクルードして使用することができますが、以下のことに注意してください。

C++ 標準では、型、オブジェクト、これらのヘッダー中で使用する関数名は、大域的な名前空間だけでなく、std 名前空間にも記述するよう規定しています。つまり、Solaris 2.6 および Solaris 7 オペレーティング環境に付属しているヘッダーはそのままでは使用できないということです。標準モードでコンパイルする場合は、Sun WorkShop 6 C++ コンパイラに付属しているヘッダーを使用してください。ヘッダーが正しくないと、プログラムのコンパイルやリンクが失敗する可能性があります。

Solaris 2.6 および Solaris 7 オペレーティング環境では、ヘッダーにはパス名ではなく標準のヘッダー名を使用してください。次の文は正しい例です

#include <stdio.h> // 正しい

次の文は正しくない例です。

#include "/usr/include/stdio.h" // 誤り
#include </usr/include/stdio.h> // 誤り

Solaris 8 オペレーティング環境では、/usr/include にある標準の C ヘッダーは C++ にも有効であり、C++ コンパイラによって自動的に使用されます。したがって、次の文を使用した場合、Solaris 2.6 と 7 オペレーティング環境でコンパイルしたときは、C++ コンパイラ付属の stdio.h が使用されます。

#include <stdio.h>

ところが、Solaris 8 オペレーティング環境でコンパイルしたときは、Solaris 付属の stdio.h が使用されます。Solaris 8 オペレーティング環境では、include 文で明示的なパス名を使用することに関する制限はありません。しかし、パス名 (</usr/include/stdio.h> など) を使用すると、コードの移植性が失われます。

C++ 標準では、17 個ある C 標準のヘッダーのすべてに、別のバージョンのヘッダーが追加されています。つまり、<NAME.h> 形式のすべてのヘッダーについて、ヘッダー名の最後の .h を落とし、先頭に c を追加した <cNAME> という形式の名前を持つヘッダーがあります (NAME は名前を示します)。例:<cstdio><cstring><cctype>

新しいバージョンのヘッダーには、従来の形式のヘッダーで使用されていた名前が含まれていますが、新しいバージョンのヘッダーは std 名前空間にのみ存在します。次に、C++ 標準に則った使用例を示します。

#include <cstdio>
int main() {
printf("Hello, "); //エラー、printf が不明
std::printf("world!\n"); // OK
}

<stdio.h> の代わりに <cstdio> が使用されているため、printf という名前は、名前空間 std にあるだけで、大域的な名前空間にはありません。printf の名前を修飾するか、using 宣言を追加する必要があります。

#include <cstdio>
using std::printf;
int main() {
printf("Hello, "); // OK
std::printf("world!\n"); //OK
}

/usr/include 中の C 標準のヘッダーには、C 標準では使用が許可されていない宣言が多数含まれています。これらの宣言が存在しているのは、慣習上の理由によります。つまり、UNIX システムにおいては、これらのヘッダー中で慣例的に標準外の宣言が使用されてきたということ、また、他の標準 (POSIX や XOPEN など) においては、そうした宣言が必要になるためです。互換性を維持するため、Sun C++ の <NAME.h> ヘッダーには、そうした名前が残されていますが、これらの名前は、大域的な名前空間にしか存在せず、新しいバージョンの <cNAME> ヘッダーには存在しません。

これは、新しいヘッダーが以前のプログラムには使用されていないため、互換性や慣習上の問題が生じることはないためです。<cNAME> は、一般的なプログラミングに役立たないと思われるかもしれませんが、最大の移植性を持つ標準的な C++ コードを作成するには、<cNAME> ヘッダーに移植不可能な宣言が含まれないようにする必要があります。<stdio.h> を使用した例を次に示します。

#include <stdio.h>
extern FILE* f; // std::FILE でも OK
int func1() { return fileno(f); }      // OK
int func2() { return std::fileno(f); } // エラー

次の例では、<cstiod> を使用しています。

#include <cstdio>
extern std::FILE* f; // FILE は名前空間 std にだけある
int func1() { return fileno(f); }      // エラー
int func2() { return std::fileno(f); } // エラー

上記の例の中の fileno は、互換性を維持するために <stdio.h> に残されている「標準外」の関数です。この関数は、大域的な名前空間にのみ存在し、std 名前空間には存在しません。標準外の関数であるため、<cstdio> にも存在しません。


注 - Solaris 2.6 オペレーティング環境用の、Sun WorkShop 6 C++ コンパイラ版 <wchar.h><cwchar><wctype.h>、および <cwctype> ヘッダーファイルからは、いくつかの関数が意図的に除外されています。これは、Solaris 2.6 環境ではそれらの関数の実行に必要な機能が装備されていないためです。

C++ 標準では、同じコンパイル単位で <NAME.h><cNAME> の両方のバージョンの C 標準ヘッダーを使用することを許可しています。一般的に、このことが意図的に行われることはないと思われますが、たとえば、使用するプロジェクトヘッダーに
<stdlib.h> が含まれていて、作成したコードに <cstdlib> がインクルードされたときには、このことが起こります。Solaris 2.6 オペレーティング環境では、いくつかのヘッダー、特に <wchar.h><cwchar><wctype.h><cwctype> ヘッダーの組み合わせについては機能しません。これらのヘッダーを使用したときにコンパイラのエラーが出力された場合は、<cNAME> 版ではなく、<NAME.h> 版のヘッダーを使用してください。

標準ヘッダーの実装

『C++ ユーザーズガイド』には、標準ヘッダーの実装方法とともに、その方法が採用された理由が詳細にわたって説明されています。C または C++ 標準のヘッダーをインクルードした場合、コンパイラは実際には指定された名前の後に .SUNWCCh が付いた名前を持つファイルを検索します。たとえば、<string> であれば <string.SUNWCCh><string.h> であれば <string.h.SUNWCCh> を検索します。コンパイラの include ディレクトリには、この両方の名前が含まれており、2 つの名前のどちらも同じファイルを示します。たとえば、include/CC ディレクトリには、stringstring.SUNWCCh の両方が含まれており、これらの名前は同じファイル (<string> をインクルードしたときにアクセスされるファイル) を示します。

エラーメッセージとデバッグ情報には、.SUNWCCh という接尾頭辞は追加されません。たとえば、<string> をインクルードした場合、エラーメッセージとデバッグ情報には、string とだけ示されます。接尾頭辞なしの名前に関するデフォルトのメークファイル規則で問題が起きるのを避けるため、ファイル依存情報には、string.SUNWCCh が使用されます。たとえば、SunOS の find コマンドを使用して、単にヘッダーファイルだけ探す場合は、.SUNWCCh 接尾辞で検索するようにしてください。


サン・マイクロシステムズ株式会社
Copyright information. All rights reserved.
ホーム   |   目次   |   前ページへ   |   次ページへ   |   索引