しのはらのC++実験室トップページ
「移植性の高いプログラムの書き方」トップページ
M__NO_NAMESPACE
が定義されているとします。
#ifndef M__NO_NAMESPACE
# define M__BEGIN_NAMESPACE namespace mylib{
# define M__END_NAMESPACE }
# define M__USING_NAMESPACE using namespace mylib;
# define M__NAME_SPEC ::mylib
#else
# define M__BEGIN_NAMESPACE
# define M__END_NAMESPACE
# define M__USING_NAMESPACE
# define M__NAME_SPEC
#endif
どうもここまでは誰もが思い付くことらしいのです。
サークルの先輩にもほとんど同じ事をしている人を発見して、
少しうれしかったです。M__NAME_SPECマクロだけは苦心したんです。
その辺を説明しようと思います。
M__BEGIN_NAMESPACEマクロと
M__END_NAMESPACEマクロによって、
ライブラリのHやCPPファイルのクラス宣言や定義を囲みます。
そうすることでnamespaceが有効なコンパイラでは、
それらを適切な名前空間に配置できます。M__USING_NAMESPACE
マクロを使用します。これはnamespaceに対するusing宣言に置換されます。M__NAME_SPEC(上の定義を参照)
について説明します。Xというクラスがあるとすれば、
M__NAME_SPEC::Xという表記は、
その時のスコープに関係なく同一のクラスXを表わすことになります。
つまり絶対表現です。この様子を示します。
| 表記 | namespace無し | namespace有り |
M__NAME_SPEC |
/*無し*/ |
::mylib |
M__NAME_SPEC::X |
::X |
::mylib::X |
mylib)
にあるクラスの中からクラスの外の識別子を、
曖昧さ無く参照するために使おうとしたものです。
M__BEGIN_NAMESPACE
class Matrix{
/* ... */
public:
friend void inverse(Matrix& ans, const Matrix& m);
void inverse(){
M__NAME_SPEC::inverse(*this,Matrix(*this));
// この上の行に注目
}
};
M__END_NAMESPACE
メンバ関数inverseからfriend関数inverse
を呼び出したいのですが、
同名のメンバ関数がfriend関数を隠してしまうために、
スコープ解決演算子が必要になるわけです。
クラス自体がグローバルである場合は::inverseとすれば良いのですが、
クラス自体がnamespaceに囲まれている場合はmylib::inverse
あるいは::mylib::inverseと書かねばなりません。
その区別のために作ったのがM__NAME_SPECマクロなわけです。
M__NAME_SPECマクロには直後の::
(ダブルコロン)を含めていました。
マクロを使用する場所では当然::を書きません。
つまり、
M__NAME_SPEC inverse(/*..... */ );
のように書いていたのです。これでBorland C++やWatcom C/C++では、
全く問題がなかったのです。::の後の空白」を許さないらしいのでした。
(ちなみに現在の最新バージョンではその問題はなくなっているようです。)そうして結局は最もスマートな、このページの最初に示したやり方に、 落ち着いたわけです。#ifndef M__NO_NAMESPACE # define M__NAME_SPEC(x) ::bslib::x #else # define M__NAME_SPEC(x) ::x #endif