PropertyMap: different exception types

undeclared
undefined
type_error
This commit is contained in:
Robert Haschke 2018-02-21 20:40:00 +01:00
parent 9cd6efe46f
commit da9cdb5cf6
3 changed files with 87 additions and 28 deletions

View File

@ -82,6 +82,15 @@ class Property {
} }
public: 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; typedef int SourceId;
/// function callback used to initialize property value from another PropertyMap /// function callback used to initialize property value from another PropertyMap
typedef std::function<boost::any(const PropertyMap& other)> InitializerFunction; typedef std::function<boost::any(const PropertyMap& other)> 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. /** PropertyMap is map of (name, Property) pairs.
* *
* Conveniency methods are provided to setup property initialization for several * 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<T>); return declare(name, typeid(T), description, default_value, &Property::serialize<T>);
} }
/// 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); Property& property(const std::string &name);
const Property& property(const std::string &name) const { const Property& property(const std::string &name) const {
return const_cast<PropertyMap*>(this)->property(name); return const_cast<PropertyMap*>(this)->property(name);
@ -190,15 +225,15 @@ public:
/// temporarily set the value of a property /// temporarily set the value of a property
void setCurrent(const std::string& name, const boost::any& value); 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; 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<typename T> template<typename T>
const T& get(const std::string& name) const { const T& get(const std::string& name) const {
const boost::any& value = get(name); const boost::any& value = get(name);
if (value.empty()) if (value.empty())
throw std::runtime_error(std::string("undefined property: " + name)); throw Property::undefined(name);
return boost::any_cast<const T&>(value); return boost::any_cast<const T&>(value);
} }
/// get typed value of property, using fallback if undefined. Throws bad_any_cast on type mismatch. /// get typed value of property, using fallback if undefined. Throws bad_any_cast on type mismatch.

View File

@ -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_); 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) { void Property::setValue(const boost::any &value) {
setCurrentValue(value); setCurrentValue(value);
default_ = value_; default_ = value_;
@ -72,8 +62,9 @@ void Property::setValue(const boost::any &value) {
void Property::setCurrentValue(const boost::any &value) void Property::setCurrentValue(const boost::any &value)
{ {
if (!value.empty()) if (!value.empty() && std::type_index(value.type()) != type_index_)
typeCheck(value, type_index_); throw Property::type_error(value.type().name(), type_index_.name());
value_ = value; value_ = value;
if (signaller_) if (signaller_)
signaller_(this); signaller_(this);
@ -92,7 +83,7 @@ std::string Property::serialize() const {
Property& Property::configureInitFrom(SourceId source, const Property::InitializerFunction &f) Property& Property::configureInitFrom(SourceId source, const Property::InitializerFunction &f)
{ {
if (source != source_id_ && initializer_) 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; source_id_ = source;
initializer_ = f; 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))); 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_) 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; 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) Property& PropertyMap::property(const std::string &name)
{ {
auto it = props_.find(name); auto it = props_.find(name);
if (it == props_.end()) if (it == props_.end())
throw std::runtime_error("Undeclared property '" + name + "'"); throw Property::undeclared(name);
return it->second; return it->second;
} }
@ -142,7 +139,7 @@ void PropertyMap::set<boost::any>(const std::string& name, const boost::any& val
auto range = props_.equal_range(name); auto range = props_.equal_range(name);
if (range.first == range.second) { // name is not yet declared if (range.first == range.second) { // name is not yet declared
if (value.empty()) 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(), auto it = props_.insert(range.first, std::make_pair(name, Property(value.type(), "", boost::any(),
Property::SerializeFunction()))); Property::SerializeFunction())));
it->second.setValue(value); 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 task_constructor
} // namespace moveit } // namespace moveit

View File

@ -13,9 +13,9 @@ TEST(Property, standard) {
EXPECT_EQ(props.get<double>("double1"), 1.0); EXPECT_EQ(props.get<double>("double1"), 1.0);
EXPECT_EQ(props.get<double>("double2"), 2.0); EXPECT_EQ(props.get<double>("double2"), 2.0);
EXPECT_THROW(props.get<double>("double3"), std::runtime_error); EXPECT_THROW(props.get<double>("double3"), Property::undeclared);
EXPECT_THROW(props.get<double>("double4"), std::runtime_error); EXPECT_THROW(props.get<double>("double4"), Property::undefined);
EXPECT_FALSE(props.property("double4").defined()); EXPECT_FALSE(props.property("double4").defined());
EXPECT_EQ(props.get<double>("double4", 0.0), 0.0); EXPECT_EQ(props.get<double>("double4", 0.0), 0.0);
@ -41,10 +41,10 @@ TEST(Property, redeclare) {
// avoid second declaration with different type // avoid second declaration with different type
props.declare<double>("double1"); props.declare<double>("double1");
EXPECT_THROW(props.declare<long double>("double1"), std::logic_error); EXPECT_THROW(props.declare<long double>("double1"), Property::type_error);
// types not matching? // 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); props.set("double1", 3.14);
EXPECT_EQ(props.get<double>("double1"), 3.14); EXPECT_EQ(props.get<double>("double1"), 3.14);
@ -108,7 +108,7 @@ TEST_F(InitFromTest, standard) {
EXPECT_EQ(slave.get<double>("double1"), 1.0); EXPECT_EQ(slave.get<double>("double1"), 1.0);
EXPECT_EQ(slave.get<double>("double2"), 2.0); EXPECT_EQ(slave.get<double>("double2"), 2.0);
EXPECT_FALSE(slave.property("double3").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, limited) { TEST_F(InitFromTest, limited) {
@ -117,7 +117,7 @@ TEST_F(InitFromTest, limited) {
EXPECT_EQ(slave.get<double>("double1"), 1.0); EXPECT_EQ(slave.get<double>("double1"), 1.0);
EXPECT_FALSE(slave.property("double2").defined()); EXPECT_FALSE(slave.property("double2").defined());
EXPECT_FALSE(slave.property("double3").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) { TEST_F(InitFromTest, sourceId) {
@ -126,7 +126,7 @@ TEST_F(InitFromTest, sourceId) {
EXPECT_FALSE(slave.property("double1").defined()); EXPECT_FALSE(slave.property("double1").defined());
EXPECT_FALSE(slave.property("double2").defined()); EXPECT_FALSE(slave.property("double2").defined());
EXPECT_FALSE(slave.property("double3").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) { TEST_F(InitFromTest, multipleSourceIds) {
@ -141,7 +141,7 @@ TEST_F(InitFromTest, otherName) {
EXPECT_EQ(slave.get<double>("double1"), 2.0); EXPECT_EQ(slave.get<double>("double1"), 2.0);
EXPECT_FALSE(slave.property("double2").defined()); EXPECT_FALSE(slave.property("double2").defined());
EXPECT_FALSE(slave.property("double3").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) { TEST_F(InitFromTest, function) {