Files
rippled/include/xrpl/protocol/STObject.h
Ed Hennis 3a45fc3364 fix: Use ".value()" instead of "->" when with STObject::Proxy objects
- Turns out that "Proxy::operator->" is not a safe substitute for
  "Proxy::value()." if the field is not required. The implementation
  is different such that "operator->" will return a null ptr if the
  field is not present. This includes default fields with a value of
  zero!
2025-11-12 18:41:54 -05:00

1270 lines
31 KiB
C++

#ifndef XRPL_PROTOCOL_STOBJECT_H_INCLUDED
#define XRPL_PROTOCOL_STOBJECT_H_INCLUDED
#include <xrpl/basics/CountedObject.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STCurrency.h>
#include <xrpl/protocol/STIssue.h>
#include <xrpl/protocol/STPathSet.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/detail/STVar.h>
#include <boost/iterator/transform_iterator.hpp>
#include <optional>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace ripple {
class STArray;
inline void
throwFieldNotFound(SField const& field)
{
Throw<std::runtime_error>("Field not found: " + field.getName());
}
class STObject : public STBase, public CountedObject<STObject>
{
// Proxy value for a STBase derived class
template <class T>
class Proxy;
template <class T>
class ValueProxy;
template <class T>
class OptionalProxy;
struct Transform
{
explicit Transform() = default;
using argument_type = detail::STVar;
using result_type = STBase;
STBase const&
operator()(detail::STVar const& e) const;
};
using list_type = std::vector<detail::STVar>;
list_type v_;
SOTemplate const* mType;
public:
using iterator = boost::
transform_iterator<Transform, STObject::list_type::const_iterator>;
virtual ~STObject() = default;
STObject(STObject const&) = default;
template <typename F>
STObject(SOTemplate const& type, SField const& name, F&& f)
: STObject(type, name)
{
f(*this);
}
STObject&
operator=(STObject const&) = default;
STObject(STObject&&);
STObject&
operator=(STObject&& other);
STObject(SOTemplate const& type, SField const& name);
STObject(SOTemplate const& type, SerialIter& sit, SField const& name);
STObject(SerialIter& sit, SField const& name, int depth = 0);
STObject(SerialIter&& sit, SField const& name);
explicit STObject(SField const& name);
static STObject
makeInnerObject(SField const& name);
iterator
begin() const;
iterator
end() const;
bool
empty() const;
void
reserve(std::size_t n);
void
applyTemplate(SOTemplate const& type);
void
applyTemplateFromSField(SField const&);
bool
isFree() const;
void
set(SOTemplate const&);
bool
set(SerialIter& u, int depth = 0);
SerializedTypeID
getSType() const override;
bool
isEquivalent(STBase const& t) const override;
bool
isDefault() const override;
void
add(Serializer& s) const override;
std::string
getFullText() const override;
std::string
getText() const override;
// TODO(tom): options should be an enum.
Json::Value getJson(JsonOptions = JsonOptions::none) const override;
void
addWithoutSigningFields(Serializer& s) const;
Serializer
getSerializer() const;
template <class... Args>
std::size_t
emplace_back(Args&&... args);
int
getCount() const;
bool setFlag(std::uint32_t);
bool clearFlag(std::uint32_t);
bool isFlag(std::uint32_t) const;
std::uint32_t
getFlags() const;
uint256
getHash(HashPrefix prefix) const;
uint256
getSigningHash(HashPrefix prefix) const;
STBase const&
peekAtIndex(int offset) const;
STBase&
getIndex(int offset);
STBase const*
peekAtPIndex(int offset) const;
STBase*
getPIndex(int offset);
int
getFieldIndex(SField const& field) const;
SField const&
getFieldSType(int index) const;
STBase const&
peekAtField(SField const& field) const;
STBase&
getField(SField const& field);
STBase const*
peekAtPField(SField const& field) const;
STBase*
getPField(SField const& field, bool createOkay = false);
// these throw if the field type doesn't match, or return default values
// if the field is optional but not present
unsigned char
getFieldU8(SField const& field) const;
std::uint16_t
getFieldU16(SField const& field) const;
std::uint32_t
getFieldU32(SField const& field) const;
std::uint64_t
getFieldU64(SField const& field) const;
uint128
getFieldH128(SField const& field) const;
uint160
getFieldH160(SField const& field) const;
uint192
getFieldH192(SField const& field) const;
uint256
getFieldH256(SField const& field) const;
std::int32_t
getFieldI32(SField const& field) const;
AccountID
getAccountID(SField const& field) const;
Blob
getFieldVL(SField const& field) const;
STAmount const&
getFieldAmount(SField const& field) const;
STPathSet const&
getFieldPathSet(SField const& field) const;
STVector256 const&
getFieldV256(SField const& field) const;
// If not found, returns an object constructed with the given field
STObject
getFieldObject(SField const& field) const;
STArray const&
getFieldArray(SField const& field) const;
STCurrency const&
getFieldCurrency(SField const& field) const;
STNumber const&
getFieldNumber(SField const& field) const;
/** Get the value of a field.
@param A TypedField built from an SField value representing the desired
object field. In typical use, the TypedField will be implicitly
constructed.
@return The value of the specified field.
@throws STObject::FieldErr if the field is not present.
*/
template <class T>
typename T::value_type
operator[](TypedField<T> const& f) const;
/** Get the value of a field as a std::optional
@param An OptionaledField built from an SField value representing the
desired object field. In typical use, the OptionaledField will be
constructed by using the ~ operator on an SField.
@return std::nullopt if the field is not present, else the value of
the specified field.
*/
template <class T>
std::optional<std::decay_t<typename T::value_type>>
operator[](OptionaledField<T> const& of) const;
/** Get a modifiable field value.
@param A TypedField built from an SField value representing the desired
object field. In typical use, the TypedField will be implicitly
constructed.
@return A modifiable reference to the value of the specified field.
@throws STObject::FieldErr if the field is not present.
*/
template <class T>
ValueProxy<T>
operator[](TypedField<T> const& f);
/** Return a modifiable field value as std::optional
@param An OptionaledField built from an SField value representing the
desired object field. In typical use, the OptionaledField will be
constructed by using the ~ operator on an SField.
@return Transparent proxy object to an `optional` holding a modifiable
reference to the value of the specified field. Returns
std::nullopt if the field is not present.
*/
template <class T>
OptionalProxy<T>
operator[](OptionaledField<T> const& of);
/** Get the value of a field.
@param A TypedField built from an SField value representing the desired
object field. In typical use, the TypedField will be implicitly
constructed.
@return The value of the specified field.
@throws STObject::FieldErr if the field is not present.
*/
template <class T>
typename T::value_type
at(TypedField<T> const& f) const;
/** Get the value of a field as std::optional
@param An OptionaledField built from an SField value representing the
desired object field. In typical use, the OptionaledField will be
constructed by using the ~ operator on an SField.
@return std::nullopt if the field is not present, else the value of
the specified field.
*/
template <class T>
std::optional<std::decay_t<typename T::value_type>>
at(OptionaledField<T> const& of) const;
/** Get a modifiable field value.
@param A TypedField built from an SField value representing the desired
object field. In typical use, the TypedField will be implicitly
constructed.
@return A modifiable reference to the value of the specified field.
@throws STObject::FieldErr if the field is not present.
*/
template <class T>
ValueProxy<T>
at(TypedField<T> const& f);
/** Return a modifiable field value as std::optional
@param An OptionaledField built from an SField value representing the
desired object field. In typical use, the OptionaledField will be
constructed by using the ~ operator on an SField.
@return Transparent proxy object to an `optional` holding a modifiable
reference to the value of the specified field. Returns
std::nullopt if the field is not present.
*/
template <class T>
OptionalProxy<T>
at(OptionaledField<T> const& of);
/** Set a field.
if the field already exists, it is replaced.
*/
void
set(std::unique_ptr<STBase> v);
void
set(STBase&& v);
void
setFieldU8(SField const& field, unsigned char);
void
setFieldU16(SField const& field, std::uint16_t);
void
setFieldU32(SField const& field, std::uint32_t);
void
setFieldU64(SField const& field, std::uint64_t);
void
setFieldH128(SField const& field, uint128 const&);
void
setFieldH256(SField const& field, uint256 const&);
void
setFieldI32(SField const& field, std::int32_t);
void
setFieldVL(SField const& field, Blob const&);
void
setFieldVL(SField const& field, Slice const&);
void
setAccountID(SField const& field, AccountID const&);
void
setFieldAmount(SField const& field, STAmount const&);
void
setFieldIssue(SField const& field, STIssue const&);
void
setFieldCurrency(SField const& field, STCurrency const&);
void
setFieldNumber(SField const& field, STNumber const&);
void
setFieldPathSet(SField const& field, STPathSet const&);
void
setFieldV256(SField const& field, STVector256 const& v);
void
setFieldArray(SField const& field, STArray const& v);
void
setFieldObject(SField const& field, STObject const& v);
template <class Tag>
void
setFieldH160(SField const& field, base_uint<160, Tag> const& v);
STObject&
peekFieldObject(SField const& field);
STArray&
peekFieldArray(SField const& field);
bool
isFieldPresent(SField const& field) const;
STBase*
makeFieldPresent(SField const& field);
void
makeFieldAbsent(SField const& field);
bool
delField(SField const& field);
void
delField(int index);
bool
hasMatchingEntry(STBase const&);
bool
operator==(STObject const& o) const;
bool
operator!=(STObject const& o) const;
class FieldErr;
private:
enum WhichFields : bool {
// These values are carefully chosen to do the right thing if passed
// to SField::shouldInclude (bool)
omitSigningFields = false,
withAllFields = true
};
void
add(Serializer& s, WhichFields whichFields) const;
// Sort the entries in an STObject into the order that they will be
// serialized. Note: they are not sorted into pointer value order, they
// are sorted by SField::fieldCode.
static std::vector<STBase const*>
getSortedFields(STObject const& objToSort, WhichFields whichFields);
// Implementation for getting (most) fields that return by value.
//
// The remove_cv and remove_reference are necessitated by the STBitString
// types. Their value() returns by const ref. We return those types
// by value.
template <
typename T,
typename V = typename std::remove_cv<typename std::remove_reference<
decltype(std::declval<T>().value())>::type>::type>
V
getFieldByValue(SField const& field) const;
// Implementations for getting (most) fields that return by const reference.
//
// If an absent optional field is deserialized we don't have anything
// obvious to return. So we insist on having the call provide an
// 'empty' value we return in that circumstance.
template <typename T, typename V>
V const&
getFieldByConstRef(SField const& field, V const& empty) const;
// Implementation for setting most fields with a setValue() method.
template <typename T, typename V>
void
setFieldUsingSetValue(SField const& field, V value);
// Implementation for setting fields using assignment
template <typename T>
void
setFieldUsingAssignment(SField const& field, T const& value);
// Implementation for peeking STObjects and STArrays
template <typename T>
T&
peekField(SField const& field);
STBase*
copy(std::size_t n, void* buf) const override;
STBase*
move(std::size_t n, void* buf) override;
friend class detail::STVar;
};
//------------------------------------------------------------------------------
template <class T>
class STObject::Proxy
{
public:
using value_type = typename T::value_type;
value_type
value() const;
value_type
operator*() const;
/// Do not use operator->() unless the field is required, or you've checked
/// that it's set.
T const*
operator->() const;
protected:
STObject* st_;
SOEStyle style_;
TypedField<T> const* f_;
Proxy(Proxy const&) = default;
Proxy(STObject* st, TypedField<T> const* f);
T const*
find() const;
template <class U>
void
assign(U&& u);
};
// Constraint += and -= ValueProxy operators
// to value types that support arithmetic operations
template <typename U>
concept IsArithmeticNumber = std::is_arithmetic_v<U> ||
std::is_same_v<U, Number> || std::is_same_v<U, STAmount>;
template <
typename U,
typename Value = typename U::value_type,
typename Unit = typename U::unit_type>
concept IsArithmeticValueUnit =
std::is_same_v<U, unit::ValueUnit<Unit, Value>> &&
IsArithmeticNumber<Value> && std::is_class_v<Unit>;
template <typename U, typename Value = typename U::value_type>
concept IsArithmeticST = !IsArithmeticValueUnit<U> && IsArithmeticNumber<Value>;
template <typename U>
concept IsArithmetic =
IsArithmeticNumber<U> || IsArithmeticST<U> || IsArithmeticValueUnit<U>;
template <class T, class U>
concept Addable = requires(T t, U u) { t = t + u; };
template <typename T, typename U>
concept IsArithmeticCompatible =
IsArithmetic<typename T::value_type> && Addable<typename T::value_type, U>;
template <class T>
class STObject::ValueProxy : public Proxy<T>
{
private:
using value_type = typename T::value_type;
public:
ValueProxy(ValueProxy const&) = default;
ValueProxy&
operator=(ValueProxy const&) = delete;
template <class U>
std::enable_if_t<std::is_assignable_v<T, U>, ValueProxy&>
operator=(U&& u);
// Convenience operators for value types supporting
// arithmetic operations
template <IsArithmetic U>
requires IsArithmeticCompatible<T, U>
ValueProxy&
operator+=(U const& u);
template <IsArithmetic U>
requires IsArithmeticCompatible<T, U>
ValueProxy&
operator-=(U const& u);
operator value_type() const;
template <typename U>
friend bool
operator==(U const& lhs, STObject::ValueProxy<T> const& rhs)
{
return rhs.value() == lhs;
}
private:
friend class STObject;
ValueProxy(STObject* st, TypedField<T> const* f);
};
template <class T>
class STObject::OptionalProxy : public Proxy<T>
{
private:
using value_type = typename T::value_type;
using optional_type = std::optional<typename std::decay<value_type>::type>;
public:
OptionalProxy(OptionalProxy const&) = default;
OptionalProxy&
operator=(OptionalProxy const&) = delete;
/** Returns `true` if the field is set.
Fields with soeDEFAULT and set to the
default value will return `true`
*/
explicit
operator bool() const noexcept;
operator optional_type() const;
/** Explicit conversion to std::optional */
optional_type
operator~() const;
friend bool
operator==(OptionalProxy const& lhs, std::nullopt_t) noexcept
{
return !lhs.engaged();
}
friend bool
operator==(std::nullopt_t, OptionalProxy const& rhs) noexcept
{
return rhs == std::nullopt;
}
friend bool
operator==(OptionalProxy const& lhs, optional_type const& rhs) noexcept
{
if (!lhs.engaged())
return !rhs;
if (!rhs)
return false;
return *lhs == *rhs;
}
friend bool
operator==(optional_type const& lhs, OptionalProxy const& rhs) noexcept
{
return rhs == lhs;
}
friend bool
operator==(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
{
if (lhs.engaged() != rhs.engaged())
return false;
return !lhs.engaged() || *lhs == *rhs;
}
friend bool
operator!=(OptionalProxy const& lhs, std::nullopt_t) noexcept
{
return !(lhs == std::nullopt);
}
friend bool
operator!=(std::nullopt_t, OptionalProxy const& rhs) noexcept
{
return !(rhs == std::nullopt);
}
friend bool
operator!=(OptionalProxy const& lhs, optional_type const& rhs) noexcept
{
return !(lhs == rhs);
}
friend bool
operator!=(optional_type const& lhs, OptionalProxy const& rhs) noexcept
{
return !(lhs == rhs);
}
friend bool
operator!=(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
{
return !(lhs == rhs);
}
// Emulate std::optional::value_or
value_type
value_or(value_type val) const;
OptionalProxy&
operator=(std::nullopt_t const&);
OptionalProxy&
operator=(optional_type&& v);
OptionalProxy&
operator=(optional_type const& v);
template <class U>
std::enable_if_t<std::is_assignable_v<T, U>, OptionalProxy&>
operator=(U&& u);
private:
friend class STObject;
OptionalProxy(STObject* st, TypedField<T> const* f);
bool
engaged() const noexcept;
void
disengage();
optional_type
optional_value() const;
};
class STObject::FieldErr : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
template <class T>
STObject::Proxy<T>::Proxy(STObject* st, TypedField<T> const* f) : st_(st), f_(f)
{
if (st_->mType)
{
// STObject has associated template
if (!st_->peekAtPField(*f_))
Throw<STObject::FieldErr>(
"Template field error '" + this->f_->getName() + "'");
style_ = st_->mType->style(*f_);
}
else
{
style_ = soeINVALID;
}
}
template <class T>
auto
STObject::Proxy<T>::value() const -> value_type
{
auto const t = find();
if (t)
return t->value();
if (style_ == soeINVALID)
{
Throw<STObject::FieldErr>("Value requested from invalid STObject.");
}
if (style_ != soeDEFAULT)
{
Throw<STObject::FieldErr>(
"Missing field '" + this->f_->getName() + "'");
}
return value_type{};
}
template <class T>
auto
STObject::Proxy<T>::operator*() const -> value_type
{
return this->value();
}
/// Do not use operator->() unless the field is required, or you've checked that
/// it's set.
template <class T>
T const*
STObject::Proxy<T>::operator->() const
{
return this->find();
}
template <class T>
inline T const*
STObject::Proxy<T>::find() const
{
return dynamic_cast<T const*>(st_->peekAtPField(*f_));
}
template <class T>
template <class U>
void
STObject::Proxy<T>::assign(U&& u)
{
if (style_ == soeDEFAULT && u == value_type{})
{
st_->makeFieldAbsent(*f_);
return;
}
T* t;
if (style_ == soeINVALID)
t = dynamic_cast<T*>(st_->getPField(*f_, true));
else
t = dynamic_cast<T*>(st_->makeFieldPresent(*f_));
XRPL_ASSERT(t, "ripple::STObject::Proxy::assign : type cast succeeded");
*t = std::forward<U>(u);
}
//------------------------------------------------------------------------------
template <class T>
template <class U>
std::enable_if_t<std::is_assignable_v<T, U>, STObject::ValueProxy<T>&>
STObject::ValueProxy<T>::operator=(U&& u)
{
this->assign(std::forward<U>(u));
return *this;
}
template <typename T>
template <IsArithmetic U>
requires IsArithmeticCompatible<T, U>
STObject::ValueProxy<T>&
STObject::ValueProxy<T>::operator+=(U const& u)
{
this->assign(this->value() + u);
return *this;
}
template <class T>
template <IsArithmetic U>
requires IsArithmeticCompatible<T, U>
STObject::ValueProxy<T>&
STObject::ValueProxy<T>::operator-=(U const& u)
{
this->assign(this->value() - u);
return *this;
}
template <class T>
STObject::ValueProxy<T>::operator value_type() const
{
return this->value();
}
template <class T>
STObject::ValueProxy<T>::ValueProxy(STObject* st, TypedField<T> const* f)
: Proxy<T>(st, f)
{
}
//------------------------------------------------------------------------------
template <class T>
STObject::OptionalProxy<T>::operator bool() const noexcept
{
return engaged();
}
template <class T>
STObject::OptionalProxy<T>::operator typename STObject::OptionalProxy<
T>::optional_type() const
{
return optional_value();
}
template <class T>
typename STObject::OptionalProxy<T>::optional_type
STObject::OptionalProxy<T>::operator~() const
{
return optional_value();
}
template <class T>
auto
STObject::OptionalProxy<T>::operator=(std::nullopt_t const&) -> OptionalProxy&
{
disengage();
return *this;
}
template <class T>
auto
STObject::OptionalProxy<T>::operator=(optional_type&& v) -> OptionalProxy&
{
if (v)
this->assign(std::move(*v));
else
disengage();
return *this;
}
template <class T>
auto
STObject::OptionalProxy<T>::operator=(optional_type const& v) -> OptionalProxy&
{
if (v)
this->assign(*v);
else
disengage();
return *this;
}
template <class T>
template <class U>
std::enable_if_t<std::is_assignable_v<T, U>, STObject::OptionalProxy<T>&>
STObject::OptionalProxy<T>::operator=(U&& u)
{
this->assign(std::forward<U>(u));
return *this;
}
template <class T>
STObject::OptionalProxy<T>::OptionalProxy(STObject* st, TypedField<T> const* f)
: Proxy<T>(st, f)
{
}
template <class T>
bool
STObject::OptionalProxy<T>::engaged() const noexcept
{
return this->style_ == soeDEFAULT || this->find() != nullptr;
}
template <class T>
void
STObject::OptionalProxy<T>::disengage()
{
if (this->style_ == soeREQUIRED || this->style_ == soeDEFAULT)
Throw<STObject::FieldErr>(
"Template field error '" + this->f_->getName() + "'");
if (this->style_ == soeINVALID)
this->st_->delField(*this->f_);
else
this->st_->makeFieldAbsent(*this->f_);
}
template <class T>
auto
STObject::OptionalProxy<T>::optional_value() const -> optional_type
{
if (!engaged())
return std::nullopt;
return this->value();
}
template <class T>
typename STObject::OptionalProxy<T>::value_type
STObject::OptionalProxy<T>::value_or(value_type val) const
{
return engaged() ? this->value() : val;
}
//------------------------------------------------------------------------------
inline STBase const&
STObject::Transform::operator()(detail::STVar const& e) const
{
return e.get();
}
//------------------------------------------------------------------------------
inline STObject::STObject(SerialIter&& sit, SField const& name)
: STObject(sit, name)
{
}
inline STObject::iterator
STObject::begin() const
{
return iterator(v_.begin());
}
inline STObject::iterator
STObject::end() const
{
return iterator(v_.end());
}
inline bool
STObject::empty() const
{
return v_.empty();
}
inline void
STObject::reserve(std::size_t n)
{
v_.reserve(n);
}
inline bool
STObject::isFree() const
{
return mType == nullptr;
}
inline void
STObject::addWithoutSigningFields(Serializer& s) const
{
add(s, omitSigningFields);
}
// VFALCO NOTE does this return an expensive copy of an object with a
// dynamic buffer?
// VFALCO TODO Remove this function and fix the few callers.
inline Serializer
STObject::getSerializer() const
{
Serializer s;
add(s, withAllFields);
return s;
}
template <class... Args>
inline std::size_t
STObject::emplace_back(Args&&... args)
{
v_.emplace_back(std::forward<Args>(args)...);
return v_.size() - 1;
}
inline int
STObject::getCount() const
{
return v_.size();
}
inline STBase const&
STObject::peekAtIndex(int offset) const
{
return v_[offset].get();
}
inline STBase&
STObject::getIndex(int offset)
{
return v_[offset].get();
}
inline STBase const*
STObject::peekAtPIndex(int offset) const
{
return &v_[offset].get();
}
inline STBase*
STObject::getPIndex(int offset)
{
return &v_[offset].get();
}
template <class T>
typename T::value_type
STObject::operator[](TypedField<T> const& f) const
{
return at(f);
}
template <class T>
std::optional<std::decay_t<typename T::value_type>>
STObject::operator[](OptionaledField<T> const& of) const
{
return at(of);
}
template <class T>
inline auto
STObject::operator[](TypedField<T> const& f) -> ValueProxy<T>
{
return at(f);
}
template <class T>
inline auto
STObject::operator[](OptionaledField<T> const& of) -> OptionalProxy<T>
{
return at(of);
}
template <class T>
typename T::value_type
STObject::at(TypedField<T> const& f) const
{
auto const b = peekAtPField(f);
if (!b)
// This is a free object (no constraints)
// with no template
Throw<STObject::FieldErr>("Missing field: " + f.getName());
if (auto const u = dynamic_cast<T const*>(b))
return u->value();
XRPL_ASSERT(
mType,
"ripple::STObject::at(TypedField auto) : field template non-null");
XRPL_ASSERT(
b->getSType() == STI_NOTPRESENT,
"ripple::STObject::at(TypedField auto) : type not present");
if (mType->style(f) == soeOPTIONAL)
Throw<STObject::FieldErr>("Missing optional field: " + f.getName());
XRPL_ASSERT(
mType->style(f) == soeDEFAULT,
"ripple::STObject::at(TypedField auto) : template style is default");
// Used to help handle the case where value_type is a const reference,
// otherwise we would return the address of a temporary.
static std::decay_t<typename T::value_type> const dv{};
return dv;
}
template <class T>
std::optional<std::decay_t<typename T::value_type>>
STObject::at(OptionaledField<T> const& of) const
{
auto const b = peekAtPField(*of.f);
if (!b)
return std::nullopt;
auto const u = dynamic_cast<T const*>(b);
if (!u)
{
XRPL_ASSERT(
mType,
"ripple::STObject::at(OptionaledField auto) : field template "
"non-null");
XRPL_ASSERT(
b->getSType() == STI_NOTPRESENT,
"ripple::STObject::at(OptionaledField auto) : type not present");
if (mType->style(*of.f) == soeOPTIONAL)
return std::nullopt;
XRPL_ASSERT(
mType->style(*of.f) == soeDEFAULT,
"ripple::STObject::at(OptionaledField auto) : template style is "
"default");
return typename T::value_type{};
}
return u->value();
}
template <class T>
inline auto
STObject::at(TypedField<T> const& f) -> ValueProxy<T>
{
return ValueProxy<T>(this, &f);
}
template <class T>
inline auto
STObject::at(OptionaledField<T> const& of) -> OptionalProxy<T>
{
return OptionalProxy<T>(this, of.f);
}
template <class Tag>
void
STObject::setFieldH160(SField const& field, base_uint<160, Tag> const& v)
{
STBase* rf = getPField(field, true);
if (!rf)
throwFieldNotFound(field);
if (rf->getSType() == STI_NOTPRESENT)
rf = makeFieldPresent(field);
using Bits = STBitString<160>;
if (auto cf = dynamic_cast<Bits*>(rf))
cf->setValue(v);
else
Throw<std::runtime_error>("Wrong field type");
}
inline bool
STObject::operator!=(STObject const& o) const
{
return !(*this == o);
}
template <typename T, typename V>
V
STObject::getFieldByValue(SField const& field) const
{
STBase const* rf = peekAtPField(field);
if (!rf)
throwFieldNotFound(field);
SerializedTypeID id = rf->getSType();
if (id == STI_NOTPRESENT)
return V(); // optional field not present
T const* cf = dynamic_cast<T const*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
return cf->value();
}
// Implementations for getting (most) fields that return by const reference.
//
// If an absent optional field is deserialized we don't have anything
// obvious to return. So we insist on having the call provide an
// 'empty' value we return in that circumstance.
template <typename T, typename V>
V const&
STObject::getFieldByConstRef(SField const& field, V const& empty) const
{
STBase const* rf = peekAtPField(field);
if (!rf)
throwFieldNotFound(field);
SerializedTypeID id = rf->getSType();
if (id == STI_NOTPRESENT)
return empty; // optional field not present
T const* cf = dynamic_cast<T const*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
return *cf;
}
// Implementation for setting most fields with a setValue() method.
template <typename T, typename V>
void
STObject::setFieldUsingSetValue(SField const& field, V value)
{
static_assert(!std::is_lvalue_reference<V>::value, "");
STBase* rf = getPField(field, true);
if (!rf)
throwFieldNotFound(field);
if (rf->getSType() == STI_NOTPRESENT)
rf = makeFieldPresent(field);
T* cf = dynamic_cast<T*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
cf->setValue(std::move(value));
}
// Implementation for setting fields using assignment
template <typename T>
void
STObject::setFieldUsingAssignment(SField const& field, T const& value)
{
STBase* rf = getPField(field, true);
if (!rf)
throwFieldNotFound(field);
if (rf->getSType() == STI_NOTPRESENT)
rf = makeFieldPresent(field);
T* cf = dynamic_cast<T*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
(*cf) = value;
}
// Implementation for peeking STObjects and STArrays
template <typename T>
T&
STObject::peekField(SField const& field)
{
STBase* rf = getPField(field, true);
if (!rf)
throwFieldNotFound(field);
if (rf->getSType() == STI_NOTPRESENT)
rf = makeFieldPresent(field);
T* cf = dynamic_cast<T*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
return *cf;
}
} // namespace ripple
#endif