add ValueBuilder
This commit is contained in:
@@ -116,6 +116,31 @@ struct _and {};
|
||||
template<typename A, typename B, class R>
|
||||
struct _and<A,B, typename A::type, typename B::type, R> { typedef R type; };
|
||||
|
||||
/** Mangle type to best pass as an argument.
|
||||
*
|
||||
* POD types passed by value.
|
||||
* All others by const reference.
|
||||
*/
|
||||
template<typename T>
|
||||
struct arg_type {typedef const T& type;};
|
||||
#define SIMPLE_ARG_TYPE(TYPE) template<> struct arg_type<TYPE> { typedef TYPE type; };
|
||||
SIMPLE_ARG_TYPE(bool)
|
||||
SIMPLE_ARG_TYPE(char)
|
||||
SIMPLE_ARG_TYPE(signed char)
|
||||
SIMPLE_ARG_TYPE(unsigned char)
|
||||
SIMPLE_ARG_TYPE(short)
|
||||
SIMPLE_ARG_TYPE(unsigned short)
|
||||
SIMPLE_ARG_TYPE(int)
|
||||
SIMPLE_ARG_TYPE(unsigned int)
|
||||
SIMPLE_ARG_TYPE(long)
|
||||
SIMPLE_ARG_TYPE(unsigned long)
|
||||
SIMPLE_ARG_TYPE(long long)
|
||||
SIMPLE_ARG_TYPE(unsigned long long)
|
||||
SIMPLE_ARG_TYPE(float)
|
||||
SIMPLE_ARG_TYPE(double)
|
||||
SIMPLE_ARG_TYPE(long double)
|
||||
#undef SIMPLE_ARG_TYPE
|
||||
|
||||
}}}
|
||||
|
||||
#endif // TEMPLATEMETA_H
|
||||
|
||||
@@ -4,6 +4,7 @@ SRC_DIRS += $(PVDATA_SRC)/pv
|
||||
|
||||
INC += pv/pvType.h
|
||||
INC += pv/pvIntrospect.h
|
||||
INC += pv/valueBuilder.h
|
||||
INC += pv/pvData.h
|
||||
INC += pv/convert.h
|
||||
INC += pv/standardField.h
|
||||
@@ -13,3 +14,4 @@ INC += pv/pvdVersion.h
|
||||
INC += pv/pvdVersionNum.h
|
||||
|
||||
LIBSRCS += pvdVersion.cpp
|
||||
LIBSRCS += valueBuilder.cpp
|
||||
|
||||
261
src/pv/valueBuilder.cpp
Normal file
261
src/pv/valueBuilder.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
|
||||
#include "valueBuilder.h"
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
struct ValueBuilder::child {
|
||||
virtual ~child() {}
|
||||
Type type;
|
||||
child(Type t) : type(t) {}
|
||||
virtual void build(const std::string& name, FieldBuilderPtr& builder)=0;
|
||||
virtual void store(const PVFieldPtr& val)=0;
|
||||
|
||||
};
|
||||
|
||||
struct ValueBuilder::child_struct : public ValueBuilder::child
|
||||
{
|
||||
virtual ~child_struct() {}
|
||||
child_struct(ValueBuilder *par, const std::string& id)
|
||||
:child(structure)
|
||||
,builder(par, id)
|
||||
{}
|
||||
|
||||
ValueBuilder builder;
|
||||
virtual void build(const std::string& name, FieldBuilderPtr& builder) OVERRIDE FINAL
|
||||
{
|
||||
FieldBuilderPtr nest(builder->addNestedStructure(name));
|
||||
buildStruct(this->builder, nest);
|
||||
builder = nest->endNested();
|
||||
}
|
||||
virtual void store(const PVFieldPtr& val) OVERRIDE FINAL
|
||||
{
|
||||
if(val->getField()->getType()!=structure)
|
||||
THROW_EXCEPTION2(std::logic_error, "Structure type mis-match");
|
||||
PVStructurePtr str(std::tr1::static_pointer_cast<PVStructure>(val));
|
||||
storeStruct(builder, str);
|
||||
}
|
||||
|
||||
static
|
||||
void buildStruct(const ValueBuilder& self, FieldBuilderPtr& builder);
|
||||
static
|
||||
void storeStruct(const ValueBuilder& self, const PVStructurePtr& val);
|
||||
|
||||
static
|
||||
void fillStruct(ValueBuilder& self, const PVStructure& val);
|
||||
};
|
||||
|
||||
struct ValueBuilder::child_scalar_base : public ValueBuilder::child
|
||||
{
|
||||
virtual ~child_scalar_base() {}
|
||||
ScalarType stype;
|
||||
child_scalar_base(ScalarType s) : child(scalar), stype(s) {}
|
||||
|
||||
virtual void build(const std::string& name, FieldBuilderPtr& builder) OVERRIDE FINAL
|
||||
{
|
||||
builder->add(name, stype);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ValueBuilder::child_scalar : public ValueBuilder::child_scalar_base
|
||||
{
|
||||
virtual ~child_scalar() {}
|
||||
T value;
|
||||
child_scalar(const void* v) : child_scalar_base(static_cast<ScalarType>(ScalarTypeID<T>::value)), value(*static_cast<const T*>(v)) {}
|
||||
|
||||
virtual void store(const PVFieldPtr& val) OVERRIDE FINAL
|
||||
{
|
||||
if(val->getField()->getType()!=scalar)
|
||||
THROW_EXCEPTION2(std::logic_error, "Scalar type mis-match");
|
||||
|
||||
PVScalarPtr scalar(std::tr1::static_pointer_cast<PVScalar>(val));
|
||||
scalar->putFrom(value);
|
||||
}
|
||||
};
|
||||
|
||||
ValueBuilder::ValueBuilder(const std::string &id) :parent(0),id(id) {}
|
||||
|
||||
void ValueBuilder::child_struct::fillStruct(ValueBuilder& self, const PVStructure& val)
|
||||
{
|
||||
StructureConstPtr type(val.getStructure());
|
||||
const StringArray& field = type->getFieldNames();
|
||||
for(StringArray::const_iterator it=field.begin(), end=field.end(); it!=end; ++it)
|
||||
{
|
||||
PVFieldPtr sub(val.getSubField(*it));
|
||||
assert(sub);
|
||||
FieldConstPtr subtype(sub->getField());
|
||||
switch(subtype->getType()) {
|
||||
case scalar:
|
||||
{
|
||||
PVScalar* subs(static_cast<PVScalar*>(sub.get()));
|
||||
ScalarType stype = subs->getScalar()->getScalarType();
|
||||
switch(stype) {
|
||||
#define STYPE(stype) case pv##stype: { PV ##stype* ptr(static_cast<PV##stype*>(subs)); PV##stype::value_type temp(ptr->get()); self._add(*it, pv##stype, &temp); } break
|
||||
STYPE(Boolean);
|
||||
STYPE(Byte);
|
||||
STYPE(Short);
|
||||
STYPE(Int);
|
||||
STYPE(Long);
|
||||
STYPE(UByte);
|
||||
STYPE(UShort);
|
||||
STYPE(UInt);
|
||||
STYPE(ULong);
|
||||
STYPE(Float);
|
||||
STYPE(Double);
|
||||
STYPE(String);
|
||||
#undef STYPE
|
||||
}
|
||||
}
|
||||
break;
|
||||
case structure:
|
||||
self._add(*it, *static_cast<PVStructure*>(sub.get()));
|
||||
break;
|
||||
default:
|
||||
THROW_EXCEPTION2(std::runtime_error, "ValueBuilder can only clone scalar and structure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValueBuilder::ValueBuilder(const PVStructure& clone) :parent(0)
|
||||
{
|
||||
StructureConstPtr ctype(clone.getStructure());
|
||||
id = ctype->getID();
|
||||
child_struct::fillStruct(*this, clone);
|
||||
}
|
||||
|
||||
|
||||
ValueBuilder::ValueBuilder(ValueBuilder* par, const std::string &id)
|
||||
:parent(par)
|
||||
,id(id)
|
||||
{}
|
||||
|
||||
ValueBuilder::~ValueBuilder()
|
||||
{
|
||||
for(children_t::const_iterator it=children.begin(), end=children.end(); it!=end; ++it)
|
||||
delete it->second;
|
||||
children.clear();
|
||||
}
|
||||
|
||||
void ValueBuilder::_add(const std::string& name, const PVStructure& V)
|
||||
{
|
||||
StructureConstPtr T(V.getStructure());
|
||||
ValueBuilder& self = addNested(name, structure, T->getID());
|
||||
child_struct::fillStruct(self, V);
|
||||
self.endNested();
|
||||
}
|
||||
|
||||
void ValueBuilder::_add(const std::string& name, ScalarType stype, const void *V)
|
||||
{
|
||||
const children_t::iterator it(children.find(name));
|
||||
if(it!=children.end()) {
|
||||
if(it->second->type!=scalar && it->second->type!=scalarArray)
|
||||
THROW_EXCEPTION2(std::logic_error, "Not allowed to replace field. wrong type");
|
||||
}
|
||||
|
||||
std::auto_ptr<child> store;
|
||||
switch(stype) {
|
||||
#define STYPE(stype) case stype: store.reset(new child_scalar<typename ScalarTypeTraits<stype>::type>(V)); break
|
||||
STYPE(pvBoolean);
|
||||
STYPE(pvByte);
|
||||
STYPE(pvShort);
|
||||
STYPE(pvInt);
|
||||
STYPE(pvLong);
|
||||
STYPE(pvUByte);
|
||||
STYPE(pvUShort);
|
||||
STYPE(pvUInt);
|
||||
STYPE(pvULong);
|
||||
STYPE(pvFloat);
|
||||
STYPE(pvDouble);
|
||||
STYPE(pvString);
|
||||
#undef STYPE
|
||||
}
|
||||
if(!store.get())
|
||||
THROW_EXCEPTION2(std::logic_error, "Unhandled ScalarType");
|
||||
|
||||
if(it!=children.end()) {
|
||||
delete it->second;
|
||||
children.erase(it);
|
||||
}
|
||||
children[name] = store.get();
|
||||
store.release();
|
||||
}
|
||||
|
||||
ValueBuilder& ValueBuilder::addNested(const std::string& name, Type type, const std::string &id)
|
||||
{
|
||||
if(type!=structure)
|
||||
THROW_EXCEPTION2(std::invalid_argument, "addNested() only supports structure");
|
||||
child_struct *sub;
|
||||
children_t::const_iterator it(children.find(name));
|
||||
if(it==children.end()) {
|
||||
std::auto_ptr<child_struct> store(new child_struct(this, id));
|
||||
sub = store.get();
|
||||
children[name] = store.get();
|
||||
store.release();
|
||||
} else if(it->second->type==structure) {
|
||||
sub = static_cast<child_struct*>(it->second);
|
||||
} else {
|
||||
std::ostringstream msg;
|
||||
msg<<"Can't replace non-struct field '"<<name<<"' with struct";
|
||||
THROW_EXCEPTION2(std::invalid_argument, msg.str());
|
||||
}
|
||||
sub->builder.id = id;
|
||||
return sub->builder;
|
||||
}
|
||||
|
||||
ValueBuilder& ValueBuilder::endNested()
|
||||
{
|
||||
if(!parent) {
|
||||
THROW_EXCEPTION2(std::logic_error, "Can't end top of structure");
|
||||
}
|
||||
return *parent;
|
||||
}
|
||||
|
||||
void ValueBuilder::child_struct::buildStruct(const ValueBuilder& self, FieldBuilderPtr& builder)
|
||||
{
|
||||
if(!self.id.empty())
|
||||
builder->setId(self.id);
|
||||
|
||||
for(children_t::const_iterator it=self.children.begin(), end=self.children.end(); it!=end; ++it)
|
||||
{
|
||||
it->second->build(it->first, builder);
|
||||
}
|
||||
}
|
||||
|
||||
void ValueBuilder::child_struct::storeStruct(const ValueBuilder& self, const PVStructurePtr& val)
|
||||
{
|
||||
for(children_t::const_iterator it=self.children.begin(), end=self.children.end(); it!=end; ++it)
|
||||
{
|
||||
it->second->store(val->getSubFieldT(it->first));
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer ValueBuilder::buildPVStructure() const
|
||||
{
|
||||
if(parent)
|
||||
THROW_EXCEPTION2(std::logic_error, "Only top level structure may be built. Missing endNested() ?");
|
||||
|
||||
StructureConstPtr type;
|
||||
{
|
||||
FieldBuilderPtr tbuild(getFieldCreate()->createFieldBuilder());
|
||||
|
||||
child_struct::buildStruct(*this, tbuild);
|
||||
|
||||
type = tbuild->createStructure();
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer root(getPVDataCreate()->createPVStructure(type));
|
||||
|
||||
child_struct::storeStruct(*this, root);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
}}// namespace epics::pvData
|
||||
91
src/pv/valueBuilder.h
Normal file
91
src/pv/valueBuilder.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef VALUEBUILDER_H
|
||||
#define VALUEBUILDER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
class PVStructure;
|
||||
|
||||
/** Incrementally define and initialize a PVStructure
|
||||
*
|
||||
* Equivalent to FieldBuilder with the added ability to assign initial values.
|
||||
*
|
||||
@code
|
||||
epics::pvData::PVStructurePtr val(epics::pvData::ValueBuilder()
|
||||
.add<pvInt>("intfld", 42)
|
||||
.addNested("sub")
|
||||
.add<pvString>("strfld", "testing")
|
||||
.endNested()
|
||||
.buildPVStructure());
|
||||
@endcode
|
||||
*/
|
||||
class ValueBuilder
|
||||
{
|
||||
public:
|
||||
//! empty structure
|
||||
explicit ValueBuilder(const std::string& id=std::string());
|
||||
//! Clone existing definition and value
|
||||
explicit ValueBuilder(const PVStructure&);
|
||||
~ValueBuilder();
|
||||
|
||||
//! Add a scalar field with a given name and initial value
|
||||
template<ScalarType ENUM>
|
||||
FORCE_INLINE ValueBuilder& add(const std::string& name, typename meta::arg_type<typename ScalarTypeTraits<ENUM>::type>::type V)
|
||||
{
|
||||
_add(name, ENUM, &V);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE ValueBuilder& add(const std::string& name, const PVStructure& V) {
|
||||
_add(name, V);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Start a sub-structure
|
||||
ValueBuilder& addNested(const std::string& name, Type type=structure, const std::string& id = std::string());
|
||||
//! End a sub-structure
|
||||
ValueBuilder& endNested();
|
||||
|
||||
/** Complete building structure
|
||||
*
|
||||
* @note ValueBuilder may be re-used after calling buildPVStructure()
|
||||
*/
|
||||
std::tr1::shared_ptr<PVStructure> buildPVStructure() const;
|
||||
|
||||
private:
|
||||
void _add(const std::string& name, ScalarType stype, const void *V);
|
||||
void _add(const std::string& name, const PVStructure& V);
|
||||
|
||||
ValueBuilder(ValueBuilder*, const std::string &id = std::string());
|
||||
|
||||
ValueBuilder * const parent;
|
||||
struct child;
|
||||
friend struct child;
|
||||
struct child_struct;
|
||||
friend struct child_struct;
|
||||
struct child_scalar_base;
|
||||
friend struct child_scalar_base;
|
||||
template <typename T>
|
||||
struct child_scalar;
|
||||
template <typename T>
|
||||
friend struct child_scalar;
|
||||
|
||||
typedef std::map<std::string, child*> children_t;
|
||||
children_t children;
|
||||
std::string id;
|
||||
|
||||
ValueBuilder(const ValueBuilder&);
|
||||
ValueBuilder& operator=(const ValueBuilder&);
|
||||
};
|
||||
|
||||
}}// namespace epics::pvData
|
||||
|
||||
#endif // VALUEBUILDER_H
|
||||
@@ -61,3 +61,7 @@ TESTPROD_HOST += testFieldBuilder
|
||||
testFieldBuilder_SRCS += testFieldBuilder.cpp
|
||||
testHarness_SRCS += testFieldBuilder.cpp
|
||||
TESTS += testFieldBuilder
|
||||
|
||||
TESTPROD_HOST += testValueBuilder
|
||||
testValueBuilder_SRCS += testValueBuilder.cpp
|
||||
TESTS += testValueBuilder
|
||||
|
||||
154
testApp/pv/testValueBuilder.cpp
Normal file
154
testApp/pv/testValueBuilder.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/createRequest.h>
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
namespace {
|
||||
|
||||
void testBuild()
|
||||
{
|
||||
testDiag("testBuild()");
|
||||
pvd::ValueBuilder builder;
|
||||
builder.add<pvd::pvInt>("A", 42)
|
||||
.add<pvd::pvShort>("B", 43);
|
||||
|
||||
pvd::PVStructurePtr val(builder.buildPVStructure());
|
||||
|
||||
testOk1(val->getStructure()->getID()=="structure");
|
||||
{
|
||||
pvd::PVIntPtr A(val->getSubField<pvd::PVInt>("A"));
|
||||
testOk1(!!A && A->get()==42);
|
||||
}
|
||||
{
|
||||
pvd::PVShortPtr B(val->getSubField<pvd::PVShort>("B"));
|
||||
testOk1(!!B && B->get()==43);
|
||||
}
|
||||
}
|
||||
|
||||
void testSubStruct()
|
||||
{
|
||||
testDiag("testSubStruct()");
|
||||
pvd::PVStructurePtr val(pvd::ValueBuilder("foo")
|
||||
.add<pvd::pvInt>("A", 42)
|
||||
.addNested("X", pvd::structure, "bar")
|
||||
.add<pvd::pvInt>("Y", 111)
|
||||
.add<pvd::pvString>("Z", "hello world")
|
||||
.endNested()
|
||||
.add<pvd::pvInt>("B", 43)
|
||||
.buildPVStructure());
|
||||
|
||||
testOk1(val->getStructure()->getID()=="foo");
|
||||
testOk1(val->getSubFieldT<pvd::PVStructure>("X")->getStructure()->getID()=="bar");
|
||||
{
|
||||
pvd::PVIntPtr A(val->getSubField<pvd::PVInt>("A"));
|
||||
testOk1(!!A && A->get()==42);
|
||||
}
|
||||
{
|
||||
pvd::PVIntPtr B(val->getSubField<pvd::PVInt>("B"));
|
||||
testOk1(!!B && B->get()==43);
|
||||
}
|
||||
{
|
||||
pvd::PVIntPtr Y(val->getSubField<pvd::PVInt>("X.Y"));
|
||||
testOk1(!!Y && Y->get()==111);
|
||||
}
|
||||
{
|
||||
pvd::PVStringPtr Y(val->getSubField<pvd::PVString>("X.Z"));
|
||||
testOk1(!!Y && Y->get()=="hello world");
|
||||
}
|
||||
}
|
||||
|
||||
void testReplace()
|
||||
{
|
||||
testDiag("testReplace()");
|
||||
pvd::PVStructurePtr val(pvd::ValueBuilder("foo")
|
||||
.add<pvd::pvInt>("A", 42)
|
||||
.add<pvd::pvInt>("A", 43)
|
||||
.addNested("X", pvd::structure, "bar")
|
||||
.add<pvd::pvInt>("Y", 111)
|
||||
.endNested()
|
||||
.addNested("X", pvd::structure, "baz")
|
||||
.add<pvd::pvInt>("Y", 112)
|
||||
.endNested()
|
||||
.buildPVStructure());
|
||||
|
||||
testDiag("top id %s", val->getStructure()->getID().c_str());
|
||||
testOk1(val->getStructure()->getID()=="foo");
|
||||
testDiag("X id %s", val->getSubFieldT<pvd::PVStructure>("X")->getStructure()->getID().c_str());
|
||||
testOk1(val->getSubFieldT<pvd::PVStructure>("X")->getStructure()->getID()=="baz");
|
||||
{
|
||||
pvd::PVIntPtr A(val->getSubField<pvd::PVInt>("A"));
|
||||
testOk1(!!A && A->get()==43);
|
||||
}
|
||||
{
|
||||
pvd::PVIntPtr Y(val->getSubField<pvd::PVInt>("X.Y"));
|
||||
testOk1(!!Y && Y->get()==112);
|
||||
}
|
||||
}
|
||||
|
||||
void testAppend()
|
||||
{
|
||||
testDiag("testAppend()");
|
||||
|
||||
pvd::PVStructurePtr base(pvd::createRequest("field(foo)record[bar=5]"));
|
||||
std::cerr<<base<<"\n";
|
||||
|
||||
testOk1(!!base->getSubField<pvd::PVStructure>("field.foo"));
|
||||
testOk1(!base->getSubField<pvd::PVStructure>("field.other"));
|
||||
testOk1(base->getSubFieldT<pvd::PVString>("record._options.bar")->get()=="5");
|
||||
testOk1(!base->getSubField<pvd::PVStructure>("record._options.foo"));
|
||||
|
||||
pvd::PVStructurePtr mod(pvd::ValueBuilder(*base)
|
||||
.addNested("field")
|
||||
.addNested("other")
|
||||
.endNested()
|
||||
.endNested()
|
||||
.addNested("record")
|
||||
.addNested("_options")
|
||||
.add<pvd::pvInt>("bar", 4)
|
||||
.add<pvd::pvInt>("foo", 1)
|
||||
.endNested()
|
||||
.endNested()
|
||||
.buildPVStructure());
|
||||
std::cerr<<mod<<"\n";
|
||||
|
||||
testOk1(base->getField().get()!=mod->getField().get());
|
||||
testOk1(base->getField()!=mod->getField());
|
||||
|
||||
// base unchanged
|
||||
testOk1(!!base->getSubField<pvd::PVStructure>("field.foo"));
|
||||
testOk1(!base->getSubField<pvd::PVStructure>("field.other"));
|
||||
testOk1(base->getSubFieldT<pvd::PVString>("record._options.bar")->get()=="5");
|
||||
testOk1(!base->getSubField<pvd::PVStructure>("record._options.foo"));
|
||||
|
||||
testOk1(!!mod->getSubField<pvd::PVStructure>("field.foo"));
|
||||
testOk1(!!mod->getSubField<pvd::PVStructure>("field.other"));
|
||||
testOk1(mod->getSubFieldT<pvd::PVInt>("record._options.bar")->get()==4);
|
||||
testOk1(mod->getSubFieldT<pvd::PVInt>("record._options.foo")->get()==1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testValueBuilder)
|
||||
{
|
||||
testPlan(13);
|
||||
try {
|
||||
testBuild();
|
||||
testSubStruct();
|
||||
testReplace();
|
||||
testAppend();
|
||||
}catch(std::exception& e){
|
||||
PRINT_EXCEPTION(e);
|
||||
testAbort("Unexpected exception: %s", e.what());
|
||||
}
|
||||
return testDone();
|
||||
}
|
||||
Reference in New Issue
Block a user