2017-01-24 20 views
3

boost::variantのコンバージョンとC++のstd::vectorの組み合わせに関する質問です。私のプロジェクトでは、SQLの出力としてバリアントを使用します。私は常に1種類のバリアントを使用します。私は何をしようとしていることは私の選択の同等のタイプに変異体、バリアント又は変異体の2xvectorのベクトルから容易に変換することです。単純に、私はの線に沿って何か必要があります。ここではまずバリアント、ベクトル<variant>とベクトル<ベクター<variant>>を私の選択と同等のタイプに変換する

std::vector <int> my_variable = convert(some_variant_vector) 

は私の設定です:

#include "boost\variant.hpp" 
#include "boost\date_time\gregorian\gregorian.hpp" 
typedef boost::variant< int, std::string, boost::gregorian::date> _var; 
typedef std::vector<_var> _vec_var; 
typedef std::vector<_vec_var> _vec2_var; 

私は_var(または_vec_varまたは_vec2_var)に変換する簡単な方法を持っているしたいと思いますint/string/dateに依存します。私は私の答えは次のようになりますことを知って、次のpostから:

template<typename T> 
struct converter_visitor : public boost::static_visitor<T> 
{ 
    const _var &converter; 
    converter_visitor(const _var &r) : converter(r) {} 
    T operator()(const _var &) const{ 
     return boost::get<_var>(converter); 
    } 

    const _vec_var &v_converter;   // case of vector<> 
    converter_visitor(const _vec_var &r) : v_converter(r) {} 
    T operator()(const _vec_var &) const{ 
     T ans; 
     ans.reserve(_cont.size()); 
     for (int i = 0; i < _cont.size(); ++i) 
      ans.push_back(boost::get<T>(v_converter[i])); 
     return ans; 
    } 

    const _vec2_var & v2_converter;   // case of vector<vector> 
    converter_visitor(const _vec2_var &r) : v2_converter(r) {} 
    T operator()(const _vec2_var &) const { 
     T ans; 
     ans.reserve(v2_converter.size(), v2_converter[0].size()); 
     for (int i = 0; i < _cont.size(); ++i) 
     { 
      for (size_t j = 0; j < v2_converter[0].size(); j++) 
      { 
       ans.push_back(boost::get<T>(v2_converter[i][j])); 
      } 
     } 
     return ans; 
    } 
}; 

int main() 
{ 
    _var variable = 1; 
    int integer_conversion; 
    boost::apply_visitor(converter_visitor(test), integer_conversion); 
    return 0; 
} 

意図したとおりに動作していないとして、残念ながら私はこれでしばらく立ち往生されており、さらにはコンパイルされません。

+0

'boost :: variant'は一度' std :: string'を保持し、もう一つは同じ 'std :: vector'に' int'を入れることは可能でしょうか? – Exagon

+0

ああ、それは完全に可能です。しかし、私のベクトルは常に同じタイプを保持します。 –

+0

'std :: vector my_variable = convert(some_variant_vector)'を呼び出すと、 'some_variant_vector'に' int'を持つすべてのバリアントのint値を取得しますか? – Exagon

答えて

2

私はあなたが欲しいタイプ取得するには、以下をお勧めします:

template<typename T> 
class converter_visitor : public boost::static_visitor<> 
{ 
public: 
    std::vector<T>& vec; 

    converter_visitor(std::vector<T>& r) : vec(r) {} 

    // only push back values of specific types... 
    void operator()(const T& u) const { 
     vec.push_back(u); 
    } 

    // ignore other types... 
    void operator()(...) const {} 
}; 

template<typename T> 
converter_visitor<T> make_visitor(std::vector<T>& r) { return converter_visitor<T>(r); } 

をして、ネストされたベクトルを扱うことができる再帰フィルタFUNCにそれを漏斗:

template<typename T,typename U> 
void filter(std::vector<T>& result,const U& var) { 
    boost::apply_visitor(make_visitor(result), var); 
} 

template<typename T,typename U> 
void filter(std::vector<T>& result,const std::vector<U>& cont) { 
    std::for_each(cont.begin(),cont.end(),[&](const U& c) { 
     filter(result,c); 
    }); 
} 

その後、あなたが行うことができます。

_var v = 314; 
std::vector<int> result; 
filter(result,v); 
print(result); 

結果:314

_vec_var v; 
v.push_back(2); 
v.push_back(3); 
v.push_back("hello"); 
v.push_back(5); 
v.push_back(7); 
v.push_back("world"); 

std::vector<int> result;   
filter(result,v); 
print(result); 

std::vector<std::string> result2;   
filter(result2,v); 
print(result2); 

結果1:2 3 5 7
結果2:Hello Worldの

_vec_var v1; 
v1.push_back(11); 
v1.push_back(13); 
v1.push_back("see ya"); 

_vec_var v2; 
v2.push_back(17); 
v2.push_back(19); 
v2.push_back("later"); 

_vec2_var vv; 
vv.push_back(v1); 
vv.push_back(v2); 

std::vector<int> result;   
filter(result,vv); 
print(result); 

std::vector<std::string> result2;   
filter(result2,vv); 
print(result2); 

結果1:11 13 17 19
結果2:後

YA参照

see live demo here

+0

これはすばらしい答えです。 –

1

私がコメントで言ったように、私は次のことをやった、あなたの問題への解決策を見つけた:

boost::variantはそうTfalseを保持している場合、私はbool_visitor<T>ウィッヒ戻りtrueを書きました。我々は特定のタイプを保持するdoesntのベクトルのすべての変種を削除することができますこのビジターで

template<typename T> 
struct bool_visitor : public boost::static_visitor<bool> { 

    inline constexpr bool operator()(T val) const 
    { 
     return false; 
    } 

    template<typename K> 

    typename std::enable_if<!std::is_convertible<K, T>::value, bool>::type 
    operator()(K val) const 
    { 
     return true; 
    } 

}; 

:ここ

は訪問者です。 この後、私たちは、彼らが保持型にすべてのバリエーションを変換するstd::transformを使用しています。 私たちは、そうならば、我々は平坦化機能で、最初のベクトルを平らに、与えられたベクトルは、ネストされたベクトル(ベクトル)であるかどうかをチェックするためにSFINAEを使用します。

template <typename FromIter, typename ToIter> 
void flatten (FromIter start, FromIter end, ToIter dest) { 
    while (start != end) { 
     dest = std::copy(start->begin(), start->end(), dest); 
     ++start; 
    } 
} 

ベクターは、私たちに必要なネストされているかどうかをチェックしますis_vectorトレイト:

template<typename> 
struct is_vector : std::false_type {}; 

template<typename T, typename A> 
struct is_vector<std::vector<T,A>> : std::true_type {}; 

今私たちは与えられたベクトルに私たちが望む機能を有効にするためにSFINAEを使用することができます。

template<typename T, typename K> 
typename 
std::enable_if_t< 
     !is_vector<K>::value, 
     std::vector<T>> 
get_vec_of(std::vector<K>& input_vec){ 
    //delete all variants not holding T 
    auto it = std::remove_if(input_vec.begin(), input_vec.end(), 
          [](auto item){ 
           return boost::apply_visitor(bool_visitor<T>(), item); 
          }); 
    //input_vec.erase(it, input_vec.end()); 

    //create new vector of T 
    std::vector<T> return_vec; 

    //transform all variants holding T to T and put them in the return_vec 
    std::transform(input_vec.begin(), it, std::back_inserter(return_vec), 
        [](auto item){ 
         //this can never throw because all variants in the vector are holding T 
         return boost::get<T>(item); 
        }); 

    return return_vec; 
} 

template<typename T, typename K> 
typename 
std::enable_if_t< 
     is_vector<K>::value, 
     std::vector<T>> 
get_vec_of(std::vector<K>& input_vec){ 
    std::vector<typename K::value_type> flatten_vec; 
    flatten(input_vec.begin(), input_vec.end(), std::back_inserter(flatten_vec)); 
    return get_vec_of<T>(flatten_vec); 
}; 

このソリューションは、変更指定されたベクトルの内部の要素の順序これがあなたにとって悪い場合は、おそらく最初にベクトルをコピーする必要があります。

hereは、この機能を使用する方法のデモです、そしてそれがどのように働きますか。