Skip to content

NamedType: add defaults and make sure state stays valid #193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
50 changes: 43 additions & 7 deletions include/adm/detail/named_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ namespace adm {
/// @brief Implementation details
namespace detail {

/// Get the default value for some NamedType; specialise this to add custom
/// defaults. Note that this is mainly used to make NamedType values
/// default-constructable when the validator doesn't accept the default
/// value of the underlying type.
template <typename NT>
NT getNamedTypeDefault() {
return NT{typename NT::value_type{}};
}

/**
* @brief Named type class
*
Expand All @@ -22,15 +31,38 @@ namespace adm {
typedef T value_type;
typedef Tag tag;

NamedType() : value_() { Validator::validate(get()); }
NamedType() : NamedType(getNamedTypeDefault<NamedType>()) {}

NamedType(const NamedType&) = default;
NamedType(NamedType&&) = default;
NamedType& operator=(const NamedType&) = default;
NamedType& operator=(NamedType&&) = default;

explicit NamedType(T const& value) : value_(value) {
Validator::validate(get());
}
explicit NamedType(T&& value) : value_(std::move(value)) {
Validator::validate(get());
}
T& get() { return value_; }
T const& get() const { return value_; }

NamedType& operator=(const T& value) {
Validator::validate(value);
value_ = value;
return *this;
}
NamedType& operator=(T&& value) {
Validator::validate(value);
value_ = std::move(value);
return *this;
}

T const& get() const& { return value_; }

T get() && {
T tmp = std::move(value_);
*this = getNamedTypeDefault<NamedType>();
return tmp;
}

bool operator==(const NamedType<T, Tag, Validator>& other) const {
return this->get() == other.get();
Expand Down Expand Up @@ -86,7 +118,10 @@ namespace adm {
}

NamedType<T, Tag, Validator>& operator++() {
++value_;
T tmp = value_;
tmp++;
Validator::validate(tmp);
value_ = std::move(tmp);
return *this;
}

Expand All @@ -97,7 +132,10 @@ namespace adm {
}

NamedType<T, Tag, Validator>& operator--() {
--value_;
T tmp = value_;
tmp--;
Validator::validate(tmp);
value_ = std::move(tmp);
return *this;
}

Expand All @@ -107,9 +145,7 @@ namespace adm {
return tmp;
}

T* operator->() { return &value_; }
T const* operator->() const { return &value_; }
T& operator*() { return value_; }
T const& operator*() const { return value_; }

private:
Expand Down
10 changes: 5 additions & 5 deletions include/adm/elements/common_parameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
namespace adm {
namespace detail {
template <>
inline Importance getDefault<Importance>() {
inline Importance getNamedTypeDefault<Importance>() {
return Importance{10};
}

Expand All @@ -23,22 +23,22 @@ namespace adm {
}

template <>
inline Rtime getDefault<Rtime>() {
inline Rtime getNamedTypeDefault<Rtime>() {
return Rtime{std::chrono::nanoseconds{0}};
}

template <>
inline HeadLocked getDefault<HeadLocked>() {
inline HeadLocked getNamedTypeDefault<HeadLocked>() {
return HeadLocked{false};
}

template <>
inline ScreenRef getDefault<ScreenRef>() {
inline ScreenRef getNamedTypeDefault<ScreenRef>() {
return ScreenRef{false};
}

template <>
inline Normalization getDefault<Normalization>() {
inline Normalization getNamedTypeDefault<Normalization>() {
return Normalization{"SN3D"};
}

Expand Down
7 changes: 7 additions & 0 deletions include/adm/elements/frequency.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ namespace adm {
*/
using LowPass = detail::NamedType<float, LowPassTag, detail::MinValidator<0>>;

namespace detail {
template <>
inline FrequencyType getNamedTypeDefault<FrequencyType>() {
return FrequencyType{"lowPass"};
}
} // namespace detail

/// @brief Tag for Frequency class
struct FrequencyTag {};
/**
Expand Down
18 changes: 18 additions & 0 deletions include/adm/elements/gain_interaction_range.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@ namespace adm {
detail::NamedType<std::string, GainInteractionBoundValueTag,
detail::BoundValueValidator>;

namespace detail {
template <>
inline GainInteractionMin getNamedTypeDefault<GainInteractionMin>() {
return GainInteractionMin{Gain::fromLinear(1.0)};
}

template <>
inline GainInteractionMax getNamedTypeDefault<GainInteractionMax>() {
return GainInteractionMax{Gain::fromLinear(1.0)};
}

template <>
inline GainInteractionBoundValue
getNamedTypeDefault<GainInteractionBoundValue>() {
return GainInteractionBoundValue{"min"};
}
} // namespace detail

/// @brief Tag for GainInteractionRange class
struct GainInteractionRangeTag {};
/**
Expand Down
15 changes: 15 additions & 0 deletions include/adm/elements/position_interaction_range.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ namespace adm {
using CoordinateInteractionValue =
detail::NamedType<std::string, CoordinateInteractionValueTag,
detail::CoordinateValueValidator>;

namespace detail {
template <>
inline PositionInteractionBoundValue
getNamedTypeDefault<PositionInteractionBoundValue>() {
return PositionInteractionBoundValue{"min"};
}

template <>
inline CoordinateInteractionValue
getNamedTypeDefault<CoordinateInteractionValue>() {
return CoordinateInteractionValue{"azimuth"};
}
} // namespace detail

/// @brief Tag for PositionInteractionRange class
struct PositionInteractionRangeTag {};
/**
Expand Down
14 changes: 14 additions & 0 deletions include/adm/elements/position_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,18 @@ namespace adm {
using CartesianCoordinateValue =
detail::NamedType<std::string, CartesianCoordinateValueTag,
detail::CartesianCoordinateValueValidator>;

namespace detail {
template <>
inline SphericalCoordinateValue
getNamedTypeDefault<SphericalCoordinateValue>() {
return SphericalCoordinateValue{"azimuth"};
}

template <>
inline CartesianCoordinateValue
getNamedTypeDefault<CartesianCoordinateValue>() {
return CartesianCoordinateValue{"X"};
}
} // namespace detail
} // namespace adm
17 changes: 17 additions & 0 deletions include/adm/elements/screen_edge_lock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ namespace adm {
using VerticalEdge = detail::NamedType<std::string, VerticalEdgeTag,
detail::VerticalEdgeValidator>;

namespace detail {
template <>
inline ScreenEdge getNamedTypeDefault<ScreenEdge>() {
return ScreenEdge{"left"};
}

template <>
inline HorizontalEdge getNamedTypeDefault<HorizontalEdge>() {
return HorizontalEdge{"left"};
}

template <>
inline VerticalEdge getNamedTypeDefault<VerticalEdge>() {
return VerticalEdge{"top"};
}
} // namespace detail

/// @brief Tag for ScreenEdgeLock class
struct ScreenEdgeLockTag {};
/**
Expand Down
7 changes: 7 additions & 0 deletions include/adm/elements/speaker_position.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ namespace adm {
using BoundValue = detail::NamedType<std::string, BoundValueTag,
detail::BoundValueValidator>;

namespace detail {
template <>
inline BoundValue getNamedTypeDefault<BoundValue>() {
return BoundValue{"min"};
}
} // namespace detail

/// @brief Tag for CartesianSpeakerPosition class
struct CartesianSpeakerPositionTag {};
/**
Expand Down
1 change: 1 addition & 0 deletions include/adm/elements/time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ namespace adm {
/// FractionalTime
class Time {
public:
Time() : time(std::chrono::nanoseconds::zero()) {}
// non-explicit, as this should act like a variant
template <typename Rep, typename Period>
// NOLINTNEXTLINE(google-explicit-constructor)
Expand Down
45 changes: 45 additions & 0 deletions tests/named_type_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,51 @@ TEST_CASE("NamedType_range_check") {
NamedIntegerRange goodValue(5);
REQUIRE_THROWS_AS(NamedIntegerRange(12), OutOfRangeError);
REQUIRE_THROWS_AS(NamedIntegerRange(-1), OutOfRangeError);

NamedIntegerRange lower_limit(0);
REQUIRE_THROWS_AS(lower_limit--, OutOfRangeError);
REQUIRE_THROWS_AS(--lower_limit, OutOfRangeError);
REQUIRE(lower_limit == 0);

NamedIntegerRange upper_limit(10);
REQUIRE_THROWS_AS(upper_limit++, OutOfRangeError);
REQUIRE_THROWS_AS(++upper_limit, OutOfRangeError);
REQUIRE(upper_limit == 10);
}

class MoveOnly {
public:
MoveOnly() : value(0) {}
MoveOnly(int value) : value(value) {}

MoveOnly(MoveOnly const &) = delete;
MoveOnly &operator=(MoveOnly const &) = delete;

MoveOnly(MoveOnly &&) = default;
MoveOnly &operator=(MoveOnly &&) = default;

int get() const { return value; }

private:
int value;
};

struct MoveOnlyTag {};
using NamedMoveOnly = adm::detail::NamedType<MoveOnly, MoveOnlyTag>;

TEST_CASE("NamedType_move") {
NamedMoveOnly value{MoveOnly{1}};

MoveOnly moved = std::move(value).get();
REQUIRE(moved.get() == 1);
REQUIRE(value.get().get() == 0);

MoveOnly moved_from_temporary = std::move(NamedMoveOnly{MoveOnly{2}}).get();
REQUIRE(moved_from_temporary.get() == 2);

value = MoveOnly{3};
NamedMoveOnly moved_from_named(std::move(value));
REQUIRE(moved_from_named.get().get() == 3);
}

TEST_CASE("screen_edge_validator") {
Expand Down