C++11とC++98 & boostの切り替え

 現在、21世紀なので普段はC++11を基本的に使っているのだが、たまにC++98でないといけないケースは存在する。共用のサーバにC++11対応コンパイラが入っていない、などだ(なぜ入っていないかは謎である)。かといって石は何の変哲もないXeonだったりするので非常に謎が多い。C++11対応を望む声が小さいのかもしれない。

 ところで、std::arrayを使いたいという欲求はC++erにとっては非常に一般的なものだろう。しかしstd::arrayC++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::arrayC++11の時はstd::arrayに、C++98 & boostの時はboost::arrayになる。C++11ではstatic_assertstd::is_sameがあるが、C++98にはないので確認用のコードを再実装しないといけないのがほんの少し面倒だったが期待通り動いた。初めはtypedefを用いてやろうとしたのだが、template typedefは存在せずtemplate using aliasC++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で言及されていた。

stackoverflow.com

わかってはいたことだが(自分が思いつく程度のTipsはだいたい既知)、探してもいないのに見つけてしまうと覚悟ができていないので少しがっかりするものだ。ここまで完全に一致すると逆に面白くすらあるけれど。