Variadic Template を使って boost::variant もどきの Visitor を作ってみた


[前回](http://qiita.com/adatchey/items/e3338bbc82a940e1cc7b)の続きです.
operator==で値比較をしていなかったので, できるようにしました.

本家はこちら: http://www.boost.org/doc/libs/1_57_0/doc/html/variant.html

```variant_util.h
//
//  variant_util.h
//  メタ関数群
//  Created by atosh on 2/22/15.
//

#ifndef VARIANT_UTIL_H_
#define VARIANT_UTIL_H_

#include <type_traits>

namespace fenf {
namespace detail {

template <typename T, typename... Rest>
struct max_size_t {
private:
    static const int kRestMax = max_size_t<Rest...>::value;

public:
    static const int value = sizeof(T) > kRestMax ? sizeof(T) : kRestMax;
};

template <typename T>
struct max_size_t<T> {
    static const int value = sizeof(T);
};

template <typename Target, typename T, typename... Rest>
struct contains_t {
    static const bool value = (std::is_same<Target, T>::value) ? true : contains_t<Target, Rest...>::value;
};

template <typename Target, typename T>
struct contains_t<Target, T> {
    static const bool value = (std::is_same<Target, T>::value);
};

template <int I, typename Target, typename T, typename... Rest>
struct index_of_impl_t {
    static const int value = (std::is_same<Target, T>::value) ? I : index_of_impl_t<I + 1, Target, Rest...>::value;
};

template <int I, typename Target, typename T>
struct index_of_impl_t<I, Target, T> {
    static const int value = (std::is_same<Target, T>::value) ? I : -1;
};

template <typename Target, typename... Types>
struct index_of_t {
    static const int value = index_of_impl_t<0, Target, Types...>::value;
};

template <int I, typename T, typename... Rest>
struct at_t {
    using type = typename at_t<I - 1, Rest...>::type;
};

template <typename T, typename... Rest>
struct at_t<0, T, Rest...> {
    using type = T;
};

}  // namespace detail
}  // namespace fenf

#endif  // VARIANT_UTIL_H_
```

```variant_visitor.h
//
//  variant_visitor.h
//  apply_visitor の定義
//  Created by atosh on 2/22/15.
//

#ifndef VARIANT_VISITOR_H_
#define VARIANT_VISITOR_H_

#include <type_traits>
#include <cstring>
#include <sstream>

namespace fenf {
namespace detail {

template <class Visitor, class Variant>
typename std::result_of<Visitor(const Variant&)>::type
apply_visitor(Visitor visitor, const Variant& var)
{
    return visitor(var);
}

template <class Visitor, class Variant>
typename std::result_of<Visitor(Variant&)>::type
apply_visitor(Visitor visitor, Variant& var)
{
    return visitor(var);
}

}  // namespace detail

using detail::apply_visitor;

}  // namespace fenf

#endif  // VARIANT_VISITOR_H_
```

```variant.h
//
//  variant.h
//  variant クラスの定義
//  Created by atosh on 2/21/15.
//

#ifndef VARIANT_H_
#define VARIANT_H_

#include <cstring>
#include <sstream>

#include "variant_util.h"
#include "variant_visitor.h"

namespace fenf {
namespace detail {

template <typename T>
struct get_visitor_t {
    template <class Variant>
    const T& operator()(const Variant& var) const
    {
        static_assert(Variant::template contains_t<T>::value, "");
        return *reinterpret_cast<const T* const>(var.storage());
    }
};

template <typename T>
struct mutable_get_visitor_t {
    template <class Variant>
    T& operator()(Variant& var) const
    {
        static_assert(Variant::template contains_t<T>::value, "");
        return *reinterpret_cast<T*>(var.mutable_storage());
    }
};

template <typename T>
struct assign_visitor_t {
    const T& value_;
    assign_visitor_t(const T& value) : value_(value)
    {
    }
    template <class Variant>
    void operator()(Variant& var) const
    {
        static_assert(Variant::template contains_t<T>::value, "");
        static_assert(sizeof(T) <= Variant::kStorageSize, "");
        var.mutable_which() = Variant::template index_of_t<T>::value;
        std::memset(var.mutable_storage(), 0, Variant::kStorageSize);
        new (var.mutable_storage()) T(value_);
    }
};

template <class Variant>
struct copy_visitor_t {
    const Variant& other_;
    copy_visitor_t(const Variant& other) : other_(other)
    {
    }
    void operator()(Variant& var) const
    {
        std::memcpy(var.mutable_storage(), other_.storage(), Variant::kStorageSize);
        var.mutable_which() = other_.which();
    }
};

template <int I, class Variant>
struct is_equal_visitor_t {
    const Variant& other_;
    is_equal_visitor_t(const Variant& other) : other_(other)
    {
    }
    bool operator()(const Variant& var) const
    {
        // valueの型に一致するVisitorを再帰的に探す
        if (var.which() != I) {
            return apply_visitor(is_equal_visitor_t<I - 1, Variant>(other_), var);
        }
        using T = typename Variant::template at_t<I>::type;
        return var.template get<T>() == other_.template get<T>();
    }
};

template <class Variant>
struct is_equal_visitor_t<-1, Variant> {
    is_equal_visitor_t(const Variant&)
    {
    }
    bool operator()(const Variant&) const
    {
        return false;
    }
};

template <int I, class Variant>
struct to_string_visitor_t {
    std::string operator()(const Variant& value) const
    {
        // valueの型に一致するVisitorを再帰的に探す
        if (value.which() != I) {
            return apply_visitor(to_string_visitor_t<I - 1, Variant>(), value);
        }
        using T = typename Variant::template at_t<I>::type;
        std::stringstream ss;
        ss << apply_visitor(get_visitor_t<T>(), value);
        return ss.str();
    }
};

template <class Variant>
struct to_string_visitor_t<-1, Variant> {
    std::string operator()(const Variant& value) const
    {
        return "";
    }
};

//! Variadic Template を使った variant 実装
template <typename... Types>
class variant {
public:
    static const int kNumTypes = sizeof...(Types);                //!< 型数
    static const int kStorageSize = max_size_t<Types...>::value;  //!< 最大サイズを持つ型のサイズ

    //! 指定した型が含まれているか調べるメタ関数
    template <typename T>
    using contains_t = contains_t<T, Types...>;
    //! 指定した型のインデックスを返すメタ関数
    template <typename T>
    using index_of_t = index_of_t<T, Types...>;
    //! 指定したインデックスの型を返すメタ関数
    template <int I>
    using at_t = at_t<I, Types...>;

    variant() : which_(-1)
    {
        std::memset(storage_, 0, kStorageSize);
    }
    variant(const variant& other)
    {
        copy(other);
    }
    variant(variant&& other)
    {
        copy(other);
    }
    template <typename T>
    variant(const T& value)
    {
        assign(value);
    }
    variant& operator=(const variant& other)
    {
        copy(other);
        return *this;
    }
    variant& operator=(variant&& other)
    {
        copy(other);
        return *this;
    }
    template <typename T>
    variant& operator=(const T& value)
    {
        assign(value);
        return *this;
    }
    bool operator==(const variant& other) const
    {
        if (which() != other.which()) {
            return false;
        }
        return apply_visitor(is_equal_visitor_t<kNumTypes - 1, variant>(other), *this);
    }
    bool operator!=(const variant& other) const
    {
        return !(*this == other);
    }
    template <typename T>
    const T&
    get() const
    {
        return apply_visitor(get_visitor_t<T>(), *this);
    }
    template <typename T>
    T&
    get()
    {
        return apply_visitor(mutable_get_visitor_t<T>(), *this);
    }
    std::string
    to_string() const
    {
        return apply_visitor(to_string_visitor_t<kNumTypes - 1, variant>(), *this);
    }

    int
    which() const
    {
        return which_;
    }
    int&
    mutable_which()
    {
        return which_;
    }
    const char* const
    storage() const
    {
        return storage_;
    }
    char*
    mutable_storage()
    {
        return storage_;
    }

private:
    template <typename T>
    void
    assign(const T& value)
    {
        apply_visitor(assign_visitor_t<T>(value), *this);
    }
    void
    assign(const char* const value)
    {
        assign<std::string>(value);
    }
    void
    copy(const variant& other)
    {
        apply_visitor(copy_visitor_t<variant>(other), *this);
    }

    int which_;                   //!< 現在セットされている値の型
    char storage_[kStorageSize];  //!< 値をセットする領域
};

}  // namespace detail

using detail::variant;

}  // namespace fenf

#endif  // VARIANT_H_
```

```main.cpp
//
//  main.cpp
//
//  Created by atosh on 2/21/15.
//

#include <iostream>
#include <string>
#include <cstdint>
#include <cassert>

#include "variant.h"

int
main()
{
    typedef fenf::variant<int, double, std::string> var;
    var a = 10;
    var b = 20;
    var c = 10;
    var d = 3.14;
    var e = "foobar";
    var f = "baz";
    var g = "foobar";
    assert(!(a == b));
    assert(a == c);
    assert(!(a == d));
    assert(!(a == e));
    a.get<int>() += 10;  // mutate
    assert(a == b);
    assert(a.which() == 0);
    a = 3.14;  // mutate
    assert(a.which() == 1);
    assert(a == d);
    assert(e != f);
    assert(e == g);

    assert(a.to_string() == "3.14");
    assert(b.to_string() == "20");
    assert(e.to_string() == "foobar");

    return 0;
}
```

operator== が呼ばれると is_equal_visitor_t::operator() を呼びます.
variant::which_ に対応する型にキャストする必要があるので, which_ に一致するテンプレート引数 I を持った is_equal_visitor_t を再帰的に線形探索します.
見つかったら I を引数に取り型を返すメタ関数 at_t を使ってキャストし, 値比較します.
同じ要領で to_string も実装してみました.

存在する問題として assign, copy が例外安全でなく, リークもします. boost.variant の解決方法は[melpon氏の記事](http://d.hatena.ne.jp/melpon/20090831)で解説されています.
また storage_ のアライメントに関する問題も考慮していません. [Cryolite氏の記事](http://d.hatena.ne.jp/Cryolite/20051102)で解説されています.

実装にあたり [Boost.読書会](http://www.co-meeting.com/p/boost-reading/50950eedab5c3725770003c4) のノートを参考にさせていただきました.