以下に、 VMS上でのGNU CCの使い方を示します。
UNIXとVMSのファイルシステムの相違のために、 GNU CCは、 `#include'されるファイル名をVMSが理解できる名前に変換します。 基本的な方針は、 インクルード・ファイルが指定されている箇所に接頭辞を付けて、 ファイル名全体をVMSファイル名に変換してから、 そのファイルをオープンしようとする、 というものです。 GNU CCは、 ファイルのオープンが成功するまで、 複数の接頭辞を1つずつ順番に試みます。
#include foobar
という形式のインクルード指示子は、
VAX-CとGNU CCの間で非互換性の問題をよく引き起こします。
VAX-Cはこれを、
標準的な#include <foobar.h>
指示子のように取り扱います。
これは、
GNU CCにより実装されているANSI Cの振る舞いとは互換性がありません。
GNU CCでは、
名前foobar
はマクロとして展開されます。
マクロ展開によって、
#include
の2つの標準的な形式のいずれかが、
最終的には生成されなければなりません。
#include "file" #include <file>
この非互換性の問題に直面した場合、
最も良い解決策は、
ソース・コードを修正して、
2つの標準的な形式のいずれかに#include
指示子を書き直すことです。
こうすれば、
どちらのコンパイラを使っていても問題なく動作します。
ちゃんとしていなくてもよいから手っ取り早く直してしまいたい場合は、
そのファイル名を、
適切に展開されるマクロとして定義してしまいます。
例えば、
以下のようにします。
#define stdio <stdio.h>
この名前が、 プログラム中で別の名前と衝突を起こさない限り、 これで問題なく動作するでしょう。
VAX-Cが、
#include "foobar"
はファイル`foobar.h'をインクルードするよう要求しているものと想定するということが、 非互換性のもう1つの原因になっています。 GNU CCはこのような想定を行いません。 代わりに、 要求を字義どおりに受け取って、 `foobar'というファイルを読み込もうとします。 この問題を回避するのに最も良い方法は、 インクルード指示子の中で、 望ましいファイル拡張子を常に明示的に指定することです。
VMS用のGNU CCは、
一群のインクルード・ファイルと一緒に配布されています。
これらのインクルード・ファイルがあれば、
ほとんどの一般的な用途のプログラムをコンパイルするのには十分です。
GNU CCのディストリビューションの中には、
VMSシステムに固有の関数群のための定数や構造体を定義するヘッダ・ファイルは含まれていませんが、
これらの関数をGNU CCで使うことができない理由はありません。
これを行うには、
まず最初にヘッダ・ファイルを作らなければならないかもしれません。
パブリック・ドメインのユーティリティUNSDL
(これはDECUSテープの中に含まれています)
を使って生成するか、
システム・マクロ・ライブラリから関連するモジュールを取り出して、
エディタを使って、
Cのヘッダ・ファイルを作成します。
#include
でインクルードされるファイル名に、
DECNETノード名を含めることはできません。
明示的にであれ、
論理名を介して暗黙的にであれ、
ノード名を使おうと試みると、
プリプロセッサはI/Oエラーを報告してきます。
GNU CCは、
VAX-Cのキーワードであるglobalref
、globaldef
、globalvalue
を提供していません。
GNUアセンブラGASのあまり知られていない機能を使って、
これと同等の効果を実現することができます
(そのためには、
バージョン1.39以降のGASが必要になります)。
以下のマクロを使えば、
かなり自然な形でこの機能を使うことができるようになります。
#ifdef __GNUC__ #define GLOBALREF(TYPE,NAME) \ TYPE NAME \ asm ("_$$PsectAttributes_GLOBALSYMBOL$$" #NAME) #define GLOBALDEF(TYPE,NAME,VALUE) \ TYPE NAME \ asm ("_$$PsectAttributes_GLOBALSYMBOL$$" #NAME) \ = VALUE #define GLOBALVALUEREF(TYPE,NAME) \ const TYPE NAME[1] \ asm ("_$$PsectAttributes_GLOBALVALUE$$" #NAME) #define GLOBALVALUEDEF(TYPE,NAME,VALUE) \ const TYPE NAME[1] \ asm ("_$$PsectAttributes_GLOBALVALUE$$" #NAME) \ = {VALUE} #else #define GLOBALREF(TYPE,NAME) \ globalref TYPE NAME #define GLOBALDEF(TYPE,NAME,VALUE) \ globaldef TYPE NAME = VALUE #define GLOBALVALUEDEF(TYPE,NAME,VALUE) \ globalvalue TYPE NAME = VALUE #define GLOBALVALUEREF(TYPE,NAME) \ globalvalue TYPE NAME #endif
(名前の先頭にある接頭辞_$$PsectAttributes_GLOBALSYMBOL
は、
アセンブラが、
シンボルの属性を変更した後に取り除きます)
これらのマクロは、
VMSバイナリ・ディストリビューションの中のヘッダ・ファイル`GNU_HACKS.H'において提供されています。
使い方の例を示すと、
以下のようになります。
GLOBALREF (int, ijk); GLOBALDEF (int, jkl, 0);
マクロGLOBALREF
とマクロGLOBALDEF
は、
そのまま単純に配列に対して使用することはできません。
というのは、
配列の次元を宣言の右側に挿入することができないからです。
しかし、
以下のように、
最初に配列型に対してtypedefを定義しておけば、
これらのマクロを使用して配列を宣言することができます。
typedef int intvector[10]; GLOBALREF (intvector, foo);
配列の初期化子や構造体の初期化子を使用した場合も、
このマクロはうまく機能しなくなります。
この場合には、
初期化子をまた別のマクロとして定義することも可能ですし、
GLOBALDEF
を手作業で展開することも可能です。
GLOBALDEF
マクロを大規模な配列に対して使用したいが、
配列の個々の要素を明示的に初期化するのは避けたいという場合もあるでしょう。
この場合は、
{0,}
のような初期化子を使用することができます。
これは、
配列全体を0
で初期化します。
この実装の欠点は、
GLOBALVALUEREF
やGLOBALVALUEDEF
を使用して宣言された変数が、
常に配列になるということです。
例えば、
宣言
GLOBALVALUEREF(int, ijk);
は、
変数ijk
をint [1]
型の配列として宣言します。
これは、
globalvalueが実際には定数であり、
リンカが通常アドレスとみなすものが、
その「値」となるからです。
C においては、
整数値はこのようには扱われません。
このように扱われるのは、
配列ですから、
シンボルを配列名として扱うことで、
一貫性のある結果が得られます。
その値が正しくない型を持つように見えるということだけが欠点です。
この配列の要素にアクセスしようなどと試みてはいけません。
この配列は要素をまったく持っていません。
配列の「アドレス」は、
実際の記憶域のアドレスとは異なる可能性があります。
シンボルが実際には配列であるという事実のために、 その変数が使用されているところで、 警告の出力されることがあるかもしれません。 こうした警告を回避するには、 型のキャストを挿入してください。 以下に例を示します。 この例では、 マクロの展開結果がそのマクロ自身と同じ名前を使うことができるという、 ANSI Cの特徴を利用しています。
GLOBALVALUEREF (int, ss$_normal); GLOBALVALUEDEF (int, xyzzy,123); #ifdef __GNUC__ #define ss$_normal ((int) ss$_normal) #define xyzzy ((int) xyzzy) #endif
globaldef
やglobalref
を、
列挙型の変数に対して使用してはなりません。
列挙型のサポートは、
まだ実装されていません。
代替手段として、
変数を整数型にして、
個々の列挙値(enumeration value)に対してglobalvaluedef
を使用してください。
この実例は、
以下のようになるでしょう。
#ifdef __GNUC__ GLOBALDEF (int, color, 0); GLOBALVALUEDEF (int, RED, 0); GLOBALVALUEDEF (int, BLUE, 1); GLOBALVALUEDEF (int, GREEN, 3); #else enum globaldef color {RED, BLUE, GREEN = 3}; #endif
main
関数の中で戻り値が明示的に指定されていない場合、
GNU CCはデフォルトで1を返すよう自動的に調整します。
VMSはこれを、
通常の正常終了を示すステータス・コードとして解釈します。
バージョン1のGNU CCは、
このデフォルト値を提供していませんでした。
VMS上のGNU CCは、 GNUアセンブラGASとの組み合わせにおいてのみ正しく機能します。 VMSデバッガ用の正当なデバッグ情報を生成するためには、 バージョン1.37以降のGASが必要です。 GASにより生成されたオブジェクト・ファイルは、 通常のVMSリンカを使ってリンクします。
以前のバージョンのGNU CCでは、 生成されたコードが、 共用ライブラリ`VAXCRTL'とリンクされた場合に、 たまに奇妙な結果をもたらすことがありました。 現在のバージョンでは、 問題なく動作するはずです。
const
指定された広域変数を使用する場合には、
1つ注意しなければならないことがあります。
その変数を使用するすべてのソース・ファイルの中の、
その変数のすべての外部宣言において、
const
修飾子が指定されなければならないということです。
こうしないと、
その変数の矛盾する属性に関する警告をリンカが出力します。
このような警告が出力されてもプログラムは動作するでしょうが、
その変数は書き込み可能な記憶域に置かれるでしょう。
VMSリンカが広域シンボルの大文字・小文字を区別するにもかかわらず、 ほとんどのVMSコンパイラは広域シンボルの名前を大文字に変換しますし、 ほとんどのランタイム・ライブラリも大文字のシンボル名を持っています。 こうしたルーチンを確実に呼び出すことができるように、 GNU CCは (アセンブラGASによって) 他のVMSコンパイラと同様に、 広域シンボル名を大文字に変換します。 しかし、 Cにおける通常の慣習では大文字・小文字は区別されるので、 GNU CCは (GASを通じて) すべての文字が小文字から構成されるシンボルを除いて、 すべてのシンボル名を拡張することによって、 通常のCの振る舞いを維持しようと試みます。 具体的には、 シンボル名を最大23文字までに切り詰めてから、 これら23文字の大文字・小文字のパターンをエンコードしたものを、 切り詰められたシンボル名の末尾に付加します。 ドル記号を1つでも含むシンボル名は例外で、 これらは拡張されることなく、 直接大文字に変換されます。
名前の拡張は、 別のコンパイラにより生成されたコンパイル済みのライブラリ (例えばXlib) を使うプログラムに対して良くない結果をもたらします。 コンパイラ・オプション`/NOCASE_HACK'を使うことによって、 名前の拡張を禁止することができます。 これは、 通常VMS上でそうであるように、 Cの外部関数や外部変数を大文字・小文字に依存しないようにします。 別の手段として、 そのようなライブラリ内の関数や変数を参照しているすべての部分を小文字を使って書くこともできます。 ただし、 VMS上ではこれで問題なく動作するでしょうが、 他のシステムに対する移植性はありません。 コンパイラ・オプション`/NAMES'によっても、 広域における名前の取り扱いに関する制御を行うことができます。
GNU C++では、 関数名や変数名はまた異なった取り扱いを受けます。 GNU C++コンパイラは、 関数名に対して名前のマングル処理(name mangling)を実行します。 これは、 関数の取る引数のデータ型に関する情報が関数名に付加されることを意味します。 その1つの結果として、 関数の名前が非常に長くなる可能性があります。 VMSのリンカは名前の最初の31文字しか認識しないので、 個々の関数や変数が、 31文字で表現可能なユニークな名前を確実に持つようにするために、 特別の処理が行われます。
名前 (必要な場合には名前の拡張部分も含めた) 長さが32文字未満の場合には、 特別な処理は実行されません。 名前の長さが31文字を超える場合には、 アセンブラ(GAS)は、 関数名をもとにハッシュ文字列(hash string)を生成し、 関数名を23文字に切り詰めてから、 切り詰められた名前の末尾にハッシュ文字列を付加します。 コンパイラ・オプション`/VERBOSE'が使用されると、 アセンブラは、 切り詰められた個々のシンボルの完全な名前と切り詰められた名前を表示します。
libg++を使用するプログラムをコンパイルする時には、
`/NOCASE_HACK'コンパイラ・オプションを使うべきではありません。
libg++には、
大文字・小文字を区別しない環境では互いに区別することができなくなってしまうようなオブジェクトのインスタンス
(例えば、
Filebuf
とfilebuf
)
がいくつか存在します。
そのために、
名前の拡張処理を個別に(選択して)禁止する必要のある場合がでてきます
(例えば、
libg++とXlibを同一プログラムの中で併用する場合)。
これを行うために用意された特別な機能はありませんが、
大文字・小文字の混在するシンボルのうち、
名前の拡張処理を禁止したいもの1つ1つに対してマクロを定義することによって、
同じことを実現することができます。
このマクロは、
シンボル名を構成するすべての文字を小文字に換えたものに展開されなければなりません。
以下に例を示します。
#define StuDlyCapS studlycaps
これらのマクロ定義は、 ソース・コードに対する変更を最小にするために、 ヘッダ・ファイルの中に置くことができます。