From eadb8ff65b61395c22174a966e6591b4c53deb84 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 22 Jun 2015 17:48:32 -0400 Subject: [PATCH 1/2] add PVStructure::getAs<>() for field access w/o NULL --- src/factory/PVStructure.cpp | 48 +++++++++++++++++++++++++++ src/pv/pvData.h | 28 ++++++++++++++++ testApp/pv/testPVData.cpp | 66 ++++++++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/factory/PVStructure.cpp b/src/factory/PVStructure.cpp index 841e919..f0d9215 100644 --- a/src/factory/PVStructure.cpp +++ b/src/factory/PVStructure.cpp @@ -134,6 +134,54 @@ PVFieldPtr PVStructure::getSubField(size_t fieldOffset) const throw std::logic_error("PVStructure.getSubField: Logic error"); } +PVField* PVStructure::GetAsImpl(const char *name) const +{ + const PVStructure *parent = this; + if(!name) + throw std::invalid_argument("field name is NULL string"); + + while(true) { + const char *sep=name; + while(*sep!='\0' && *sep!='.' && *sep!=' ') sep++; + if(*sep==' ') + throw std::runtime_error("No spaces allowed in field name"); + + size_t N = sep-name; + if(N==0) + throw std::runtime_error("zero-length field name encountered"); + + const PVFieldPtrArray& pvFields = parent->getPVFields(); + + PVField *child = NULL; + + for(size_t i=0, n=pvFields.size(); i!=n; i++) { + const PVFieldPtr& fld = pvFields[i]; + const std::string& fname = fld->getFieldName(); + + if(fname.size()==N && memcmp(name, fname.c_str(), N)==0) { + child = fld.get(); + break; + } + } + + if(!child) + throw std::runtime_error("field not found"); //TODO: which sub field? + + if(*sep) { + // this is not the requested leaf + parent = dynamic_cast(child); + if(!child) + throw std::runtime_error("mid-field is not a PVStructure"); //TODO: which sub field? + child = NULL; + name = sep+1; // skip past '.' + // loop around to new parent + + } else { + return child; + } + } +} + PVBooleanPtr PVStructure::getBooleanField(string const &fieldName) { diff --git a/src/pv/pvData.h b/src/pv/pvData.h index 575019a..a2584a7 100644 --- a/src/pv/pvData.h +++ b/src/pv/pvData.h @@ -703,6 +703,34 @@ public: return std::tr1::shared_ptr(); } +private: + PVField *GetAsImpl(const char *name) const; +public: + + /** + * Get a subfield with the specified name. + * @param name a '.' seperated list of child field names (no whitespace allowed) + * @returns A reference to the sub-field (never NULL) + * @throws std::runtime_error if the requested sub-field doesn't exist, or has a different type + * @code + * PVInt& ref = pvStruct->getAs("substruct.leaffield"); + * @endcode + */ + template + PVT& getAs(const char *name) const + { + PVT *raw = dynamic_cast(GetAsImpl(name)); + if(!raw) + throw std::runtime_error("Field has wrong type"); + return *raw; + } + + template + FORCE_INLINE PVT& getAs(std::string const &fieldName) const + { + return this->getAs(fieldName.c_str()); + } + /** * Get a boolean field with the specified name. * @deprecated No longer needed. Use templete version of getSubField diff --git a/testApp/pv/testPVData.cpp b/testApp/pv/testPVData.cpp index ed1f024..7096503 100644 --- a/testApp/pv/testPVData.cpp +++ b/testApp/pv/testPVData.cpp @@ -525,9 +525,72 @@ static void testCopy() testOk(*pvUnion == *pvUnion2, "PVUnion PVStructure copy, to different type PVUnion"); } +static void testFieldAccess() +{ + testDiag("Check methods for accessing structure fields"); + + StructureConstPtr tdef = fieldCreate->createFieldBuilder()-> + add("test", pvInt)-> + addNestedStructure("hello")-> + add("world", pvInt)-> + endNested()-> + createStructure(); + + PVStructurePtr fld = pvDataCreate->createPVStructure(tdef); + + PVIntPtr a = fld->getSubField("test"); + testOk1(a!=NULL); + if(a.get()) { + PVInt& b = fld->getAs("test"); + testOk(&b==a.get(), "%p == %p", &b, a.get()); + } else + testSkip(1, "test doesn't exist?"); + + a = fld->getSubField("hello.world"); + testOk1(a!=NULL); + if(a.get()) { + PVInt& b = fld->getAs("hello.world"); + testOk(&b==a.get(), "%p == %p", &b, a.get()); + } else + testSkip(1, "hello.world doesn't exist?"); + + // non-existant + testOk1(fld->getSubField("invalid").get()==NULL); + + // wrong type + testOk1(fld->getSubField("test").get()==NULL); + + // intermediate struct non existant + testOk1(fld->getSubField("helo.world").get()==NULL); + + // empty leaf field name + testOk1(fld->getSubField("hello.").get()==NULL); + + // empty field name + testOk1(fld->getSubField("hello..world").get()==NULL); + testOk1(fld->getSubField(".").get()==NULL); + + // whitespace + testOk1(fld->getSubField(" test").get()==NULL); + + try{ + fld->getAs("invalid"); + testFail("missing requried exception"); + }catch(std::runtime_error& e){ + testPass("caught expected exception: %s", e.what()); + } + + try{ + fld->getAs("test"); + testFail("missing requried exception"); + }catch(std::runtime_error& e){ + testPass("caught expected exception: %s", e.what()); + } +} + MAIN(testPVData) { - testPlan(222); + testPlan(235); fieldCreate = getFieldCreate(); pvDataCreate = getPVDataCreate(); standardField = getStandardField(); @@ -538,6 +601,7 @@ MAIN(testPVData) testScalarArray(); testRequest(); testCopy(); + testFieldAccess(); return testDone(); } From 4f25c7a3eae8ca1008b779cd2f17ddf220d5aa23 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 22 Jun 2015 18:11:19 -0400 Subject: [PATCH 2/2] remove findSubField --- src/factory/PVStructure.cpp | 68 +++++++++++-------------------------- 1 file changed, 19 insertions(+), 49 deletions(-) diff --git a/src/factory/PVStructure.cpp b/src/factory/PVStructure.cpp index f0d9215..3977151 100644 --- a/src/factory/PVStructure.cpp +++ b/src/factory/PVStructure.cpp @@ -44,10 +44,6 @@ PVUnionPtr PVStructure::nullPVUnion; PVUnionArrayPtr PVStructure::nullPVUnionArray; PVScalarArrayPtr PVStructure::nullPVScalarArray; -static PVFieldPtr findSubField( - string const &fieldName, - const PVStructure *pvStructure); - PVStructure::PVStructure(StructureConstPtr const & structurePtr) : PVField(structurePtr), structurePtr(structurePtr), @@ -111,7 +107,11 @@ const PVFieldPtrArray & PVStructure::getPVFields() const PVFieldPtr PVStructure::getSubField(string const &fieldName) const { - return findSubField(fieldName,this); + try{ + return GetAsImpl(fieldName.c_str())->shared_from_this(); + }catch(...){ + return PVFieldPtr(); + } } @@ -256,21 +256,22 @@ PVUnionPtr PVStructure::getUnionField(string const &fieldName) PVScalarArrayPtr PVStructure::getScalarArrayField( string const &fieldName,ScalarType elementType) { - PVFieldPtr pvField = findSubField(fieldName,this); - if(pvField.get()==NULL) { + try{ + PVFieldPtr pvField = GetAsImpl(fieldName.c_str())->shared_from_this(); + FieldConstPtr field = pvField->getField(); + Type type = field->getType(); + if(type!=scalarArray) { + return nullPVScalarArray; + } + ScalarArrayConstPtr pscalarArray + = static_pointer_cast(pvField->getField()); + if(pscalarArray->getElementType()!=elementType) { + return nullPVScalarArray; + } + return std::tr1::static_pointer_cast(pvField); + }catch(...){ return nullPVScalarArray; } - FieldConstPtr field = pvField->getField(); - Type type = field->getType(); - if(type!=scalarArray) { - return nullPVScalarArray; - } - ScalarArrayConstPtr pscalarArray - = static_pointer_cast(pvField->getField()); - if(pscalarArray->getElementType()!=elementType) { - return nullPVScalarArray; - } - return std::tr1::static_pointer_cast(pvField); } PVStructureArrayPtr PVStructure::getStructureArrayField( @@ -375,37 +376,6 @@ void PVStructure::deserialize(ByteBuffer *pbuffer, } } -static PVFieldPtr findSubField( - string const & fieldName, - PVStructure const *pvStructure) -{ - if( fieldName.length()<1) return PVFieldPtr(); - string::size_type index = fieldName.find('.'); - string name = fieldName; - string restOfName = string(); - if(index>0) { - name = fieldName.substr(0, index); - if(fieldName.length()>index) { - restOfName = fieldName.substr(index+1); - } - } - PVFieldPtrArray const & pvFields = pvStructure->getPVFields(); - PVFieldPtr pvField; - size_t numFields = pvStructure->getStructure()->getNumberFields(); - for(size_t i=0; igetFieldName().compare(name); - if(result==0) { - if(restOfName.length()==0) return pvFields[i]; - if(pvField->getField()->getType()!=structure) return PVFieldPtr(); - PVStructurePtr pvStructure = - std::tr1::static_pointer_cast(pvField); - return findSubField(restOfName,pvStructure.get()); - } - } - return PVFieldPtr(); -} - std::ostream& PVStructure::dumpValue(std::ostream& o) const { o << format::indent() << getStructure()->getID() << ' ' << getFieldName();