From 7a97ff991710694f1e77a98ce4e2f7d437b4729b Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Thu, 13 Dec 2018 17:18:00 -0500 Subject: [PATCH] Add optional length check when converting AnyValue to vector --- include/cantera/base/AnyMap.h | 19 +++++++++------- include/cantera/base/AnyMap.inl.h | 27 +++++++++++++++++++---- src/base/AnyMap.cpp | 36 ++++++++++++++++++++----------- src/kinetics/Reaction.cpp | 4 ++-- test/general/test_containers.cpp | 12 +++++++++++ 5 files changed, 72 insertions(+), 26 deletions(-) diff --git a/include/cantera/base/AnyMap.h b/include/cantera/base/AnyMap.h index 0decf3d04..7cf2fbe4f 100644 --- a/include/cantera/base/AnyMap.h +++ b/include/cantera/base/AnyMap.h @@ -86,9 +86,9 @@ public: template AnyValue& operator=(const std::vector& value); template - const std::vector& asVector() const; + const std::vector& asVector(size_t nMin=npos, size_t nMax=npos) const; template - std::vector& asVector(); + std::vector& asVector(size_t nMin=npos, size_t nMax=npos); AnyValue& operator=(const AnyMap& value); AnyValue& operator=(AnyMap&& value); @@ -105,6 +105,9 @@ public: private: std::string demangle(const std::type_info& type) const; + template + void checkSize(const std::vector& v, size_t nMin, size_t nMax) const; + std::string m_key; std::unique_ptr m_value; static std::map s_typenames; @@ -112,24 +115,24 @@ private: // Implicit conversion to vector template<> -const std::vector& AnyValue::asVector() const; +const std::vector& AnyValue::asVector(size_t nMin, size_t nMax) const; template<> -std::vector& AnyValue::asVector(); +std::vector& AnyValue::asVector(size_t nMin, size_t nMax); // Implicit conversion of long int to double if accessed as a vector template<> -const std::vector& AnyValue::asVector() const; +const std::vector& AnyValue::asVector(size_t nMin, size_t nMax) const; template<> -std::vector& AnyValue::asVector(); +std::vector& AnyValue::asVector(size_t nMin, size_t nMax); // Implicit conversion of long int to double if accessed as a vector> template<> -const std::vector& AnyValue::asVector() const; +const std::vector& AnyValue::asVector(size_t nMin, size_t nMax) const; template<> -std::vector& AnyValue::asVector(); +std::vector& AnyValue::asVector(size_t nMin, size_t nMax); //! A map of string keys to values whose type can vary at runtime diff --git a/include/cantera/base/AnyMap.inl.h b/include/cantera/base/AnyMap.inl.h index 907133483..dd9c09aac 100644 --- a/include/cantera/base/AnyMap.inl.h +++ b/include/cantera/base/AnyMap.inl.h @@ -65,13 +65,17 @@ AnyValue &AnyValue::operator=(const std::vector &value) { } template -const std::vector &AnyValue::asVector() const { - return as>(); +const std::vector &AnyValue::asVector(size_t nMin, size_t nMax) const { + const auto& v = as>(); + checkSize(v, nMin, nMax); + return v; } template -std::vector &AnyValue::asVector() { - return as>(); +std::vector &AnyValue::asVector(size_t nMin, size_t nMax) { + auto& v = as>(); + checkSize(v, nMin, nMax); + return v; } template @@ -120,5 +124,20 @@ std::map AnyValue::asMap() const return dest; } +template +void AnyValue::checkSize(const std::vector& v, size_t nMin, size_t nMax) const +{ + if (nMin != npos && nMax == npos && v.size() != nMin) { + throw CanteraError("AnyValue::checkSize", "Expected array '{}' " + "to have length {}, but found an array of length {}.", + m_key, nMin, v.size()); + } else if (nMin != npos && nMax != npos + && (v.size() < nMin || v.size() > nMax)) { + throw CanteraError("AnyValue::checkSize", + "Expected array '{}' to have from {} to {} elements, but found an " + " array of length {}.", m_key, nMin, nMax, v.size()); + } +} + } #endif diff --git a/src/base/AnyMap.cpp b/src/base/AnyMap.cpp index 1a01b0acf..45b13319d 100644 --- a/src/base/AnyMap.cpp +++ b/src/base/AnyMap.cpp @@ -377,7 +377,7 @@ std::string AnyValue::demangle(const std::type_info& type) const // Explicit template specializations to allow certain conversions template<> -const std::vector& AnyValue::asVector() const +const std::vector& AnyValue::asVector(size_t nMin, size_t nMax) const { if (!is>()) { std::vector v; @@ -400,18 +400,22 @@ const std::vector& AnyValue::asVector() const // If none of these special cases match, the value won't be replaced, // and an exception will be thrown. } - return as>(); + const auto& vv = as>(); + checkSize(vv, nMin, nMax); + return vv; } template<> -std::vector& AnyValue::asVector() +std::vector& AnyValue::asVector(size_t nMin, size_t nMax) { - return const_cast&>( + auto& v = const_cast&>( const_cast(this)->asVector()); + checkSize(v, nMin, nMax); + return v; } template<> -const std::vector& AnyValue::asVector() const +const std::vector& AnyValue::asVector(size_t nMin, size_t nMax) const { if (is>()) { std::vector v; @@ -420,11 +424,13 @@ const std::vector& AnyValue::asVector() const } *m_value = v; } - return as>(); + const auto& vv = as>(); + checkSize(vv, nMin, nMax); + return vv; } template<> -std::vector& AnyValue::asVector() +std::vector& AnyValue::asVector(size_t nMin, size_t nMax) { if (is>()) { std::vector v; @@ -433,11 +439,13 @@ std::vector& AnyValue::asVector() } *m_value = v; } - return as>(); + auto& vv = as>(); + checkSize(vv, nMin, nMax); + return vv; } template<> -const std::vector& AnyValue::asVector() const +const std::vector& AnyValue::asVector(size_t nMin, size_t nMax) const { if (is>>()) { std::vector v; @@ -449,11 +457,13 @@ const std::vector& AnyValue::asVector() const } *m_value = v; } - return as>(); + const auto& vv = as>(); + checkSize(vv, nMin, nMax); + return vv; } template<> -std::vector& AnyValue::asVector() +std::vector& AnyValue::asVector(size_t nMin, size_t nMax) { if (is>>()) { std::vector v; @@ -465,7 +475,9 @@ std::vector& AnyValue::asVector() } *m_value = v; } - return as>(); + auto& vv = as>(); + checkSize(vv, nMin, nMax); + return vv; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index b2c19e868..b484ca8ad 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -314,7 +314,7 @@ Arrhenius readArrhenius(const Reaction& R, const AnyValue& rate, len_dim += pressure_dependence * reaction_phase_ndim; quantity_dim -= pressure_dependence; - auto& rate_vec = rate.asVector(); + auto& rate_vec = rate.asVector(3); double A = units.convert(rate_vec[0], Units(1.0, 0, len_dim, -1, 0, 0, quantity_dim)); double b = rate_vec[1].asDouble(); double Ta = units.convertMolarEnergy(rate_vec[2], "K"); @@ -671,7 +671,7 @@ void setupPlogReaction(PlogReaction& R, const AnyMap& node, setupReaction(R, node); std::multimap rates; for (const auto& rate : node.at("rate-constants").asVector()) { - const auto& p_rate = rate.asVector(); + const auto& p_rate = rate.asVector(2); rates.insert({units.convert(p_rate[0], "Pa"), readArrhenius(R, p_rate[1], kin, units)}); } diff --git a/test/general/test_containers.cpp b/test/general/test_containers.cpp index 9b25fc8b9..134bc9ade 100644 --- a/test/general/test_containers.cpp +++ b/test/general/test_containers.cpp @@ -96,6 +96,18 @@ TEST(AnyMap, vector) EXPECT_EQ(m["nested/item"].asVector().size(), (size_t) 4); } +TEST(AnyMap, vector_length) +{ + AnyMap m; + m["foo"] = vector_fp{2.4, 9.6, 14.4, 28.8}; + // Valid lengths + m["foo"].asVector(4); + m["foo"].asVector(2, 5); + // Invalid lengths + EXPECT_THROW(m["foo"].asVector(3), CanteraError); + EXPECT_THROW(m["foo"].asVector(5, 8), CanteraError); +} + TEST(AnyMap, getters_with_defaults) { AnyMap m;