sprout::apply_visitor で static_visitor に依存しないアプローチ

Sprout.Variant の apply_visitor に『static_visitor を継承していない(result_type が定義されていない) Visitor を渡したい!』というムチャぶりをした結果、いくつかの代案が出たので覚書。

1.文字列にして返す

言葉通り文字列にして返してあとから復元します。

#include <sprout/variant.hpp>
#include <sprout/string.hpp>


struct twice{
    template<typename T>
    constexpr auto
    operator ()(T const& t)
    ->decltype(sprout::to_string(t + t)) const{
        return sprout::to_string(t + t);
    }
};


int
main(){
    typedef sprout::variant<int, double, sprout::string<16>> var;
    {
        constexpr var v{42};
        constexpr auto result = sprout::apply_visitor(twice{}, v);
        static_assert(result == "84", "");
        static_assert(sprout::from_string<int>(result) == 84, "");
    }
    {
        constexpr var v{1.25};
        constexpr auto result = sprout::apply_visitor(twice{}, v);
        static_assert(result == "2.500000", "");
        static_assert(sprout::from_string<double>(result) == 2.5, "");
    }
    {
        constexpr var v{ sprout::stretch<16>("homu") };
        constexpr auto result = sprout::apply_visitor(twice{}, v);
        static_assert(result == "homuhomu", "");
    }

    return 0;
}


実用性は兎も角として、アプローチとしては面白いですね。


2.Variant を返す

Variant の各型を適用した戻り値型の Variant を返します。
この場合は、static_visitor ではなくて static_variant_visitor を Visitor が継承して使用します。

#include <sprout/variant.hpp>
#include <sprout/string.hpp>


struct twice
    : sprout::static_variant_visitor{

    template<typename T>
    constexpr auto
    operator ()(T const& t)
    ->decltype(t + t) const{
        return t + t;
    }
};


int
main(){
    typedef sprout::variant<int, double, sprout::string<16>> var;
    {
        constexpr var v{42};
        constexpr auto result = sprout::apply_visitor(twice{}, v);
        static_assert(sprout::get<0>(result) == 84, "");
        static_assert(result == 84, "");
    }
    {
        constexpr var v{1.25};
        constexpr auto result = sprout::apply_visitor(twice{}, v);
        static_assert(sprout::get<1>(result) == 2.5, "");
        static_assert(result == 2.5, "");
    }
    {
        constexpr var v{ sprout::stretch<16>("homu") };
        constexpr auto result = sprout::apply_visitor(twice{}, v);
        static_assert(sprout::get<2>(result) == "homuhomu", "");
        static_assert(result == sprout::stretch<32>("homuhomu"), "");
    }

    return 0;
}

3.sprout::as_variant_visitor を使用する

動作的には 2. と同じように Variant を返すのですが、static_variant_visitor を継承するのではなくて、as_variant_visitor でラップすることで非侵入的に使用します。

#include <sprout/variant.hpp>
#include <sprout/string.hpp>


struct twice{
    template<typename T>
    constexpr auto
    operator ()(T const& t)
    ->decltype(t + t) const{
        return t + t;
    }
};


int
main(){
    typedef sprout::variant<int, double, sprout::string<16>> var;
    {
        constexpr var v{42};
        constexpr auto result = sprout::apply_visitor(sprout::as_variant_visitor(twice{}), v);
        static_assert(sprout::get<0>(result) == 84, "");
        static_assert(result == 84, "");
    }
    {
        constexpr var v{1.25};
        constexpr auto result = sprout::apply_visitor(sprout::as_variant_visitor(twice{}), v);
        static_assert(sprout::get<1>(result) == 2.5, "");
        static_assert(result == 2.5, "");
    }
    {
        constexpr var v{ sprout::stretch<16>("homu") };
        constexpr auto result = sprout::apply_visitor(sprout::as_variant_visitor(twice{}), v);
        static_assert(sprout::get<2>(result) == "homuhomu", "");
        static_assert(result == sprout::stretch<32>("homuhomu"), "");
    }

    return 0;
}


個人的にはこれが一番しっくり来ますね。
と、いう感じでいくつか考えてもらいました。