From da9cdb5cf6fd15b224d8cc81c61478cf6d5a0cfb Mon Sep 17 00:00:00 2001 From: Robert Haschke Date: Wed, 21 Feb 2018 20:40:00 +0100 Subject: [PATCH] PropertyMap: different exception types undeclared undefined type_error --- .../moveit/task_constructor/properties.h | 43 ++++++++++++-- core/src/properties.cpp | 56 +++++++++++++------ core/test/test_properties.cpp | 16 +++--- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/core/include/moveit/task_constructor/properties.h b/core/include/moveit/task_constructor/properties.h index 61a13d56..b93fe3f8 100644 --- a/core/include/moveit/task_constructor/properties.h +++ b/core/include/moveit/task_constructor/properties.h @@ -82,6 +82,15 @@ class Property { } public: + /// base class for Property exceptions + class error; + /// exception thrown when accessing an undeclared property + class undeclared; + /// exception thrown when accessing an undefined property + class undefined; + /// exception thrown when trying to set a value not matching the declared type + class type_error; + typedef int SourceId; /// function callback used to initialize property value from another PropertyMap typedef std::function InitializerFunction; @@ -135,6 +144,30 @@ private: }; +class Property::error : public std::runtime_error { +protected: + std::string property_name_; +public: + explicit error(const std::string& msg) : std::runtime_error(msg) {} + const std::string& name() const { return property_name_; } + void setName(const std::string& name) { property_name_ = name; } +}; +class Property::undeclared : public Property::error { +public: + explicit undeclared(const std::string& name); + explicit undeclared(const std::string& name, const std::string& msg); +}; +class Property::undefined : public Property::error { +public: + explicit undefined(const std::string& name); + explicit undefined(const std::string& name, const std::string& msg); +}; +class Property::type_error : public Property::error { +public: + explicit type_error(const std::string& current_type, const std::string& declared_type); +}; + + /** PropertyMap is map of (name, Property) pairs. * * Conveniency methods are provided to setup property initialization for several @@ -164,7 +197,9 @@ public: return declare(name, typeid(T), description, default_value, &Property::serialize); } - /// get the property with given name + bool hasProperty(const std::string &name) const; + + /// get the property with given name, throws Property::undeclared for unknown name Property& property(const std::string &name); const Property& property(const std::string &name) const { return const_cast(this)->property(name); @@ -190,15 +225,15 @@ public: /// temporarily set the value of a property void setCurrent(const std::string& name, const boost::any& value); - /// get the value of a property + /// Gt the value of a property. Throws undeclared if unknown name const boost::any& get(const std::string& name) const; - /// Get typed value of property. Throws runtime_error if undefined or bad_any_cast on type mismatch. + /// Get typed value of property. Throws undeclared, undefined, or bad_any_cast. template const T& get(const std::string& name) const { const boost::any& value = get(name); if (value.empty()) - throw std::runtime_error(std::string("undefined property: " + name)); + throw Property::undefined(name); return boost::any_cast(value); } /// get typed value of property, using fallback if undefined. Throws bad_any_cast on type mismatch. diff --git a/core/src/properties.cpp b/core/src/properties.cpp index cc7c7e10..394ada88 100644 --- a/core/src/properties.cpp +++ b/core/src/properties.cpp @@ -55,16 +55,6 @@ Property::Property(const std::type_index& type_index, const std::string& descrip assert(default_.empty() || std::type_index(default_.type()) == type_index_); } -namespace { -void typeCheck(const boost::any& value, const std::type_index& type_index) -{ - if (std::type_index(value.type()) != type_index) { - static boost::format fmt("type (%1%) doesn't match property's declared type (%2%)"); - throw std::logic_error(boost::str(fmt % value.type().name() % type_index.name())); - } -} -} - void Property::setValue(const boost::any &value) { setCurrentValue(value); default_ = value_; @@ -72,8 +62,9 @@ void Property::setValue(const boost::any &value) { void Property::setCurrentValue(const boost::any &value) { - if (!value.empty()) - typeCheck(value, type_index_); + if (!value.empty() && std::type_index(value.type()) != type_index_) + throw Property::type_error(value.type().name(), type_index_.name()); + value_ = value; if (signaller_) signaller_(this); @@ -92,7 +83,7 @@ std::string Property::serialize() const { Property& Property::configureInitFrom(SourceId source, const Property::InitializerFunction &f) { if (source != source_id_ && initializer_) - throw std::runtime_error("Property was already configured for initialization from another source id"); + throw error("Property was already configured for initialization from another source id"); source_id_ = source; initializer_ = f; @@ -117,15 +108,21 @@ Property& PropertyMap::declare(const std::string &name, const std::type_info &ty { auto it_inserted = props_.insert(std::make_pair(name, Property(std::type_index(type), description, default_value, serialize))); if (!it_inserted.second && std::type_index(type) != it_inserted.first->second.type_index_) - throw std::logic_error("Property '" + name + "' was already declared with different type."); + throw Property::type_error(std::type_index(type).name(), it_inserted.first->second.type_index_.name()); return it_inserted.first->second; } +bool PropertyMap::hasProperty(const std::string& name) const +{ + auto it = props_.find(name); + return it != props_.end(); +} + Property& PropertyMap::property(const std::string &name) { auto it = props_.find(name); if (it == props_.end()) - throw std::runtime_error("Undeclared property '" + name + "'"); + throw Property::undeclared(name); return it->second; } @@ -142,7 +139,7 @@ void PropertyMap::set(const std::string& name, const boost::any& val auto range = props_.equal_range(name); if (range.first == range.second) { // name is not yet declared if (value.empty()) - throw std::logic_error("trying to set undeclared property '" + name + "' with NULL value"); + throw Property::undeclared(name, "trying to set undeclared property '" + name + "' with NULL value"); auto it = props_.insert(range.first, std::make_pair(name, Property(value.type(), "", boost::any(), Property::SerializeFunction()))); it->second.setValue(value); @@ -194,5 +191,32 @@ boost::any fromName(const PropertyMap& other, const std::string& other_name) } } + +Property::undeclared::undeclared(const std::string& name) + : undeclared(name, "Undeclared property: '" + name + "'") +{} + +Property::undeclared::undeclared(const std::string& name, const std::string& msg) + : Property::error(msg) +{ + setName(name); +} + +Property::undefined::undefined(const std::string& name) + : undefined(name, "Undefined property: '" + name + "'") +{} + +Property::undefined::undefined(const std::string& name, const std::string& msg) + : Property::error(msg) +{ + setName(name); +} + +static boost::format type_error_fmt("type (%1%) doesn't match property's declared type (%2%)"); +Property::type_error::type_error(const std::string& current_type, const std::string& declared_type) + : Property::error(boost::str(type_error_fmt % current_type % declared_type)) +{} + + } // namespace task_constructor } // namespace moveit diff --git a/core/test/test_properties.cpp b/core/test/test_properties.cpp index 5f582671..5a5bd740 100644 --- a/core/test/test_properties.cpp +++ b/core/test/test_properties.cpp @@ -13,9 +13,9 @@ TEST(Property, standard) { EXPECT_EQ(props.get("double1"), 1.0); EXPECT_EQ(props.get("double2"), 2.0); - EXPECT_THROW(props.get("double3"), std::runtime_error); + EXPECT_THROW(props.get("double3"), Property::undeclared); - EXPECT_THROW(props.get("double4"), std::runtime_error); + EXPECT_THROW(props.get("double4"), Property::undefined); EXPECT_FALSE(props.property("double4").defined()); EXPECT_EQ(props.get("double4", 0.0), 0.0); @@ -41,10 +41,10 @@ TEST(Property, redeclare) { // avoid second declaration with different type props.declare("double1"); - EXPECT_THROW(props.declare("double1"), std::logic_error); + EXPECT_THROW(props.declare("double1"), Property::type_error); // types not matching? - EXPECT_THROW(props.set("double1", 1), std::logic_error); + EXPECT_THROW(props.set("double1", 1), Property::type_error); props.set("double1", 3.14); EXPECT_EQ(props.get("double1"), 3.14); @@ -108,7 +108,7 @@ TEST_F(InitFromTest, standard) { EXPECT_EQ(slave.get("double1"), 1.0); EXPECT_EQ(slave.get("double2"), 2.0); EXPECT_FALSE(slave.property("double3").defined()); - EXPECT_THROW(slave.property("double4"), std::runtime_error); + EXPECT_THROW(slave.property("double4"), Property::undeclared); } TEST_F(InitFromTest, limited) { @@ -117,7 +117,7 @@ TEST_F(InitFromTest, limited) { EXPECT_EQ(slave.get("double1"), 1.0); EXPECT_FALSE(slave.property("double2").defined()); EXPECT_FALSE(slave.property("double3").defined()); - EXPECT_THROW(slave.property("double4"), std::runtime_error); + EXPECT_THROW(slave.property("double4"), Property::undeclared); } TEST_F(InitFromTest, sourceId) { @@ -126,7 +126,7 @@ TEST_F(InitFromTest, sourceId) { EXPECT_FALSE(slave.property("double1").defined()); EXPECT_FALSE(slave.property("double2").defined()); EXPECT_FALSE(slave.property("double3").defined()); - EXPECT_THROW(slave.property("double4"), std::runtime_error); + EXPECT_THROW(slave.property("double4"), Property::undeclared); } TEST_F(InitFromTest, multipleSourceIds) { @@ -141,7 +141,7 @@ TEST_F(InitFromTest, otherName) { EXPECT_EQ(slave.get("double1"), 2.0); EXPECT_FALSE(slave.property("double2").defined()); EXPECT_FALSE(slave.property("double3").defined()); - EXPECT_THROW(slave.property("double4"), std::runtime_error); + EXPECT_THROW(slave.property("double4"), Property::undeclared); } TEST_F(InitFromTest, function) {