読者です 読者をやめる 読者になる 読者になる

Fusion Sequenceの変換(ボツ)

C++

Fusion Sequenceのある型から、他のFusion Sequenceの型に変換する統一的な機構があればライブラリの中での可能性が広がるのになーと以前から思ってました。as_vector, as_listで使い分けるのではなく、型変換演算子で自動的に変換先の型を推論してそのFusion Sequenceに変換してくれたらいいのに、と。

こんなのを書いてみましたが

#include <string>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/list.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/make_list.hpp>
#include <boost/fusion/include/vector_tie.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/comparison.hpp>
#include <boost/detail/lightweight_test.hpp>

#include <boost/functional/value_factory.hpp>
#include <boost/fusion/include/make_fused.hpp>

struct Data {
    int i;
    char c;
    std::string s;

    Data(int i_, char c_, const std::string& s_)
        : i(i_), c(c_), s(s_) {}
};

bool operator==(const Data& lhs, const Data& rhs)
{
    return boost::fusion::vector_tie(lhs.i, lhs.c, lhs.s) ==
           boost::fusion::vector_tie(rhs.i, rhs.c, rhs.s);
}


BOOST_FUSION_ADAPT_STRUCT(
    Data,
    (int, i)
    (char, c)
    (std::string, s)
)

template <class Sequence>
struct fucopy_t {
    const Sequence& seq;

    fucopy_t(const Sequence& seq_) : seq(seq_) {}

    template <class USequence>
    operator USequence() const
    {
        return boost::fusion::make_fused(boost::value_factory<USequence>())(seq);
    }
};

template <class Sequence>
fucopy_t<Sequence> fucopy(const Sequence& seq)
{
    return fucopy_t<Sequence>(seq);
}

int main()
{
    namespace fusion = boost::fusion;

    const fusion::vector<int, char, std::string> v(1, 'a', "Hello");

    const fusion::list<int, char, std::string> ls = fucopy(v);
    BOOST_TEST(ls == fusion::make_list(1, 'a', "Hello"));

    const fusion::vector<int, char, std::string> v2 = fucopy(ls);
    BOOST_TEST(v2 == fusion::make_vector(1, 'a', "Hello"));

    const Data d = fucopy(v2);
    BOOST_TEST(d == Data(1, 'a', "Hello"));

    return boost::report_errors();
}

これは、Fusion Sequenceの要件とは関係なく、その型固有のコンストラクタシグニチャに依存するのであまりよろしくない。Fusion Sequenceにアダプトされた型は、全てのメンバ変数が必ずしもシーケンスに使われるわけではないので、コンストラクタ呼び出しは危険ですね。


さらに、fusion::listやfusion::vectorコンストラクタが、enable_ifを使用しないtemplate になっているので、変換時にコンストラクタのオーバーロード解決が曖昧だと言われます。
パッチを当てれば通るようになりますが、

// boost/fusion/container/vector/vector.hpp
#include <boost/utility/enable_if.hpp>
#include <boost/fusion/support/is_sequence.hpp>

...

template <typename Sequence>
vector(Sequence const& rhs, typename boost::enable_if<traits::is_sequence<Sequence> >::type* = 0)
// boost/fusion/container/list/list.hpp
#include <boost/utility/enable_if.hpp>
#include <boost/fusion/support/is_sequence.hpp>

...

template <typename Sequence>
list(Sequence const& rhs, typename boost::enable_if<traits::is_sequence<Sequence> >::type* = 0)

この方法がまずよろしくないので、ボツにしよう。


やはり、fusion::copyアルゴリズムがほしいところですが、mutable algorithmが存在しないFusionにおいては提案しても蹴られそうですね・・・。