#ifndef CHILON_VARIANT_HPP
#define CHILON_VARIANT_HPP
#include <chilon/meta/at.hpp>
#include <chilon/meta/index_of.hpp>
#include <chilon/meta/max.hpp>
#include <chilon/meta/return.hpp>
#include <chilon/singleton.hpp>
#include <boost/ptr_container/ptr_array.hpp>
#include <utility>
#include <assert.h>
namespace chilon {
/**
* A simple variant which can store one of any of the types in T that
* stores all data on the stack.
* Currently destructors are never called, so types with non-trivial
* destructors are not supported, but this can be fixed when more of the
* c++0x type_traits are complete.
* \tparam T pack of valid types the variant may store.
*/
template <class... T>
struct variant {
enum { n_elements = sizeof...(T) };
enum { max_size = meta::max<sizeof(T)...>::value };
typedef typename meta::head<T...>::type head_type;
template <class V>
variant& operator=(V const& rhs) {
int const new_index = meta::index_of<V, T...>::value;
static_assert(
new_index < n_elements, "type does not exist in variant");
type_index_ = new_index;
cast<V>() = rhs;
return *this;
}
/**
* Change variant to represent a default constructed V
*/
template <class V>
V& construct_default() {
int const new_index = meta::index_of<V, T...>::value;
static_assert(
new_index < n_elements, "type does not exist in variant");
type_index_ = new_index;
return *new (data_) V;
}
/**
* Unsafe access to type at index I.
*/
template <int I>
typename meta::at<I, T...>::type& at() {
static_assert(I < n_elements, "index too high");
#ifndef NDEBUG
assert(type_index_ == I);
#endif
return cast<typename meta::at<I, T...>::type>();
}
/**
* Unsafe const access to type at index I.
*/
template <int I>
typename meta::at<I, T...>::type const& at() const {
static_assert(I < n_elements, "type not found in variant");
#ifndef NDEBUG
assert(type_index_ == I);
#endif
return cast<typename meta::at<I, T...>::type>();
}
template <class Y>
Y& at() {
return at<meta::index_of<Y, T...>::value>();
}
int const type_index() const { return type_index_; }
private:
// unsafe casts
template <class Y>
Y const& cast() const {
return (*reinterpret_cast<Y const *>(data_));
}
template <class Y>
Y& cast() {
return (*reinterpret_cast<Y *>(data_));
}
public:
// default constructor constructs the first element in the tuple
variant() : type_index_(0) {
new (data_) head_type;
}
private:
char data_[max_size];
int type_index_;
};
namespace detail {
template <class T, class F>
struct variant_apply_helper {};
template <class T, class F>
struct variant_apply_lookup_table {
struct apply {
virtual auto operator()(T const& v, F const& f) -> decltype(f(v.template at<0>())) =0;
};
boost::ptr_array<apply, T::n_elements> appliers_;
template <int I>
struct apply_idx : apply {
virtual auto operator()(T const& v, F const& f)
CHILON_RETURN(f(v.template at<I>()))
};
template <int I, int L>
struct create_applier {
static void exec(decltype(appliers_)& appliers) {
appliers.replace(I, new apply_idx<I>());
create_applier<I + 1, L>::exec(appliers);
}
};
template <int I>
struct create_applier<I, I> {
static void exec(decltype(appliers_)& appliers) {}
};
variant_apply_lookup_table() {
create_applier<0, T::n_elements>::exec(appliers_);
}
};
template <class... T, class F>
struct variant_apply_helper< variant<T...>, F > {
typedef variant<T...> type;
inline static auto exec(type const& v, F const& f) -> decltype(f(v.template at<0>())) {
chilon::singleton<
variant_apply_lookup_table<type, F>
>::instance().appliers_[v.type_index()](v, f);
}
};
}
template <class F, class T>
inline auto variant_apply(T const& v, F const& f)
CHILON_RETURN(detail::variant_apply_helper<T, F>::exec(v, f))
template <class T, class... Y>
inline T& variant_as(variant<Y...>& v) {
return v.template at<T>();
}
template <class T, class... Y>
inline T& variant_as(variant<Y...> const& v) {
return v.template at<T>();
}
template <class T>
inline auto variant_as(T&& v) CHILON_RETURN(std::forward<T>(v))
/**
* call construct_default on a variant
*/
template <class T, class... Y>
inline void construct_default(variant<Y...>& v) {
v.template construct_default<T>();
}
/**
* construct_default on non-variant does nothing
*/
template <class T>
inline void construct_default(T&& v) {}
}
#endif