現在、21世紀なので普段はC++11を基本的に使っているのだが、たまにC++98でないといけないケースは存在する。共用のサーバにC++11対応コンパイラが入っていない、などだ(なぜ入っていないかは謎である)。かといって石は何の変哲もないXeonだったりするので非常に謎が多い。C++11対応を望む声が小さいのかもしれない。
ところで、std::array
を使いたいという欲求はC++erにとっては非常に一般的なものだろう。しかしstd::array
はC++11だ。なので、boost::array
を代わりに使うというケースがある。実際、これらの差は非常に軽微なので今までのところこれらが入れ替わったことによって問題が生じたことは(私のこれまでの短い経験では)ない。違うのはdata()
メンバ関数とかだろうか。どうせならその辺りをちゃんと(実装例のソースを読んで)調べてみればよかった。
さて、幾つかのプロジェクトで使うであろうライブラリを書くとする。その中にはC++98しか使えない状況で使うことが想定される場合、常にboost::array
を使わざるを得ないだろうか。C++11が使える状況でもそのライブラリを使うなら、できればC++11ならstd::array
が、C++98ならboost::array
が使われるように分岐してほしい。今年の夏くらいに少しそのことについて考えて、以下のようなものを書いた。プロジェクト名と名前空間はmylibとしている。
#if __cplusplus >= 201103L #define MYLIB_ENABLE_CXX11 #endif #ifdef MYLIB_ENABLE_CXX11 #include <array> namespace mylib { using std::array; }//mylib #else #include <boost/array.hpp> namespace mylib { using boost::array; }//mylib #endif
このようにすると、mylib::array
がC++11の時はstd::array
に、C++98 & boostの時はboost::array
になる。C++11ではstatic_assert
とstd::is_same
があるが、C++98にはないので確認用のコードを再実装しないといけないのがほんの少し面倒だったが期待通り動いた。初めはtypedef
を用いてやろうとしたのだが、template typedef
は存在せずtemplate using alias
はC++11なので本末転倒となり少し悩んだ後、単純にusing
すれば良いことに気づいた。また、最初はENABLE_CXX11
をビルド時に指定する方針にしていたが、__cplusplus
がまさにうってつけなようだったので、それを用いることにした。
これは、私が関わっていて、そこからたくさんの事を学んだプロジェクトの中で書かれていたコードに着想を得ている。それはもっと細かくどのライブラリが使えるかを想定して、同じくマクロ定義により分岐し、type_generator
によって型を分岐していた。例えば上の例でいくと、
#if defined(HAVE_CXX11) #include <array> #elif defined(HAVE_BOOST) #include <boost/array.hpp> #endif /*...*/ template<typename T, std::size_t N> struct get_array_type { #if defined(HAVE_CXX11) typedef std::array<T, N> type; #elif defined(HAVE_BOOST) typedef boost::array<T, N> type; #endif };
となっており、実際に使う時は
typename get_array_type<double, 3>::type position;
のようにしていた。
あまりにも当然のことだが、上記は他のライブラリでよく見かける
#ifdef MYLIB_ENABLE_CXX11 #define MYLIB_CONSTEXPR constexpr #else #define MYLIB_CONSTEXPR #endif
のようなものと併用できる。最初にこう言うものと同時に上のclass分岐を書いておくというやり方に最近ハマっている(ハマるほど何度も書いてはいないが)。
ところで、上のような方法はboost::shared_ptr
など諸々の他の非常に使いたいものにも適用できるが、それはインターフェースが同じところまでであることに注意したい。内部ではmylib::shared_ptr
を使って全てを実装することになるので、インターフェースが違ってしまうと意識に上っていなかった方では問題が生じる。コンパイルが通らなくなるなら発覚しやすく修正しやすい問題だが、違う事をするインターフェースで通ってしまったりするとヤバめのバグが発生するだろう。今の所出くわしてはいないが、両方に差がないかを調べたりしておくのが賢明なのだろう。調べてないけど。
ところで、shared_ptrってboostとstdとどんな違いがあったかな〜とこれを書きながら調べていると上記のものと全く(完全に!)同じコードが2年も前にstack overflowで言及されていた。
わかってはいたことだが(自分が思いつく程度のTipsはだいたい既知)、探してもいないのに見つけてしまうと覚悟ができていないので少しがっかりするものだ。ここまで完全に一致すると逆に面白くすらあるけれど。