NTTable refactored

This commit is contained in:
Matej Sekoranja
2014-08-21 13:28:52 +02:00
parent 327371151a
commit fdeda9dc97
4 changed files with 487 additions and 253 deletions

View File

@@ -5,148 +5,177 @@
* in file LICENSE that is included with this distribution.
*/
#include <algorithm>
#include <pv/nttable.h>
namespace epics { namespace pvData {
using namespace std;
using namespace epics::pvData;
using std::tr1::static_pointer_cast;
namespace epics { namespace nt {
bool NTTable::isNTTable(PVStructurePtr const & pvStructure)
namespace detail {
static NTFieldPtr ntField = NTField::get();
NTTableBuilder::shared_pointer NTTableBuilder::add(
std::string const & name, epics::pvData::ScalarType scalarType
)
{
NTFieldPtr ntfield = NTField::get();
PVStringArrayPtr pvLabel = static_pointer_cast<PVStringArray>
(pvStructure->getScalarArrayField("label",pvString));
if(pvLabel.get()==NULL) return false;
size_t nfields = pvLabel->getLength();
size_t nextra = 1; // label is 1 field
PVFieldPtr pvField = pvStructure->getSubField("function");
if(pvField.get()!=NULL
&& pvStructure->getStringField("function").get()!=NULL) nextra++;
pvField = pvStructure->getSubField("timeStamp");
if(pvField!=0 && ntfield->isTimeStamp(pvField->getField())) {
nextra++;
}
pvField = pvStructure->getSubField("alarm");
if(pvField.get()!=NULL && ntfield->isAlarm(pvField->getField())) {
nextra++;
}
if(nfields!=(pvStructure->getStructure()->getNumberFields()-nextra)) {
if (std::find(labels.begin(), labels.end(), name) != labels.end())
throw std::runtime_error("duplicate column name");
labels.push_back(name);
types.push_back(scalarType);
return shared_from_this();
}
StructureConstPtr NTTableBuilder::createStructure()
{
FieldBuilderPtr builder = getFieldCreate()->createFieldBuilder();
FieldBuilderPtr nestedBuilder =
builder->
setId(NTTable::URI)->
addArray("labels", pvString)->
addNestedStructure("value");
vector<string>::size_type len = labels.size();
for (vector<string>::size_type i = 0; i < len; i++)
nestedBuilder->addArray(labels[i], types[i]);
builder = nestedBuilder->endNested();
if (descriptor)
builder->add("descriptor", pvString);
if (alarm)
builder->add("alarm", ntField->createAlarm());
if (timeStamp)
builder->add("timeStamp", ntField->createTimeStamp());
StructureConstPtr s = builder->createStructure();
reset();
return s;
}
NTTableBuilder::shared_pointer NTTableBuilder::addDescriptor()
{
descriptor = true;
return shared_from_this();
}
NTTableBuilder::shared_pointer NTTableBuilder::addAlarm()
{
alarm = true;
return shared_from_this();
}
NTTableBuilder::shared_pointer NTTableBuilder::addTimeStamp()
{
timeStamp = true;
return shared_from_this();
}
PVStructurePtr NTTableBuilder::createPVStructure()
{
PVStringArray::svector l;
l.resize(labels.size());
std::copy(labels.begin(), labels.end(), l.begin());
PVStructurePtr s = getPVDataCreate()->createPVStructure(createStructure());
s->getSubField<PVStringArray>("labels")->replace(freeze(l));
return s;
}
NTTablePtr NTTableBuilder::create()
{
return NTTablePtr(new NTTable(createPVStructure()));
}
NTTableBuilder::NTTableBuilder()
{
reset();
}
void NTTableBuilder::reset()
{
labels.clear();
types.clear();
descriptor = false;
alarm = false;
timeStamp = false;
}
}
const std::string NTTable::URI("uri:ev4:nt/2012/pwd:NTTable");
bool NTTable::is_a(StructureConstPtr const & structure)
{
return structure->getID() == URI;
}
NTTableBuilderPtr NTTable::createBuilder()
{
return NTTableBuilderPtr(new detail::NTTableBuilder());
}
bool NTTable::attachTimeStamp(PVTimeStamp &pvTimeStamp) const
{
PVStructurePtr ts = getTimeStamp();
if (ts)
return pvTimeStamp.attach(ts);
else
return false;
}
FieldConstPtrArray fields = pvStructure->getStructure()->getFields();
for(size_t i=0; i<nfields; i++) {
FieldConstPtr field = fields[i+nextra];
Type type = field->getType();
if(type!=scalarArray && type!=structureArray) return false;
}
return true;
}
NTTablePtr NTTable::create(
bool hasFunction,bool hasTimeStamp, bool hasAlarm,
shared_vector<std::string> const & valueNames,
FieldConstPtrArray const &valueFields)
bool NTTable::attachAlarm(PVAlarm &pvAlarm) const
{
StandardFieldPtr standardField = getStandardField();
size_t nfields = 1;
if(hasFunction) nfields++;
if(hasTimeStamp) nfields++;
if(hasAlarm) nfields++;
nfields += valueFields.size();
FieldCreatePtr fieldCreate = getFieldCreate();
PVDataCreatePtr pvDataCreate = getPVDataCreate();
FieldConstPtrArray fields(nfields);
StringArray names(nfields);
size_t ind = 0;
if(hasFunction) {
names[ind] = "function";
fields[ind++] = fieldCreate->createScalar(pvString);
}
if(hasTimeStamp) {
names[ind] = "timeStamp";
fields[ind++] = standardField->timeStamp();
}
if(hasAlarm) {
names[ind] = "alarm";
fields[ind++] = standardField->alarm();
}
names[ind] = "label";
fields[ind++] = fieldCreate->createScalarArray(pvString);
size_t numberValues = valueNames.size();
for(size_t i=0; i<numberValues ; i++) {
names[ind] = valueNames[i];
fields[ind++] = valueFields[i];
}
StructureConstPtr st = fieldCreate->createStructure(names,fields);
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(st);
PVStringArrayPtr pvLabel = static_pointer_cast<PVStringArray>
(pvStructure->getScalarArrayField("label",pvString));
shared_vector<std::string> xxx(numberValues);
for(size_t i=0; i<numberValues; ++i) xxx[i] = valueNames[i];
shared_vector<const std::string> data(freeze(xxx));
pvLabel->replace(data);
return NTTablePtr(new NTTable(pvStructure));
PVStructurePtr al = getAlarm();
if (al)
return pvAlarm.attach(al);
else
return false;
}
NTTablePtr NTTable::clone(PVStructurePtr const & pv)
PVStructurePtr NTTable::getPVStructure() const
{
PVStructurePtr pvStructure = getPVDataCreate()->createPVStructure(pv);
return NTTablePtr(new NTTable(pvStructure));
return pvNTTable;
}
NTTable::NTTable(PVStructurePtr const & pvStructure)
: pvNTTable(pvStructure),
offsetFields(1)
PVStringPtr NTTable::getDescriptor() const
{
NTFieldPtr ntfield = NTField::get();
PVScalarArrayPtr pvScalarArray
= pvStructure->getScalarArrayField("label",pvString);
pvLabel = static_pointer_cast<PVStringArray>(pvScalarArray);
PVFieldPtr pvField = pvStructure->getSubField("function");
if(pvField.get()!=NULL) {
offsetFields++;
pvFunction = pvStructure->getStringField("function");
}
pvField = pvStructure->getSubField("timeStamp");
if(pvField.get()!=NULL && ntfield->isTimeStamp(pvField->getField())) {
offsetFields++;
pvTimeStamp = static_pointer_cast<PVStructure>(pvField);
}
pvField = pvStructure->getSubField("alarm");
if(pvField.get()!=NULL && ntfield->isAlarm(pvField->getField())) {
offsetFields++;
pvAlarm = static_pointer_cast<PVStructure>(pvField);
}
return pvNTTable->getSubField<PVString>("descriptor");
}
void NTTable::attachTimeStamp(PVTimeStamp &pv)
PVStructurePtr NTTable::getTimeStamp() const
{
if(pvTimeStamp.get()==NULL) return;
pv.attach(pvTimeStamp);
return pvNTTable->getSubField<PVStructure>("timeStamp");
}
void NTTable::attachAlarm(PVAlarm &pv)
PVStructurePtr NTTable::getAlarm() const
{
if(pvAlarm.get()==NULL) return;
pv.attach(pvAlarm);
return pvNTTable->getSubField<PVStructure>("alarm");
}
size_t NTTable::getNumberValues()
PVStringArrayPtr NTTable::getLabels() const
{
return pvLabel->getLength();
return pvNTTable->getSubField<PVStringArray>("labels");
}
FieldConstPtr NTTable::getField(size_t index)
PVFieldPtr NTTable::getColumn(std::string const & columnName) const
{
FieldConstPtrArray fields = pvNTTable->getStructure()->getFields();
return fields[index + offsetFields];
return pvNTTable->getSubField("value." + columnName);
}
PVFieldPtr NTTable::getPVField(size_t index)
{
PVFieldPtrArray pvFields = pvNTTable->getPVFields();
return pvFields[index+offsetFields];
}
NTTable::NTTable(PVStructurePtr const & pvStructure) :
pvNTTable(pvStructure)
{}
}}

View File

@@ -9,107 +9,197 @@
#include <pv/ntfield.h>
namespace epics { namespace pvData {
#include <vector>
#include <string>
/**
* Convenience Class for NTTable
* @author mrk
*
*/
namespace epics { namespace nt {
class NTTable;
typedef std::tr1::shared_ptr<NTTable> NTTablePtr;
namespace detail {
/**
* Interface for in-line creating of NTTable.
* One instance can be used to create multiple instances.
* An instance of this object must not be used concurrently (an object has a state).
* @author mse
*/
class epicsShareClass NTTableBuilder :
public std::tr1::enable_shared_from_this<NTTableBuilder>
{
public:
POINTER_DEFINITIONS(NTTableBuilder);
/**
* Add a column of given {@code Scalar} type.
* @param name name of the column.
* @param scalarType column type, a scalar array.
* @return this instance of a {@code NTTableBuilder}.
*/
shared_pointer add(std::string const & name, epics::pvData::ScalarType scalarType);
/**
* Add descriptor field to the NTTable.
* @return this instance of a {@code NTTableBuilder}.
*/
shared_pointer addDescriptor();
/**
* Add alarm structure to the NTTable.
* @return this instance of a {@code NTTableBuilder}.
*/
shared_pointer addAlarm();
/**
* Add timeStamp structure to the NTTable.
* @return this instance of a {@code NTTableBuilder}.
*/
shared_pointer addTimeStamp();
/**
* Create a {@code Structure} that represents NTTable.
* This resets this instance state and allows new instance to be created.
* @return a new instance of a {@code Structure}.
*/
epics::pvData::StructureConstPtr createStructure();
/**
* Create a {@code PVStructure} that represents NTTable.
* This resets this instance state and allows new {@code instance to be created.
* @return a new instance of a {@code PVStructure}
*/
epics::pvData::PVStructurePtr createPVStructure();
/**
* Create a {@code NTTable} instance.
* This resets this instance state and allows new {@code instance to be created.
* @return a new instance of a {@code NTTable}
*/
NTTablePtr create();
private:
NTTableBuilder();
void reset();
std::vector<std::string> labels;
std::vector<epics::pvData::ScalarType> types;
bool descriptor;
bool alarm;
bool timeStamp;
friend class ::epics::nt::NTTable;
};
}
typedef std::tr1::shared_ptr<detail::NTTableBuilder> NTTableBuilderPtr;
/**
* Convenience Class for NTTable
* @author mrk
*/
class NTTable
{
public:
POINTER_DEFINITIONS(NTTable);
static const std::string URI;
/**
* Is the pvStructure an NTTable.
* @param pvStructure The pvStructure to test.
* @return (false,true) if (is not, is) an NTNameValuePair.
* Is the structure an NTTable.
* @param structure The structure to test.
* @return (false,true) if (is not, is) an NTTable.
*/
static bool isNTTable(PVStructurePtr const &pvStructure);
static bool is_a(epics::pvData::StructureConstPtr const & structure);
/**
* Create an NTTable pvStructure.
* @param hasFunction Create a PVString field named function.
* @param hasTimeStamp Create a timeStamp structure field.
* @param hasAlarm Create an alarm structure field.
* @param numberValues The number of fields that follow the label field.
* @param valueFields The fields that follow the label field.
* @return an NTTablePtr
* Create a NTTable builder instance.
* @return builder instance.
*/
static NTTablePtr create(
bool hasFunction,bool hasTimeStamp, bool hasAlarm,
shared_vector<std::string> const & valueNames,
FieldConstPtrArray const &valueFields);
static NTTablePtr clone(PVStructurePtr const &);
static NTTableBuilderPtr createBuilder();
/**
* Destructor
* Destructor.
*/
~NTTable() {}
/**
* Get the function field.
* @return The pvString or null if no function field.
*/
PVStringPtr getFunction() {return pvFunction;}
/**
* Attach a pvTimeStamp.
* @param pvTimeStamp The pvTimeStamp that will be attached.
* Does nothing if no timeStamp
* Does nothing if no timeStamp.
* @return true if the operation was successfull (i.e. this instance has a timeStamp field), otherwise false.
*/
void attachTimeStamp(PVTimeStamp &pvTimeStamp);
bool attachTimeStamp(epics::pvData::PVTimeStamp &pvTimeStamp) const;
/**
* Attach an pvAlarm.
* @param pvAlarm The pvAlarm that will be attached.
* Does nothing if no alarm
* Does nothing if no alarm.
* @return true if the operation was successfull (i.e. this instance has a timeStamp field), otherwise false.
*/
void attachAlarm(PVAlarm &pvAlarm);
bool attachAlarm(epics::pvData::PVAlarm &pvAlarm) const;
/**
* Get the pvStructure.
* @return PVStructurePtr.
*/
PVStructurePtr getPVStructure(){return pvNTTable;}
epics::pvData::PVStructurePtr getPVStructure() const;
/**
* Get the descriptor field.
* @return The pvString or null if no function field.
*/
epics::pvData::PVStringPtr getDescriptor() const;
/**
* Get the timeStamp.
* @return PVStructurePtr which may be null.
*/
PVStructurePtr getTimeStamp(){return pvTimeStamp;}
epics::pvData::PVStructurePtr getTimeStamp() const;
/**
* Get the alarm.
* @return PVStructurePtr which may be null.
*/
PVStructurePtr getAlarm() {return pvAlarm;}
epics::pvData::PVStructurePtr getAlarm() const;
/**
* Get the label field.
* @return The pvStringArray for the label.
* Get the labels field.
* @return The pvStringArray for the labels.
*/
PVStringArrayPtr getLabel() {return pvLabel;}
epics::pvData::PVStringArrayPtr getLabels() const;
/**
* Get the the number of fields that follow the label field.
* @return The number of fields.
*/
size_t getNumberValues();
/**
* Get the Field for a field that follows the label field.
* @param index The index of the field desired.
* @return The FieldConstPtr for the field.
*/
FieldConstPtr getField(size_t index);
/**
* Get the PVField for a field that follows the label field.
* @param index The index of the field desired.
* Get the PVField (column) for a field that follows the label field.
* @param columnName The name of the column.
* @return The PVFieldPtr for the field.
*/
PVFieldPtr getPVField(size_t index);
epics::pvData::PVFieldPtr getColumn(std::string const & columnName) const;
/**
* Get the PVField (column) for a field that follows the label field of a specified type (e.g. PVDoubleArray).
* @param columnName The name of the column.
* @return The <PVT> field.
*/
template<typename PVT>
std::tr1::shared_ptr<PVT> getColumn(std::string const & columnName) const
{
epics::pvData::PVFieldPtr pvField = getColumn(columnName);
if (pvField.get())
return std::tr1::dynamic_pointer_cast<PVT>(pvField);
else
return std::tr1::shared_ptr<PVT>();
}
private:
NTTable(PVStructurePtr const & pvStructure);
PVStructurePtr pvNTTable;
PVStringPtr pvFunction;
PVStructurePtr pvTimeStamp;
PVStructurePtr pvAlarm;
PVStringArrayPtr pvLabel;
size_t offsetFields;
NTTable(epics::pvData::PVStructurePtr const & pvStructure);
epics::pvData::PVStructurePtr pvNTTable;
friend class detail::NTTableBuilder;
};
}}

View File

@@ -2,6 +2,8 @@ TOP=../..
include $(TOP)/configure/CONFIG
PROD_LIBS += nt pvData Com
PROD_HOST += ntfieldTest
ntfieldTest_SRCS += ntfieldTest.cpp
ntfieldTest_LIBS += nt pvData Com
@@ -10,9 +12,11 @@ PROD_HOST += ntnameValueTest
ntnameValueTest_SRCS += ntnameValueTest.cpp
ntnameValueTest_LIBS += nt pvData Com
PROD_HOST += nttableTest
nttableTest_SRCS += nttableTest.cpp
nttableTest_LIBS += nt pvData Com
TESTPROD_HOST += nttableTest
nttableTest_SRCS = nttableTest.cpp
TESTS += nttableTest
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES
#----------------------------------------

View File

@@ -10,92 +10,203 @@
* Author: Marty Kraimer
*/
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <ctime>
#include <list>
#include <iostream>
#include <epicsAssert.h>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <pv/nt.h>
using namespace epics::nt;
using namespace epics::pvData;
using std::tr1::static_pointer_cast;
using std::string;
using std::cout;
using std::endl;
using std::tr1::dynamic_pointer_cast;
static FieldCreatePtr fieldCreate = getFieldCreate();
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
static NTFieldPtr ntField = NTField::get();
static PVNTFieldPtr pvntField = PVNTField::get();
static void test(FILE * fd)
void test_builder()
{
size_t n = 2;
FieldConstPtrArray fields(n);
shared_vector<string> names(n);
names[0] = "position";
names[1] = "alarms";
fields[0] = fieldCreate->createScalarArray(pvDouble);
fields[1] = ntField->createAlarmArray();
NTTablePtr ntTable = NTTable::create(
true,true,true,names,fields);
PVStructurePtr pvStructure = ntTable->getPVStructure();
cout << *pvStructure << endl;
cout << *pvStructure->getStructure() << endl;
PVDoubleArrayPtr pvPositions
= static_pointer_cast<PVDoubleArray>(ntTable->getPVField(0));
shared_vector<double> positions(2);
positions[0] = 1.0;
positions[1] = 2.0;
pvPositions->replace(freeze(positions));
PVStructureArrayPtr pvAlarms
= static_pointer_cast<PVStructureArray>(ntTable->getPVField(1));
testDiag("test_builder");
NTTableBuilderPtr builder = NTTable::createBuilder();
testOk(builder.get() != 0, "Got builder");
StructureConstPtr structure = builder->
add("column0", pvDouble)->
add("column1", pvString)->
add("column2", pvInt)->
addDescriptor()->
addAlarm()->
addTimeStamp()->
createStructure();
testOk1(structure.get() != 0);
if (!structure)
return;
testOk1(NTTable::is_a(structure));
testOk1(structure->getID() == NTTable::URI);
testOk1(structure->getNumberFields() == 5);
testOk1(structure->getField("labels").get() != 0);
testOk1(structure->getField("value").get() != 0);
testOk1(structure->getField("descriptor").get() != 0);
testOk1(structure->getField("alarm").get() != 0);
testOk1(structure->getField("timeStamp").get() != 0);
StructureConstPtr s = dynamic_pointer_cast<const Structure>(structure->getField("value"));
#define TEST_COLUMN(name, type) \
testOk(s.get() != 0 && \
s->getField(name).get() != 0 && \
dynamic_pointer_cast<const ScalarArray>(s->getField(name)).get() != 0 && \
dynamic_pointer_cast<const ScalarArray>(s->getField(name))->getElementType() == type, \
name " check");
TEST_COLUMN("column0", pvDouble);
TEST_COLUMN("column1", pvString);
TEST_COLUMN("column2", pvInt);
#undef TEST_COLUMN
std::cout << *structure << std::endl;
// duplicate test
try
{
structure = builder->
add("column0", pvDouble)->
add("column0", pvString)->
createStructure();
testFail("duplicate column name");
} catch (std::runtime_error &) {
testPass("duplicate column name");
}
}
void test_labels()
{
testDiag("test_labels");
NTTableBuilderPtr builder = NTTable::createBuilder();
testOk(builder.get() != 0, "Got builder");
PVStructurePtr pvStructure = builder->
add("column0", pvDouble)->
add("column1", pvString)->
add("column2", pvInt)->
createPVStructure();
testOk1(pvStructure.get() != 0);
if (!pvStructure)
return;
std::cout << *pvStructure << std::endl;
PVStringArrayPtr labels = pvStructure->getSubField<PVStringArray>("labels");
testOk1(labels.get() != 0);
testOk1(labels->getLength() == 3);
PVStringArray::const_svector l(labels->view());
testOk1(l[0] == "column0");
testOk1(l[1] == "column1");
testOk1(l[2] == "column2");
}
void test_nttable()
{
testDiag("test_nttable");
NTTableBuilderPtr builder = NTTable::createBuilder();
testOk(builder.get() != 0, "Got builder");
NTTablePtr ntTable = builder->
add("column0", pvDouble)->
add("column1", pvString)->
add("column2", pvInt)->
addDescriptor()->
addAlarm()->
addTimeStamp()->
create();
testOk1(ntTable.get() != 0);
testOk1(ntTable->getPVStructure().get() != 0);
testOk1(ntTable->getDescriptor().get() != 0);
testOk1(ntTable->getAlarm().get() != 0);
testOk1(ntTable->getTimeStamp().get() != 0);
testOk1(ntTable->getLabels().get() != 0);
testOk1(ntTable->getColumn<PVDoubleArray>("column0").get() != 0);
testOk1(ntTable->getColumn<PVStringArray>("column1").get() != 0);
testOk1(ntTable->getColumn<PVIntArray>("column2").get() != 0);
testOk1(ntTable->getColumn("invalid").get() == 0);
//
// example how to set column values
//
PVIntArray::svector newValues;
newValues.push_back(1);
newValues.push_back(2);
newValues.push_back(8);
PVIntArrayPtr intColumn = ntTable->getColumn<PVIntArray>("column2");
intColumn->replace(freeze(newValues));
//
// example how to get column values
//
PVIntArray::const_svector values(intColumn->view());
testOk1(values.size() == 3);
testOk1(values[0] == 1);
testOk1(values[1] == 2);
testOk1(values[2] == 8);
//
// timeStamp ops
//
PVTimeStamp pvTimeStamp;
if (ntTable->attachTimeStamp(pvTimeStamp))
{
testPass("timeStamp attach");
// example how to set current time
TimeStamp ts;
ts.getCurrent();
pvTimeStamp.set(ts);
// example how to get EPICS time
TimeStamp ts2;
pvTimeStamp.get(ts2);
testOk1(ts2.getEpicsSecondsPastEpoch() != 0);
}
else
testFail("timeStamp attach fail");
//
// alarm ops
//
PVAlarm pvAlarm;
Alarm alarm;
shared_vector<PVStructurePtr> palarms(n);
for(size_t i=0; i<n; i++) {
palarms[i] = pvntField->createAlarm();
pvAlarm.attach(palarms[i]);
alarm.setMessage("test");
alarm.setSeverity(majorAlarm);
alarm.setStatus(clientStatus);
if (ntTable->attachAlarm(pvAlarm))
{
testPass("alarm attach");
// example how to set an alarm
Alarm alarm;
alarm.setStatus(deviceStatus);
alarm.setSeverity(minorAlarm);
alarm.setMessage("simulation alarm");
pvAlarm.set(alarm);
}
pvAlarms->replace(freeze(palarms));
shared_vector<string> labels(n);
labels[0] = pvPositions->getFieldName();
labels[1] = pvAlarms->getFieldName();
PVStringArrayPtr label = ntTable->getLabel();
label->replace(freeze(labels));
PVStringPtr function = ntTable->getFunction();
function->put("test");
ntTable->attachAlarm(pvAlarm);
alarm.setMessage("test alarm");
alarm.setSeverity(majorAlarm);
alarm.setStatus(clientStatus);
pvAlarm.set(alarm);
PVTimeStamp pvTimeStamp;
ntTable->attachTimeStamp(pvTimeStamp);
TimeStamp timeStamp(1000,1000,10);
pvTimeStamp.set(timeStamp);
cout << *pvStructure << endl;
assert(NTTable::isNTTable(pvStructure));
else
testFail("alarm attach fail");
//
// set descriptor
//
ntTable->getDescriptor()->put("This is a test NTTable");
// dump NTTable
std::cout << *ntTable->getPVStructure() << std::endl;
}
MAIN(testNTTable) {
testPlan(39);
test_builder();
test_labels();
test_nttable();
return testDone();
}
int main(int argc,char *argv[])
{
char *fileName = 0;
if(argc>1) fileName = argv[1];
FILE * fd = stdout;
if(fileName!=0 && fileName[0]!=0) {
fd = fopen(fileName,"w+");
}
test(fd);
return(0);
}