gintenlib.cast を使う

これは、使用例を見れば一目瞭然でしょう:

#include <gintenlib/cast.hpp>

#include <iostream>
using namespace std;


// 適当に多重定義関数を用意します
void hoge( int )  { cout << "int version\n"; }
void hoge( void* ){ cout << "void* version\n"; }

// 同様にクラスに対しても関数を用意します
// なんとなく noncopyable なクラスで作る
#include <boost/noncopyable.hpp>
class base : boost::noncopyable {};
struct derived : base {};

void fuga( base& )   { cout << "base& version\n"; }
void fuga( derived& ){ cout << "derived& version\n"; }


int main()
{
  // 予め using 宣言すれば四文字で使えて幸せ
  using gintenlib::cast;

  // int 版
  hoge( 0 ); 
  // void* 版。static_cast<void*>(0) でもOKだが長い
  hoge( cast<void*>(0) );
  
  derived x;  // あるいは base x;
  // base& 版
  fuga( cast<base&>(x) );
  // derived& 版。base x; の場合はコンパイルエラー
  fuga( cast<derived&>(x) );  
  // static_cast だと、コンパイルエラーにならない
}

結果:

int version
void* version
base& version
derived& version

このように、単純に「型を変換する」ことができます。
static_cast を用いてもよいのですが、ポインタや参照関連で安全性が損なわれる可能性があります。 gintenlib::cast はこの点で完全に安全、文字数も予め using 宣言しておけば四文字と、手軽な型変換にぴったりの代物となっています。
・・と、偉そうに語りましたが、実はこれ、 Boost に implicit_cast という同機能の代物があります(実装は僅かに違いますが機能は全く同じ)。
というわけで、移植性を考える場合には、迷わず Boost の方を使いましょう。
一応「文字数が少ない」という便利さが残っているので、削除はしませんが :)


さてここからは、ちょっとした舞台裏の小話。読んでも読まなくても。



さて、この gintenlib::cast の実装ですが、最初、こんな感じになっていました:

template<typename Target, typename Source>
inline Target cast( const Source& src )
{
  return src;
}

まー普通のコードで、特に問題はない・・・と思いきや、このコードには弱点があります。
それは「参照型の変換ができない」という点。 const& で受け取っているので当然ですね(実はもう一つ弱点がありますが、当時は気付きませんでした。今気付いた貴方は凄い)


そこで、 boost::enable_if を用いて、このように書き換えます。

// 参照版
template<typename Target, typename Source>
inline Target cast( Source& src,
  typename boost::enable_if< boost::is_reference<Target> >::type* = 0 )
{
  return src;
}
// 非参照版
template<typename Target, typename Source>
inline Target cast( const Source& src,
  typename boost::disable_if< boost::is_reference<Target> >::type* = 0 )
{
  return src;
}

これで、参照に対しても、きちんと動くようになりました。
ただ、不満点としてソースコードが長くなってしまったので、 マクロ GINTENLIB_ENABLE_IF ってのを作って、

// 参照版
template<typename Target, typename Source>
inline Target cast( Source& src,
  GINTENLIB_ENABLE_IF(( boost::is_reference<Target> )) )
{
  return src;
}
// 非参照版
template<typename Target, typename Source>
inline Target cast( const Source& src,
  GINTENLIB_DISABLE_IF(( boost::is_reference<Target> )) )
{
  return src;
}

としました。これで cast<Base&>( derived ) 的なコードも動きます。


これで gintenlib::cast は完成した・・・と思いきや、実はまだ欠点が。
それは cast<void*>(0); というコードが動かない点です。 int から void* には変換できないぞ、と言われてしまいます。
まー当然です。前情報なしに 0 って書いたら、そりゃ整数リテラルだと判断されてしまうので。


これをどうしようものか、と考えていた僕の頭に、ある日突然アイデアが降ってきました。それは、そもそもニ引数テンプレートにしなくても、

template<typename Target>
inline Target cast( Target src )
{
  return src;
}

とすればいい、というもの。
とはいえ、これでは「対象の型を明示しなくても動いてしまう」という弱点が有ります。それに、値渡しになるため、余計なコピーが行われる可能性も。


僕はとりあえず前者は置いておき、後者に対応することにしました。こういうときは boost::call_traits です。

template<typename Target>
inline Target cast( typename boost::call_traits<Target>::param_type src )
{
  return src;
}

こう書いて、おや、と思います。これで前者の問題も解決したじゃないですか。というわけで、これで完成です。


・・・とまぁ、銀天ライブラリは、こんな感じで回り道しながら、ゆっくり作られているのでした。


ちなみに後で boost::implicit_cast の存在を知った時、ソースコード見たら似た感じになっていて、ちょっとだけ誇らしかったのですが、同時に自分が車輪を最発明していた事実に気付き、軽く凹んだり。
確かに存在しない道理は無いよなー。いや、うっかりうっかり