diff --git a/common/value.hh b/common/value.hh index fc5f151..7ca759c 100644 --- a/common/value.hh +++ b/common/value.hh @@ -121,20 +121,31 @@ class Value { const ConstTuple tuple() const { return get(); } const DataPtr& data() const { return get(); } - template - I integer() const { - const auto ret = integer(); - if constexpr (std::is_unsigned::value) { - if (ret < 0) { - throw IncompatibleException("integer underflow"); - } - } else { - if (ret != static_cast(static_cast(ret))) { - throw IncompatibleException("integer out of range"); - } - } - return static_cast(ret); + template + N integer() const { + return SafeCast(integer()); } + template + N scalar() const { + return SafeCast(scalar()); + } + template + N integerOrScalar() const { + try { + return SafeCast(integer()); + } catch (nf7::Exception&) { + return SafeCast(scalar()); + } + } + template + N scalarOrInteger() const { + try { + return SafeCast(scalar()); + } catch (nf7::Exception&) { + return SafeCast(integer()); + } + } + const Value& tuple(size_t idx) const { auto& tup = *tuple(); return idx < tup.size()? tup[idx].second: @@ -147,6 +158,13 @@ class Value { return itr < tup.end()? itr->second: throw IncompatibleException("unknown tuple field: "+std::string {name}); } + const Value& tupleOr(auto idx, const Value& v) const noexcept { + try { + return tuple(idx); + } catch (nf7::Exception&) { + return v; + } + } template std::shared_ptr data() const { if (auto ptr = std::dynamic_pointer_cast(data())) return ptr; @@ -187,20 +205,17 @@ class Value { template - const T& get() const - try { - return std::get(value_); - } catch (std::bad_variant_access&) { - throw IncompatibleException( - std::string{"expected "}+typeid(T).name()+" but it's "+typeName()); + const T& get() const { + return const_cast(*this).get(); } template T& get() try { return std::get(value_); } catch (std::bad_variant_access&) { - throw IncompatibleException( - std::string{"expected "}+typeid(T).name()+" but it's "+typeName()); + std::stringstream st; + st << "expected " << typeid(T).name() << " but it's " << typeName(); + throw IncompatibleException(st.str()); } template T getUniq() { @@ -211,6 +226,28 @@ class Value { return std::make_shared(*v); } } + + template + static R SafeCast(N in) { + const auto ret = static_cast(in); + const auto retn = static_cast(ret); + if constexpr (std::is_unsigned::value) { + if (in < 0) { + throw IncompatibleException("integer underflow"); + } + } + if constexpr (std::is_integral::value && std::is_integral::value) { + if (in != retn) { + throw IncompatibleException("integer out of range"); + } + } + if constexpr (std::is_integral::value && std::is_floating_point::value) { + if (std::max(retn, in) - std::min(retn, in) > 1) { + throw IncompatibleException("bad precision while conversion of floating point"); + } + } + return ret; + } }; class Value::Data {