関数内部の処理の状況によって、返却する値の型を変更したい場合ってありますよね?

こんなとき、VBなどではVariant型という、何の型でも入れられる型があります。

こんなことできないでしょうか?


実は、Boostのanyクラスを使用するとできます。

たいていの場合、これで解決するかと思います。


ここでは、あえて自分で似たようなクラスを作成してみましょう。

ただし、数個の特定の型だけ扱えるクラスです。

anyと違い、ある程度、型と型の間の変換を柔軟にしてくれるものを目指します!



【サンプル】

#include <cstdlib>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <ctime>
#include "boost/shared_ptr.hpp"
#include "boost/lexical_cast.hpp"

//こちらの記事 をコピーして以下のファイル名で保存してください

#include "field.h "



using namespace nana;

using namespace boost;


int main(int argc, char *argv[])
{
    vector<nana::FieldPtr> vec;


    try{

     //データを配列に代入(型が違うのに1つの配列に代入できます)
      vec.push_back( nana::createFieldPtr<string>("123") );
      vec.push_back( nana::createFieldPtr<long>(5) );
      vec.push_back( nana::createFieldPtr<DateTime>(DateTime()) );
      vec.push_back( nana::createFieldPtr<float>(123.456) );


     //文字として扱ってみる
      vector<FieldPtr>::iterator i;
      vector<FieldPtr>::iterator begin = vec.begin();
      vector<FieldPtr>::iterator end = vec.end();
      for(i=begin; i!=end; ++i){
        cout << (*i)->getString() + "#" << endl;
      }


     //floatとして扱ってみる
      for(i=begin; i!=end; ++i){
        cout << (*i)->getFloat() << endl;
      }

    }catch(exception& e){
      cout << e.what() << endl;
    }


    system("PAUSE");
    return EXIT_SUCCESS;
}





【入力】

string, long, date_time, float の型を入力にしています。


【出力】

標準出力に、1回目は文字列として、2回目は浮動小数として出力します。


123#
5#
1206980017#
123.456#
123
5
1.20698e+009
123.456




【説明】

main()の、配列に push_back() している箇所を見てみてください。

string, long, date_time, float など様々型を1つの配列に入れられています!


<ポイント>

field.h を見ますと記述しているクラスは基底クラスとその派生クラスです。


ポイントは、基底クラスは一つのクラスで、その派生クラスがtemplateになっているところです。

また、templateで、指定の型以外の使用を禁止するため、この記事 のテクニックを使用しています。


派生クラスは型を指定することで様々なクラスを生成しますが、その基底クラスが1つなので

すべて同じ基底クラスにキャストできます!


これを利用することで、違う型どうしを1つのvectorに代入できます!!にひひ



<ヘルパー関数:createFieldPtr

これは、FieldValue<Type> をnewして、さらにshared_ptrに設定したものを返します。

これのコードを毎回記述するのが面倒なので、それを作成する関数を作成しています。

field.hには、いくつかクラスがありますが、使用するときはこの関数を呼び出すだけです。


<利用用途>
上記の例のように vector に入れることもできますが、

関数の返り値を、FieldPtr にするということもできます。


こうすれば、関数の引数の値によって返り値の型を変更したいときも大丈夫です。



【補足】

上記の例では、Fieldクラスとその派生クラスのgetXxxx()は、lexical_castで値を取得するようにしています。

これはプログラムをシンプルにするためです。

lexical_castは基本的にはあまり速くありません。

ですので、処理速度が気になる場合は、templateの特殊化を利用して速度が速くなるようにしましょう。



【その他】

上記のように、型を自由にできるクラスとしては、Boost::anyがあります。

これでも十分上記のようなことができるかと思います。

弱点としては、型をany型からintにするとか、any型からstringにするときに、内部で保管している型と

変換後の型が同じでないとexceptionが発生します。



参考:

templateで一部の型以外の使用を禁止するには?

・templateの特殊化とは?

・違う型どうしを自由に変換するには?  ←lexical_castの記事

・boost::any