diff --git a/include/adm/detail/named_type.hpp b/include/adm/detail/named_type.hpp index 35ae799b..5f73d1d4 100644 --- a/include/adm/detail/named_type.hpp +++ b/include/adm/detail/named_type.hpp @@ -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 + NT getNamedTypeDefault() { + return NT{typename NT::value_type{}}; + } + /** * @brief Named type class * @@ -22,15 +31,38 @@ namespace adm { typedef T value_type; typedef Tag tag; - NamedType() : value_() { Validator::validate(get()); } + NamedType() : NamedType(getNamedTypeDefault()) {} + + 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(); + return tmp; + } bool operator==(const NamedType& other) const { return this->get() == other.get(); @@ -86,7 +118,10 @@ namespace adm { } NamedType& operator++() { - ++value_; + T tmp = value_; + tmp++; + Validator::validate(tmp); + value_ = std::move(tmp); return *this; } @@ -97,7 +132,10 @@ namespace adm { } NamedType& operator--() { - --value_; + T tmp = value_; + tmp--; + Validator::validate(tmp); + value_ = std::move(tmp); return *this; } @@ -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: diff --git a/include/adm/elements/common_parameters.hpp b/include/adm/elements/common_parameters.hpp index ff8348f6..235253f2 100644 --- a/include/adm/elements/common_parameters.hpp +++ b/include/adm/elements/common_parameters.hpp @@ -13,7 +13,7 @@ namespace adm { namespace detail { template <> - inline Importance getDefault() { + inline Importance getNamedTypeDefault() { return Importance{10}; } @@ -23,22 +23,22 @@ namespace adm { } template <> - inline Rtime getDefault() { + inline Rtime getNamedTypeDefault() { return Rtime{std::chrono::nanoseconds{0}}; } template <> - inline HeadLocked getDefault() { + inline HeadLocked getNamedTypeDefault() { return HeadLocked{false}; } template <> - inline ScreenRef getDefault() { + inline ScreenRef getNamedTypeDefault() { return ScreenRef{false}; } template <> - inline Normalization getDefault() { + inline Normalization getNamedTypeDefault() { return Normalization{"SN3D"}; } diff --git a/include/adm/elements/frequency.hpp b/include/adm/elements/frequency.hpp index 79dd9b79..71d1b190 100644 --- a/include/adm/elements/frequency.hpp +++ b/include/adm/elements/frequency.hpp @@ -35,6 +35,13 @@ namespace adm { */ using LowPass = detail::NamedType>; + namespace detail { + template <> + inline FrequencyType getNamedTypeDefault() { + return FrequencyType{"lowPass"}; + } + } // namespace detail + /// @brief Tag for Frequency class struct FrequencyTag {}; /** diff --git a/include/adm/elements/gain_interaction_range.hpp b/include/adm/elements/gain_interaction_range.hpp index bc6cfb71..d41874a6 100644 --- a/include/adm/elements/gain_interaction_range.hpp +++ b/include/adm/elements/gain_interaction_range.hpp @@ -35,6 +35,24 @@ namespace adm { detail::NamedType; + namespace detail { + template <> + inline GainInteractionMin getNamedTypeDefault() { + return GainInteractionMin{Gain::fromLinear(1.0)}; + } + + template <> + inline GainInteractionMax getNamedTypeDefault() { + return GainInteractionMax{Gain::fromLinear(1.0)}; + } + + template <> + inline GainInteractionBoundValue + getNamedTypeDefault() { + return GainInteractionBoundValue{"min"}; + } + } // namespace detail + /// @brief Tag for GainInteractionRange class struct GainInteractionRangeTag {}; /** diff --git a/include/adm/elements/position_interaction_range.hpp b/include/adm/elements/position_interaction_range.hpp index 889da1fd..135652e1 100644 --- a/include/adm/elements/position_interaction_range.hpp +++ b/include/adm/elements/position_interaction_range.hpp @@ -91,6 +91,21 @@ namespace adm { using CoordinateInteractionValue = detail::NamedType; + + namespace detail { + template <> + inline PositionInteractionBoundValue + getNamedTypeDefault() { + return PositionInteractionBoundValue{"min"}; + } + + template <> + inline CoordinateInteractionValue + getNamedTypeDefault() { + return CoordinateInteractionValue{"azimuth"}; + } + } // namespace detail + /// @brief Tag for PositionInteractionRange class struct PositionInteractionRangeTag {}; /** diff --git a/include/adm/elements/position_types.hpp b/include/adm/elements/position_types.hpp index c4aa1829..99dd9776 100644 --- a/include/adm/elements/position_types.hpp +++ b/include/adm/elements/position_types.hpp @@ -174,4 +174,18 @@ namespace adm { using CartesianCoordinateValue = detail::NamedType; + + namespace detail { + template <> + inline SphericalCoordinateValue + getNamedTypeDefault() { + return SphericalCoordinateValue{"azimuth"}; + } + + template <> + inline CartesianCoordinateValue + getNamedTypeDefault() { + return CartesianCoordinateValue{"X"}; + } + } // namespace detail } // namespace adm diff --git a/include/adm/elements/screen_edge_lock.hpp b/include/adm/elements/screen_edge_lock.hpp index cf346eea..fa73976d 100644 --- a/include/adm/elements/screen_edge_lock.hpp +++ b/include/adm/elements/screen_edge_lock.hpp @@ -36,6 +36,23 @@ namespace adm { using VerticalEdge = detail::NamedType; + namespace detail { + template <> + inline ScreenEdge getNamedTypeDefault() { + return ScreenEdge{"left"}; + } + + template <> + inline HorizontalEdge getNamedTypeDefault() { + return HorizontalEdge{"left"}; + } + + template <> + inline VerticalEdge getNamedTypeDefault() { + return VerticalEdge{"top"}; + } + } // namespace detail + /// @brief Tag for ScreenEdgeLock class struct ScreenEdgeLockTag {}; /** diff --git a/include/adm/elements/speaker_position.hpp b/include/adm/elements/speaker_position.hpp index acf32e7c..41166ac0 100644 --- a/include/adm/elements/speaker_position.hpp +++ b/include/adm/elements/speaker_position.hpp @@ -21,6 +21,13 @@ namespace adm { using BoundValue = detail::NamedType; + namespace detail { + template <> + inline BoundValue getNamedTypeDefault() { + return BoundValue{"min"}; + } + } // namespace detail + /// @brief Tag for CartesianSpeakerPosition class struct CartesianSpeakerPositionTag {}; /** diff --git a/include/adm/elements/time.hpp b/include/adm/elements/time.hpp index 65116ecb..686f30e8 100644 --- a/include/adm/elements/time.hpp +++ b/include/adm/elements/time.hpp @@ -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 // NOLINTNEXTLINE(google-explicit-constructor) diff --git a/tests/named_type_tests.cpp b/tests/named_type_tests.cpp index 7a14d39d..5174f6ec 100644 --- a/tests/named_type_tests.cpp +++ b/tests/named_type_tests.cpp @@ -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; + +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") {