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

2015/02/23 追記:
[次の記事](http://qiita.com/adatchey/items/2b489997d95d1b2d4a36) を書きました.

------

boost::variant の実装を覗いてみたらテンプレート引数をプリプロセッサで頑張っているように見えたので, Variadic Template で作ってみたくなりました.

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


```variant.hpp
//
//  variant.h
//
//  Created by atosh on 2/21/15.
//

#ifndef VARIANT_H_
#define VARIANT_H_

#include <type_traits>
#include <cstring>

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);
};

//! 何番目の型か (0 origin)
template <int N, typename Target, typename T, typename... Rest>
struct index_of_impl_t {
    static const int value = (std::is_same<Target, T>::value) ? N : index_of_impl_t<N + 1, Target, Rest...>::value;
};

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

//! 何番目の型か (0 origin)
template <typename Target, typename... Types>
struct index_of_t {
    static const int value = index_of_impl_t<0, Target, Types...>::value;
};

//! インデックスを指定して型を取得する
template <int N, typename T, typename... Rest>
struct at_t {
    using type = typename at_t<N - 1, Rest...>::type;
};

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

//! variant 実装
template <typename... Types>
class variant {
public:
    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;
    }
    template <typename T>
    const T&
    get() const
    {
        return cast<T>();
    }
    template <typename T>
    T&
    get()
    {
        return cast<T>();
    }
    int
    which() const
    {
        return which_;
    }

private:
	//! 代入
    template <typename T>
    void
    assign(const T& value)
    {
        static_assert(contains_t<T, Types...>::value, "");
        static_assert(sizeof(value) <= kStorageSize, "");
        which_ = index_of_t<T, Types...>::value;
        std::memset(storage_, 0, kStorageSize);
        new (storage_) T(value);
    }
    //! 代入, char* の特殊化
    void
    assign(const char* const value)
    {
        assign<std::string>(value);
    }
    //! 型変換
    template <typename T>
    T&
    cast()
    {
        static_assert(contains_t<T, Types...>::value, "");
        return *reinterpret_cast<T*>(storage_);
    }
    //! 別オブジェクトからのコピー
    void
    copy(const variant& other)
    {
        std::memcpy(storage_, other.storage_, kStorageSize);
        which_ = other.which_;
    }

    static const int kStorageSize = max_size_t<Types...>::value;
    int which_;
    char storage_[kStorageSize];
};

template <typename... Types>
inline bool operator==(const variant<Types...>& left, const variant<Types...>& right)
{
	// 比較が浅い
    return std::memcmp(&left, &right, sizeof(variant<Types...>)) == 0;
}

template <typename... Types>
inline bool operator!=(const variant<Types...>& left, const variant<Types...>& right)
{
	// 比較が浅い
    return std::memcmp(&left, &right, sizeof(variant<Types...>)) != 0;
}

}  // 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<int64_t, double, std::string> var;
    var a = int64_t(10);
    var b = int64_t(20);
    var c = int64_t(10);
    var d = 3.14;
    var e = "foobar";
    assert(!(a == b));
    assert(a == c);
    assert(!(a == d));
    assert(!(a == e));
    a.get<int64_t>() += 10;
    assert(a == b);
    assert(a.which() == 0);
    a = 3.14;
    assert(a.which() == 1);
    assert(a == d);
    
    return 0;    
}

```

operator== が深い比較をしていないため std::string なんかだと結果が正しくないのが課題です. ちゃんと Visitor クラスを用意せねばなりません.
また assign, copy 時のリソースリーク, 例外非安全性がありますので実用には絶えませんが, 実現方法検討のサンプルということで.