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(); }