Skip to content

File underlying.hpp

File List > backends > cxx > include > zmbt > decor > underlying.hpp

Go to the documentation of this file

#ifndef ZMBT_DECORATOR_UNDERLYING_HPP_
#define ZMBT_DECORATOR_UNDERLYING_HPP_

#include <type_traits>

#include "zmbt/core.hpp"
#include "zmbt/reflect.hpp"


namespace zmbt {

namespace decor {



template <class E>
struct underlying
{
    static_assert(std::is_enum<E>::value, "");
    using decorated_type = std::underlying_type_t<E>;


    ~underlying() = default;
    underlying() = default;
    underlying(underlying const&) = default;
    underlying(underlying &&) = default;
    underlying& operator=(underlying const&) = default;
    underlying& operator=(underlying &&) = default;


    underlying(decorated_type const v) : value_{v} {}
    underlying& operator=(decorated_type const v) { value_ = v; return *this; }

    underlying(E const v) : value_{static_cast<decorated_type>(v)} {}
    underlying& operator=(E const v) { value_ = static_cast<decorated_type>(v); return *this; }


    underlying(boost::json::string_view v) : value_{static_cast<decorated_type>(dejsonize<E>(v))} {}
    underlying& operator=(boost::json::string_view v) { value_ = static_cast<decorated_type>(dejsonize<E>(v)); return *this; }


    decorated_type value() const
    {
        return value_;
    }

    std::string stringify() const
    {
        return json_from(E{value()}).as_string().c_str();
    }

    operator decorated_type() const
    {
        return value();
    }

    auto operator!()  const { return !value(); }
    auto operator~()  const { return ~value(); }
    auto operator-()  const { return -value(); }

    template <class T> auto operator==(T const& other) const { return value() == other; }
    template <class T> auto operator!=(T const& other) const { return value() != other; }
    template <class T> auto operator>>(T const& other) const { return value() >> other; }
    template <class T> auto operator<<(T const& other) const { return value() << other; }
    template <class T> auto operator<=(T const& other) const { return value() <= other; }
    template <class T> auto operator>=(T const& other) const { return value() >= other; }
    template <class T> auto operator<(T const& other)  const { return value() <  other; }
    template <class T> auto operator>(T const& other)  const { return value() >  other; }
    template <class T> auto operator+(T const& other)  const { return value() +  other; }
    template <class T> auto operator-(T const& other)  const { return value() -  other; }
    template <class T> auto operator*(T const& other)  const { return value() *  other; }
    template <class T> auto operator/(T const& other)  const { return value() /  other; }
    template <class T> auto operator%(T const& other)  const { return value() %  other; }
    template <class T> auto operator&(T const& other)  const { return value() &  other; }
    template <class T> auto operator|(T const& other)  const { return value() |  other; }
    template <class T> auto operator^(T const& other)  const { return value() ^  other; }
    template <class T> auto operator&&(T const& other) const { return value() && other; }
    template <class T> auto operator||(T const& other) const { return value() || other; }

private:
    decorated_type value_;
};


template <class T>
constexpr zmbt::type_tag<underlying<T>> Underlying;

} // namespace decor

namespace reflect {

template <class T>
struct custom_serialization<decor::underlying<T>> {


    static boost::json::value json_from(decor::underlying<T> const t)
    {
        boost::json::value v;
        v.emplace_string() = t.stringify();
        return v;
    }

    static decor::underlying<T>
    dejsonize(boost::json::value const& v)
    {
        decor::underlying<T> result;
        switch (v.kind()) {
            case boost::json::kind::string:
                result = v.get_string().data();
                break;
            case boost::json::kind::int64:
                result = v.get_int64();
                break;
            case boost::json::kind::uint64:
                result = v.get_uint64();
                break;
            default:
                throw std::invalid_argument("zmbt::decor::underlying<T> conversion failure");
                break;
        }
        return result;
    }

};

} // namespace reflect
} // namespace zmbt

#endif  // ZMBT_DECORATOR_UNDERLYING_HPP_