add Value::lookup()

This commit is contained in:
Michael Davidsaver
2020-07-16 12:37:48 -07:00
parent bb7ac1e8e6
commit 290a2689fc
4 changed files with 83 additions and 13 deletions
+13
View File
@@ -72,6 +72,17 @@ All operations on an invalid Value should be safe and well defined.
In this example, the operator[] lookup of a non-existant field returns an invalid Value.
Attempting to extract an integer from this will then throw a `pvxs::NoField` exception.
Value
-----
Field Lookup
^^^^^^^^^^^^
Access to members of structured types is accomplished through `pvxs::Value::operator[]` or `pvxs::Value::lookup`.
These two methods differ in how errors are communicated.
operator[] will return an "invalid" or "empty" Value if the expression does not address a member.
lookup() will throw an exception describing where and how expression evaluation failed.
Iteration
^^^^^^^^^
@@ -103,6 +114,8 @@ and will always appear as empty.
.. doxygenstruct:: pvxs::NoConvert
.. doxygenstruct:: pvxs::LookupError
Array fields
------------
+45 -6
View File
@@ -24,6 +24,13 @@ NoConvert::NoConvert()
NoConvert::~NoConvert() {}
LookupError::LookupError(const std::string& msg)
:std::runtime_error(msg)
{}
LookupError::~LookupError() {}
std::shared_ptr<const impl::FieldDesc>
Value::Helper::type(const Value& v)
{
@@ -636,7 +643,7 @@ bool Value::tryCopyIn(const void *ptr, StoreType type)
}
}
void Value::traverse(const std::string &expr, bool modify)
void Value::traverse(const std::string &expr, bool modify, bool dothrow)
{
size_t pos=0;
bool maybedot = false;
@@ -656,6 +663,8 @@ void Value::traverse(const std::string &expr, bool modify)
// at top
store.reset();
desc = nullptr;
if(dothrow)
throw LookupError(SB()<<"Can't traverse to parent of root with '"<<expr<<"'");
break;
}
@@ -670,6 +679,8 @@ void Value::traverse(const std::string &expr, bool modify)
if(expr[pos]!='.') {
store.reset();
desc = nullptr;
if(dothrow)
throw LookupError(SB()<<"expected '.' at "<<pos<<" in '"<<expr<<"'");
break;
}
maybedot = false;
@@ -680,7 +691,9 @@ void Value::traverse(const std::string &expr, bool modify)
decltype (desc->mlookup)::const_iterator it;
if(sep>0 && (it=desc->mlookup.find(expr.substr(pos, sep-pos)))!=desc->mlookup.end()) {
const auto& name = expr.substr(pos, sep-pos);
if(sep>0 && (it=desc->mlookup.find(name))!=desc->mlookup.end()) {
// found it
auto next = desc+it->second;
decltype(store) value(store, store.get()+it->second);
@@ -692,6 +705,8 @@ void Value::traverse(const std::string &expr, bool modify)
// no such member
store.reset();
desc = nullptr;
if(dothrow)
throw LookupError(SB()<<"no such member '"<<name<<"' in '"<<expr<<"'");
}
} else if(desc->code.code==TypeCode::Union || desc->code.code==TypeCode::Any) {
@@ -732,6 +747,8 @@ void Value::traverse(const std::string &expr, bool modify)
// traversing const Value, can't select Union
store.reset();
desc = nullptr;
if(dothrow)
throw LookupError(SB()<<"traversing const Value, can't select Union in '"<<expr<<"'");
}
}
}
@@ -739,6 +756,8 @@ void Value::traverse(const std::string &expr, bool modify)
// expected "->"
store.reset();
desc = nullptr;
if(dothrow)
throw LookupError(SB()<<"expected -> in '"<<expr<<"'");
}
} else if(desc->code.isarray() && desc->code.kind()==Kind::Compound) {
@@ -766,33 +785,53 @@ void Value::traverse(const std::string &expr, bool modify)
// wrong element type or out of range
store.reset();
desc = nullptr;
if(dothrow)
throw std::runtime_error(SB()<<"wrong element type or out of range in '"<<expr<<"'");
}
} else {
// syntax error
store.reset();
desc = nullptr;
if(dothrow)
throw std::runtime_error(SB()<<"indexing syntax error in '"<<expr<<"'");
}
} else {
// syntax error or wrong field type (can't index scalar array)
store.reset();
desc = nullptr;
if(dothrow)
throw std::runtime_error(SB()<<"indexing syntax error or wrong field type (can't index scalar array) in '"<<expr<<"'");
}
}
}
Value Value::operator[](const char *name)
Value Value::operator[](const std::string& name)
{
Value ret(*this);
ret.traverse(name, true);
ret.traverse(name, true, false);
return ret;
}
const Value Value::operator[](const char *name) const
const Value Value::operator[](const std::string& name) const
{
Value ret(*this);
ret.traverse(name, false);
ret.traverse(name, false, false);
return ret;
}
Value Value::lookup(const std::string& name)
{
Value ret(*this);
ret.traverse(name, true, true);
return ret;
}
const Value Value::lookup(const std::string& name) const
{
Value ret(*this);
ret.traverse(name, false, true);
return ret;
}
+20 -6
View File
@@ -445,6 +445,12 @@ struct PVXS_API NoConvert : public std::runtime_error
virtual ~NoConvert();
};
struct PVXS_API LookupError : public std::runtime_error
{
explicit LookupError(const std::string& msg);
virtual ~LookupError();
};
/** Generic data container
*
* References a single data field, which may be free-standing (eg. "int x = 5;")
@@ -644,7 +650,7 @@ public:
// Struct/Union access
private:
void traverse(const std::string& expr, bool modify);
void traverse(const std::string& expr, bool modify, bool dothrow);
public:
/** Attempt to access a descendant field.
@@ -653,7 +659,7 @@ public:
* * name of a child field. eg. "value"
* * name of a descendant field. eg "alarm.severity"
* * element of an array of structures. eg "dimension[0]"
* * name of a union field. eg. "booleanValue"
* * name of a union field. eg. "->booleanValue"
*
* These may be composed. eg.
*
@@ -662,10 +668,18 @@ public:
*
* @returns A valid() Value if the descendant field exists, otherwise an invalid Value.
*/
Value operator[](const char *name);
inline Value operator[](const std::string& name) { return (*this)[name.c_str()]; }
const Value operator[](const char *name) const;
inline const Value operator[](const std::string& name) const { return (*this)[name.c_str()]; }
Value operator[](const std::string& name);
const Value operator[](const std::string& name) const;
/** Attempt to access a descendant field, or throw exception.
*
* Acts like operator[] on success, but throws a (hopefully descriptive)
* exception instead of returning an invalid Value.
*
* @throws LookupError If the lookup can not be satisfied
*/
Value lookup(const std::string& name);
const Value lookup(const std::string& name) const;
//! Number of child fields.
//! only Struct, StructA, Union, UnionA return non-zero
+5 -1
View File
@@ -27,6 +27,10 @@ void testTraverse()
testOk1(!top["<"].valid());
testThrows<std::runtime_error>([&top](){
top.lookup("<");
});
{
auto top2 = top["value<"];
testOk1(top.equalType(top2));
@@ -310,7 +314,7 @@ void testAssignSimilar()
MAIN(testdata)
{
testPlan(92);
testPlan(93);
testSetup();
testTraverse();
testAssign();