This product is made available subject to acceptance of the EPICS open source license.
Since the last version of this document the following changes have been made to the proposed interface definitionsi for PVValueArray:
The stream operator++ methods were changed so that they only appear in PVField. All the complexity should be hidden in the implementation and, perhaps like Java, in the convert facility.
A few minor changes were made because of mistakes in documenting existing API descriptions.
This is the documentation for pvData.h as defined by pvDataCPP-md. When complete it will be merged into pvDataCPP.html. This document proposes an implementation of the PVXXX interfaces that are different than the existing pvDataCPP-md interfaces. See the next section for a comparison of the four interface descriptions. The main reason for proposing a different definition is the primary purpose for pvData:
pvData (Process Variable Data) defines and implements an efficent way to store, access, and communicate memory resident data structures.This statement appears as the first sentence of pvDataJava.html. A few sentances later the document makes clear that communication includes network communication. Thus pvData provides an interface for network accessible structured data. If the interfaces for C++ and Java are similar then someone who understands the interface in one of the languages and knows both languages will quickly understand the interface of the other language. With only a few exceptions the naming and other conventions do not follow the C++ standard library conventions. The pvData.h API is similar to the definition for Java. The differences are mainly related to language differences. Some differences are:
There is one big difference from the existing Java API: The method PVValueArray::get. As an example the Java definition for PVDoubleArray is currently:
int get(int offset, int length, DoubleArrayData data);
This document assumes this will be replaced by:
double[] get();
The existing version allowed the data source to provide the array in chunks. The new version assumes that the data source always provides contiguous storage for the entire raw array. If this is accepted it simplifies a lot of code.
Three topics not discussed in this document are:
PVScalarArrayPtr createPVScalarArray(const PVScalarArray &, size_t offset, size_t length);
This will share the raw data array but allow a new window.
The following compares the definitions of the following: PVField, PVScalar and extensions, PVArray and extensions. PVStructureArray is not discussed.
This is the base for all the PVXXX iterfaces. It provides basic methods for allowing network transfer and for traversing structured data. The pvDataJava and pvDataCPP definitions are similar. pvDataCPP-md added method getFullName. The proposed interface is like the existing pvDataCPP except that the dumpValue method is replaced by the stream operator<<.
interface PVField extends Requester, Serializable {
std::string getFieldName();
void setRequester(Requester requester);
int getFieldOffset();
int getNextFieldOffset();
int getNumberFields();
PVAuxInfo getPVAuxInfo();
boolean isImmutable();
void setImmutable();
Field getField();
PVStructure getParent();
void renameField(std::string newName);
void postPut(); // calls PVRecordField.postPut if this is a field of a record
void setPostHandler(PostHandler postHandler);
void toString(StringBuilder buf);
void toString(StringBuilder buf,int indentLevel);
std::string toString();
}
class PVField
: virtual public Serializable,
public std::tr1::enable_shared_from_this<PVField>
{
public:
POINTER_DEFINITIONS(PVField);
virtual ~PVField();
virtual void message(std::string message,MessageType messageType);
std::string getFieldName() const ;
virtual void setRequester(RequesterPtr const &prequester);
std::size_t getFieldOffset() const;
std::size_t getNextFieldOffset() const;
std::size_t getNumberFields() const;
PVAuxInfoPtr & getPVAuxInfo()
bool isImmutable() const;
virtual void setImmutable();
const FieldConstPtr & getField() const ;
PVStructure * getParent() const
void replacePVField(const PVFieldPtr& newPVField);
void renameField(std::string const &newName);
void postPut() ;
void setPostHandler(PostHandlerPtr const &postHandler);
virtual bool equals(PVField &pv);
virtual void toString(StringBuilder buf) ;
virtual void toString(StringBuilder buf,int indentLevel);
virtual std::ostream& dumpValue(std::ostream& o) const =0;
...
}
std::ostream& operator<<(std::ostream& o, const PVFieldPtr & f);
class PVField
: virtual public Serializable,
public std::tr1::enable_shared_from_this<PVField>
{
public:
POINTER_DEFINITIONS(PVField);
virtual ~PVField();
virtual void message(std::string message,MessageType messageType);
std::string getFieldName() const ;
virtual void setRequester(RequesterPtr const &prequester);
std::size_t getFieldOffset() const;
std::size_t getNextFieldOffset() const;
std::size_t getNumberFields() const;
PVAuxInfoPtr & getPVAuxInfo()
bool isImmutable() const;
virtual void setImmutable();
const FieldConstPtr & getField() const ;
PVStructure * getParent() const
void replacePVField(const PVFieldPtr& newPVField);
void renameField(std::string const &newName);
void postPut() ;
void setPostHandler(PostHandlerPtr const &postHandler);
virtual bool equals(PVField &pv);
virtual void toString(StringBuilder buf) ;
virtual void toString(StringBuilder buf,int indentLevel);
std::ostream& dumpValue(std::ostream& o) const;
// not in pvDataCPP
std::string getFullName() const;
...
}
std::ostream& operator<<(std::ostream& o, const PVFieldPtr & f);
class PVField : virtual public Serializable, public std::tr1::enable_shared_from_this{ public: POINTER_DEFINITIONS(PVField); virtual ~PVField(); virtual void message(std::string message,MessageType messageType); const std::string& getFieldName() const; virtual void setRequester(RequesterPtr const &prequester); std::size_t getFieldOffset() const; std::size_t getNextFieldOffset() const; std::size_t getNumberFields() const; PVAuxInfoPtr & getPVAuxInfo(); bool isImmutable() const; void setImmutable(); const FieldConstPtr & getField() const; PVStructure * getParent() const; void replacePVField(const PVFieldPtr& newPVField); void renameField(std::string const & newName); void postPut(); void setPostHandler(PostHandlerPtr const &postHandler); virtual bool equals(PVField &pv); void toString(StringBuilder buf) ; void toString(StringBuilder buf,int indentLevel); std::ostream& operator<<(std::ostream& o) const; ... };
The Java and pvDataCPP versions differ in that Java has an interface definition for each scalar type, i. e. PVBoolean, ..., PVString, and the CPP versions provide a template PVValue for the implementation.
pvDataCPP-md differs from pvDataCPP in that it implements three additional methods:
The proposed version is like the pvDataCPP version except for dumpValue and the stream interators.
interface PVScalar extends PVField {
Scalar getScalar();
}
interface PVBoolean extends PVScalar {
boolean get();
void put(boolean value);
}
interface PVByte extends PVScalar {
byte get();
void put(byte value);
}
...
interface PVDouble extends PVScalar {
double get();
void put(double value);
}
interface PVString extends PVScalar, SerializableArray {
std::string get();
void put(std::string value);
}
class PVScalar : public PVField {
public:
POINTER_DEFINITIONS(PVScalar);
virtual ~PVScalar();
typedef PVScalar &reference;
typedef const PVScalar& const_reference;
const ScalarConstPtr getScalar() const ;
...
}
template<typename T>
class PVScalarValue : public PVScalar {
public:
POINTER_DEFINITIONS(PVScalarValue);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
virtual ~PVScalarValue() {}
virtual T get() const = 0;
virtual void put(T value) = 0;
std::ostream& dumpValue(std::ostream& o) const
void operator>>=(T& value) const;
void operator<<=(T value);
...
}
typedef PVScalarValue PVBoolean;
typedef PVScalarValue PVByte;
...typedef PVScalarValue PVDouble;
typedef std::tr1::shared_ptr PVBooleanPtr;
typedef std::tr1::shared_ptr PVBytePtr;
...
typedef std::tr1::shared_ptr PVDoublePtr;
// PVString is special case, since it implements SerializableArray
class PVString : public PVScalarValue<std::string>, SerializableArray {
public:
virtual ~PVString() {}
...
};
class PVScalar : public PVField {
public:
POINTER_DEFINITIONS(PVScalar);
virtual ~PVScalar();
typedef PVScalar &reference;
typedef const PVScalar& const_reference;
const ScalarConstPtr getScalar() const ;
// not in pvDataCPP
template<ScalarType ID>
inline typename ScalarTypeTraits<ID>::type getAs() const;
virtual void getAs(void *, ScalarType) const = 0;
template<ScalarType ID>
inline void putFrom(typename ScalarTypeTraits<ID>::type val)
virtual void putFrom(const void *, ScalarType) = 0;
virtual void assign(const PVScalar&) = 0;
...
}
template<typename T>
class PVScalarValue : public PVScalar {
public:
POINTER_DEFINITIONS(PVScalarValue);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
virtual ~PVScalarValue() {}
virtual T get() const = 0;
virtual void put(T value) = 0;
std::ostream& dumpValue(std::ostream& o) const
void operator>>=(T& value) const;
void operator<<=(T value);
// not in pvDataCPP
static const ScalarType typeCode;
...
}
typedef PVScalarValue PVBoolean;
typedef PVScalarValue PVByte;
...typedef PVScalarValue PVDouble;
typedef std::tr1::shared_ptr PVBooleanPtr;
typedef std::tr1::shared_ptr PVBytePtr;
...
typedef std::tr1::shared_ptr PVDoublePtr;
// PVString is special case, since it implements SerializableArray
class PVString : public PVScalarValue<std::string>, SerializableArray {
public:
virtual ~PVString() {}
...
};
class PVScalar : public PVField {
public:
POINTER_DEFINITIONS(PVScalar);
virtual ~PVScalar();
typedef PVScalar &reference;
typedef const PVScalar& const_reference;
const ScalarConstPtr getScalar() const;
...
};
template<typename T>
class PVScalarValue : public PVScalar {
public:
POINTER_DEFINITIONS(PVScalarValue);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
virtual ~PVScalarValue() {}
virtual T get() const = 0;
virtual void put(T value) = 0;
void operator>>=(T& value) const;
void operator<<=(T value);
...
};
typedef PVScalarValue<uint8> PVBoolean;
typedef PVScalarValue<int8> PVByte;
typedef PVScalarValue<double> PVDouble;
...
typedef std::tr1::shared_ptr<PVBoolean> PVBooleanPtr;
typedef std::tr1::shared_ptr<PVByte> PVBytePtr;
...
typedef std::tr1::shared_ptr<PVDouble> PVDoublePtr;
// PVString is special case, since it implements SerializableArray
class PVString : public PVScalarValue<std::string>, SerializableArray {
public:
virtual ~PVString() {}
...
};
The Java and pvDataCPP versions differ in that Java has an interface definition for each scalarArray type, i. e. PVBooleanArray, ..., PVStringArray, and the CPP versions provide a template PVValueArray for the implementation.
pvDataCPP-md differs from pvDataCPP in that it implements additional methods:
The proposed version is differs from pvJava, pvDataCPP, and pvCPP-md. It is like the Java version if the Java get method is simplified as discussed above. For example PVDoubleArray::get becomes:
double[] get();
The corresponding C++ version becomes:
const svector & get();
const const_svector & get() const;
The remaining difference is that dumpValue is replaced by the stream operator<<.
The main difference from the pvDataJava version is that PVValueArray "wraps" shared_vector. Thus shared_vector takes the place of the raw arrays in Java. This allows the C++ interface to be more similar to Java.
The main difference from the pvDataCPP-md version is that it does not implement the extra methods and allows the client access to the shared_vector. The client is then able to perform C++ specific things to the data. BUT it also means that if the client modifies the shared_vector the client is also responsible for ensuring that the immutable and capacity related features of PVField and PVArray are respected and the postPut is properly handled.
Note that two get methods exist. One allows write access to the raw data and the other doesn't/
interface PVArray extends PVField, SerializableArray {
int getLength();
void setLength(int length);
int getCapacity();
void setCapacity(int length);
boolean isCapacityMutable();
void setCapacityMutable(boolean isMutable);
}
interface PVScalarArray extends PVArray {
ScalarArray getScalarArray();
}
For each scalar type an associated array data interface is defined. Each has a get and put method. For example:
public class DoubleArrayData {
public double[] data;
public int offset;
}
interface PVDoubleArray extends PVArray {
int get(int offset, int len, DoubleArrayData data);
int put(int offset,int len, double[] from, int fromOffset);
void shareData(double[] from);
}
class PVArray : public PVField, public SerializableArray {
public:
POINTER_DEFINITIONS(PVArray);
virtual ~PVArray();
virtual void setImmutable();
std::size_t getLength() const;
virtual void setLength(std::size_t length);
std::size_t getCapacity() const;
bool isCapacityMutable() const;
void setCapacityMutable(bool isMutable);
virtual void setCapacity(std::size_t capacity) = 0;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const = 0;
...
};
template<typename T>
class PVArrayData {
private:
std::vector<T> init;
public:
POINTER_DEFINITIONS(PVArrayData);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
std::vector<T> & data;
std::size_t offset;
PVArrayData()
: data(init)
{}
};
class PVScalarArray : public PVArray {
public:
POINTER_DEFINITIONS(PVScalarArray);
virtual ~PVScalarArray();
typedef PVScalarArray &reference;
typedef const PVScalarArray& const_reference;
const ScalarArrayConstPtr getScalarArray() const ;
...
}
template<typename T>
class PVValueArray : public PVScalarArray {
public:
POINTER_DEFINITIONS(PVValueArray);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef PVArrayData<T> ArrayDataType;
typedef std::vector<T> vector;
typedef const std::vector<T> const_vector;
typedef std::tr1::shared_ptr<vector> shared_vector;
typedef PVValueArray & reference;
typedef const PVValueArray & const_reference;
virtual ~PVValueArray() {}
virtual std::size_t get(
std::size_t offset, std::size_t length, ArrayDataType &data) = 0;
virtual std::size_t put(std::size_t offset,
std::size_t length, const_pointer from, std::size_t fromOffset) = 0;
virtual std::size_t put(std::size_t offset,
std::size_t length, const_vector &from, std::size_t fromOffset);
virtual void shareData(
shared_vector const & value,
std::size_t capacity,
std::size_t length) = 0;
virtual pointer get() = 0;
virtual pointer get() const = 0;
virtual vector const & getVector() = 0;
virtual shared_vector const & getSharedVector() = 0;
std::ostream& dumpValue(std::ostream& o) const;
std::ostream& dumpValue(std::ostream& o, size_t index) const;
...
};
/**
* Definitions for the various scalarArray types.
*/
typedef PVArrayData<uint8> BooleanArrayData;
typedef PVValueArray<uint8> PVBooleanArray;
typedef std::tr1::shared_ptr<PVBooleanArray> PVBooleanArrayPtr;
...
typedef PVArrayData<std::string> StringArrayData;
typedef PVValueArray<std::string> PVStringArray;
typedef std::tr1::shared_ptr<PVStringArray> PVStringArrayPtr;i
class PVArray : public PVField, public SerializableArray {
public:
POINTER_DEFINITIONS(PVArray);
virtual ~PVArray();
virtual void setImmutable();
std::size_t getLength() const;
virtual void setLength(std::size_t length);
std::size_t getCapacity() const;
bool isCapacityMutable() const;
void setCapacityMutable(bool isMutable);
virtual void setCapacity(std::size_t capacity) = 0;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const = 0;
...
};
std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array);
template<typename T>
class PVArrayData {
private:
std::vector<T> init;
public:
POINTER_DEFINITIONS(PVArrayData);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
std::vector<T> & data;
std::size_t offset;
PVArrayData()
: data(init)
{}
};
class PVScalarArray : public PVArray {
public:
POINTER_DEFINITIONS(PVScalarArray);
virtual ~PVScalarArray();
typedef PVScalarArray &reference;
typedef const PVScalarArray& const_reference;
const ScalarArrayConstPtr getScalarArray() const ;
// in pvDataCPP but not in pvDataCPP=md
//virtual std::ostream& dumpValue(std::ostream& o, size_t index) const = 0;
// not in pvDataCPP
template<ScalarType ID>
virtual void
getAs(shared_vector<typename ScalarTypeTraits<ID>::type>& out) const;
virtual void
getAs(ScalarType, shared_vector<void>& out) const = 0;
template<ScalarType ID>
inline size_t copyOut(typename ScalarTypeTraits<ID>::type* inp, size_t len) const;
virtual size_t copyOut(ScalarType id, void* ptr, size_t olen) const = 0;
template<ScalarType ID>
inline void putFrom(const shared_vector<typename ScalarTypeTraits<ID>::type>& inp);
virtual void putFrom(ScalarType, const shared_vector<void>&) = 0;
template<ScalarType ID>
inline void copyIn(const typename ScalarTypeTraits<ID>::type* inp, size_t len);
virtual void copyIn(ScalarType, const void*, size_t) = 0;
virtual void assign(PVScalarArray& pv) = 0;
...
}
template<typename T>
class PVValueArray : public detail::PVVectorStorage {
typedef detail::PVVectorStorage base_t;
public:
POINTER_DEFINITIONS(PVValueArray);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef PVArrayData<T> ArrayDataType;
typedef std::vector<T> vector;
typedef const std::vector<T> const_vector;
typedef std::tr1::shared_ptr<vector> shared_vector;
typedef PVValueArray & reference;
typedef const PVValueArray & const_reference;
virtual ~PVValueArray() {}
virtual std::size_t get(
std::size_t offset, std::size_t length, ArrayDataType &data) = 0;
virtual std::size_t put(std::size_t offset,
std::size_t length, const_pointer from, std::size_t fromOffset) = 0;
virtual std::size_t put(std::size_t offset,
std::size_t length, const_vector &from, std::size_t fromOffset);
virtual void shareData(
shared_vector const & value,
std::size_t capacity,
std::size_t length) = 0;
virtual pointer get() = 0;
virtual pointer get() const = 0;
virtual vector const & getVector() = 0;
virtual shared_vector const & getSharedVector() = 0;
std::ostream& dumpValue(std::ostream& o) const;
std::ostream& dumpValue(std::ostream& o, size_t index) const;
/// not in pvDataCPP
static const ScalarType typeCode;
typedef ::epics::pvData::shared_vector<T> svector;
typedef ::epics::pvData::shared_vector<const T> const_svector;
virtual void
getAs(ScalarType id, ::epics::pvData::shared_vector<void>& out) const;
virtual size_t copyOut(ScalarType id, void* ptr, size_t olen) const;
virtual void
putFrom(ScalarType id, const ::epics::pvData::shared_vector<void>& inp);
virtual void copyIn(ScalarType id, const void* ptr, size_t len);
virtual void assign(PVScalarArray& pv);
protected:
PVValueArray(ScalarArrayConstPtr const & scalar)
: PVScalarArray(scalar) {}
friend class PVDataCreate;
};
template<typename T>
std::size_t PVValueArray<T>::put(
std::size_t offset,
std::size_t length,
const_vector &from,
std::size_t fromOffset)
{ return put(offset,length, &from[0], fromOffset); }
/**
* Definitions for the various scalarArray types.
*/
typedef PVArrayData<uint8> BooleanArrayData;
typedef PVValueArray<uint8> PVBooleanArray;
typedef std::tr1::shared_ptr<PVBooleanArray> PVBooleanArrayPtr;
...
typedef PVArrayData<std::string> StringArrayData;
typedef PVValueArray<std::string> PVStringArray;
typedef std::tr1::shared_ptr<PVStringArray> PVStringArrayPtr;i
class PVArray : public PVField, public SerializableArray {
public:
POINTER_DEFINITIONS(PVArray);
virtual ~PVArray();
virtual std::size_t getLength() const = 0;
virtual void setLength(std::size_t length) = 0;
bool isCapacityMutable() const;
void setCapacityMutable(bool isMutable);
virtual std::size_t getCapacity() const = 0;
virtual void setCapacity(std::size_t capacity) = 0;
...
};
class PVScalarArray : public PVArray {
public:
POINTER_DEFINITIONS(PVScalarArray);
typedef PVScalarArray &reference;
typedef const PVScalarArray& const_reference;
virtual ~PVScalarArray();
const ScalarArrayConstPtr getScalarArray() const;
...
};
template<typename T>
class PVValueArray : public PVScalarArray
{
public:
POINTER_DEFINITIONS(PVValueArray);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef PVValueArray & reference;
typedef const PVValueArray & const_reference;
typedef shared_vector<T> svector;
typedef shared_vector<const T> const_svector;
virtual ~PVValueArray() {}
const svector & get() ;
const const_svector &get() const;
size_t put(size_t offset,size_t length, const_pointer from, size_t fromOffset);
void shareData(const svector &from);
...
};
typedef PVValueArray<uint8> PVBooleanArray;
typedef std::tr1::shared_ptr<PVBooleanArray> PVBooleanArrayPtr;
...
typedef PVValueArray<std::string> PVStringArray;
typedef std::tr1::shared_ptr<PVStringArray> PVStringArrayPtr;
This provides the interface for network accessible data. Although templates are used to minimize the amount of code, the interface is not meant to be extended. Only the types defined by pvIntrospect are implemented.
class PVField : virtual public Serializable, public std::tr1::enable_shared_from_this{ public: POINTER_DEFINITIONS(PVField); virtual ~PVField(); virtual void message(std::string message,MessageType messageType); const std::string& getFieldName() const; virtual void setRequester(RequesterPtr const &prequester); std::size_t getFieldOffset() const; std::size_t getNextFieldOffset() const; std::size_t getNumberFields() const; PVAuxInfoPtr & getPVAuxInfo(); bool isImmutable() const; void setImmutable(); const FieldConstPtr & getField() const; PVStructure * getParent() const; void replacePVField(const PVFieldPtr& newPVField); void renameField(std::string const & newName); void postPut(); void setPostHandler(PostHandlerPtr const &postHandler); virtual bool equals(PVField &pv); void toString(StringBuilder buf) ; void toString(StringBuilder buf,int indentLevel); std::ostream& operator<<(std::ostream& o) const; ... }; std::ostream& operator<<(std::ostream& o, const PVFieldPtr & f); std::ostream& operator<<(std::ostream& o, const PVFieldPtr & f, size_t index);
The public methods for PVField are:
class PVScalar : public PVField {
public:
POINTER_DEFINITIONS(PVScalar);
virtual ~PVScalar();
typedef PVScalar &reference;
typedef const PVScalar& const_reference;
const ScalarConstPtr getScalar() const;
...
};
where
templateclass PVScalarValue : public PVScalar { public: POINTER_DEFINITIONS(PVScalarValue); typedef T value_type; typedef T* pointer; typedef const T* const_pointer; virtual ~PVScalarValue() {} virtual T get() const = 0; virtual void put(T value) = 0; void operator>>=(T& value) const; void operator<<=(T value); ... }; typedef PVScalarValue<uint8> PVBoolean; typedef PVScalarValue<int8> PVByte; typedef PVScalarValue<int16> PVShort; typedef PVScalarValue<int32> PVInt; typedef PVScalarValue<int64> PVLong; typedef PVScalarValue<uint8> PVUByte; typedef PVScalarValue<uint16> PVUShort; typedef PVScalarValue<uint32> PVUInt; typedef PVScalarValue<uint64> PVULong; typedef PVScalarValue<float> PVFloat; typedef PVScalarValue<double> PVDouble; typedef std::tr1::shared_ptr<PVBoolean> PVBooleanPtr; typedef std::tr1::shared_ptr<PVByte> PVBytePtr; typedef std::tr1::shared_ptr<PVShort> PVShortPtr; typedef std::tr1::shared_ptr<PVInt> PVIntPtr; typedef std::tr1::shared_ptr<PVLong> PVLongPtr; typedef std::tr1::shared_ptr<PVUByte> PVUBytePtr; typedef std::tr1::shared_ptr<PVUShort> PVUShortPtr; typedef std::tr1::shared_ptr<PVUInt> PVUIntPtr; typedef std::tr1::shared_ptr<PVULong> PVULongPtr; typedef std::tr1::shared_ptr<PVFloat> PVFloatPtr; typedef std::tr1::shared_ptr<PVDouble> PVDoublePtr; // PVString is special case, since it implements SerializableArray class PVString : public PVScalarValue<std::string>, SerializableArray { public: virtual ~PVString() {} ... };
where
PVArray is the base interface for all the other PV Array interfaces. It extends PVField and provides the additional methods:
class PVArray : public PVField, public SerializableArray {
public:
POINTER_DEFINITIONS(PVArray);
virtual ~PVArray();
virtual std::size_t getLength() const = 0;
virtual void setLength(std::size_t length) = 0;
bool isCapacityMutable() const;
void setCapacityMutable(bool isMutable);
virtual std::size_t getCapacity() const = 0;
virtual void setCapacity(std::size_t capacity) = 0;
...
};
PVScalarArray is the base class for scalar array data. PVValueArray is a templete for the various scalar array data classes. There is a class for each possible scalar type, i. e. PVBooleanArray, ..., PVStringArray.
class PVScalarArray : public PVArray {
public:
POINTER_DEFINITIONS(PVScalarArray);
typedef PVScalarArray &reference;
typedef const PVScalarArray& const_reference;
virtual ~PVScalarArray();
const ScalarArrayConstPtr getScalarArray() const;
...
};
where
This is a template class plus instances for PVBooleanArray, ..., PVStringArray.
template<typename T>
class PVValueArray : public PVScalarArray {
public:
POINTER_DEFINITIONS(PVValueArray);
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef PVValueArray & reference;
typedef const PVValueArray & const_reference;
typedef shared_vector<T> svector;
typedef shared_vector<const T> const_svector;
virtual ~PVValueArray() {}
const svector & get() ;
const const_svector &get() const;
size_t put(size_t offset,size_t length, const_pointer from, size_t fromOffset);
void shareData(const svector &from);
...
};
typedef PVValueArray<uint8> PVBooleanArray;
typedef std::tr1::shared_ptr<PVBooleanArray> PVBooleanArrayPtr;
typedef PVValueArray<int8> PVByteArray;
typedef std::tr1::shared_ptr<PVByteArray> PVByteArrayPtr;
typedef PVValueArray<int16> PVShortArray;
typedef std::tr1::shared_ptr<PVShortArray> PVShortArrayPtr;
typedef PVValueArray<int32> PVIntArray;
typedef std::tr1::shared_ptr<PVIntArray> PVIntArrayPtr;
typedef PVValueArray<int64> PVLongArray;
typedef std::tr1::shared_ptr<PVLongArray> PVLongArrayPtr;
typedef PVValueArray<uint8> PVUByteArray;
typedef std::tr1::shared_ptr<PVUByteArray> PVUByteArrayPtr;
typedef PVValueArray<uint16> PVUShortArray;
typedef std::tr1::shared_ptr<PVUShortArray> PVUShortArrayPtr;
typedef PVValueArray<uint32> PVUIntArray;
typedef std::tr1::shared_ptr<PVUIntArray> PVUIntArrayPtr;
typedef PVValueArray<uint64> PVULongArray;
typedef std::tr1::shared_ptr<PVULongArray> PVULongArrayPtr;
typedef PVValueArray<float> PVFloatArray;
typedef std::tr1::shared_ptr<PVFloatArray> PVFloatArrayPtr;
typedef PVValueArray<double> PVDoubleArray;
typedef std::tr1::shared_ptr<PVDoubleArray> PVDoubleArrayPtr;
typedef PVValueArray<std::string> PVStringArray;
typedef std::tr1::shared_ptr<PVStringArray> PVStringArrayPtr;
where
I think that all public components of sharedVector.h are now documented, but not all have a description or example. Thus the documentation needs more work.
When NOTE EXISTING appears it means that there is a question about the existing shared_vector implementation.
A shared_vector is a container as defined by the C++ standard library. It is like std::vector but provides two additional features 1) shared raw array and 2) a window into the raw array.
To support these two features a shared_vector keeps the following private data:
The following subsections are organized as follows:
The subsections that are compatible with std::vector are organized
and start with a brief summary modeled after Section 31.3(STL Containers) in:
"The C++ Programming Language, C++11, Fourth Edition", Bjarne Stroustrup,2013
The subsection names are the same names that Stroustrup uses.
Each subsection starts with a brief summary that is similar to
the summary Stroustrup has at the beginnining of each subsection.
The comparison is always with std::vector. In addition it shows what is defined by by std::vector but not by shared_vector.
Someone who already understand the C++ STL can understand shared_vector by just looking at the brief summarys. For others the brief summary is followed by tutorial information.
The examples all assume that the following has been defined:
typedef shared_vector<int32> Int32Array; ... static void dumpArray(std::string const &message,Int32Array const& int32Array);
The following:
Int32Array int32Array(5);
dumpArray("example",int32Array);
creates a shared vector that holds an array of five elements where each element is a
32 bit signed integer.
The call to dumpArray displays the message and the array elements on standard out:
example 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
exampleSharedVector is a main program that has the code for the examples shown below.
Brief Summary
value_type Type of element size_type Unsigned type of subscripts, element counts, etc. difference_type Signed type of difference between iterators iterator Behaves like value_type* const_iterator Behaves like const value_type* reverse_iterator Behaves like value_type* const_reverse_iterator Behaves like const value_type* reference value_types& const_reference const_value_type& pointer Behaves like value_type * const_pointer Behaves like const value_type* //not part of std::vector element_type same as value_type shared_pointer_type std::tr1::shared_ptr<value_type> // defined by std::vector but not by shared_vector allocator_type
The typedefs are compatible with the STL container member types. These define types for various types of variables that belong to a container or are used to access a container.
These three typedefs define the same types as the equivalent types for an element of the shared_vector.
Int32Array::value_type value; //is the same as int32 value; Int32Array::reference rvalue = value; //is the same as int32 & rvalue = value; Int32Array::const_reference rvalue = value; //is the same as const int32 & rvalue = value;
The following is an example of code that uses the pointer typedef:
Int32Array int32Array(5); Int32Array::pointer pint32Array = int32Array.data(); size_t len = int32Array.size(); for(size_t i=0; i<len; ++i) pint32Array[i] = i;
A const_pointer is like a pointer except that only read access to the array elements is allowed.
NOTE: data The above code is better implemented as:
Int32Array int32Array(5); size_t len = int32Array.size(); for(size_t i=0; i<len; ++i) int32Array[i] = i;
This is used to get the number of elements between two elements. For example:
Int32Array::difference_type pdiff = int32Array[3] - int32Array[1]; // pdiff will have the value 2
These are member types defined by std::tr1::shared_ptr. These are not used by any of the client methods.
Brief Summary
C c(); Default constructor; c is empty.
C c(n); c is initialized with n elementis with the value value_type{};
offset is 0; size is n;
C c(n,e); Initialize c with n copies of e.
offset is 0; size is n;
C c(c); Copy an existing shared_vector of the same type.
offset and taken same as v.
shared_ptr is copied; not the raw array
C operator=(c) Assignment constructor.
shared_ptr is copied; not the raw array
C c(c,o,n); Copy an existing std::tr1::shared_ptr<value_type>
offset is o; size is c;
C c(r,o,n); Use an existing raw pointer.
default deleter use "delete[]" to delete.
offset is o; size is c;
C c(r,d,o,n); Use an existing raw pointer and deleter d;
offset is o; size is c;
not implemented
~C() The C++ default destructor is used.
C++11 specific
&& constructor move constructor
{} constructor uniform initializer constructor
where
shared_vector(); shared_vector(size_t n); shared_vector(size_t n, value_type e);
The first three constructors all create a new shared_vector by also creating a new raw array, The difference is the size of the array, i.e. how many elements it contains, and how the elements are initalized.
The following:
cout << "***exampleConstructors***" << endl;
Int32Array emptyArray();
Int32Array zeroArray(16);
int32 value = 1;
Int32Array oneArray(8, value);
dumpArray("emptyArray",emptyArray);
dumpArray("zeroArray",zeroArray);
dumpArray("oneArray",oneArray);
produces
***exampleConstructors***
emptyArray 1
zeroArray {16}[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]
oneArray {8}[1, 1, 1, 1, 1, 1, 1, 1]
NOTE EXISTING: Why did emptyArray disply the above. Should it be "emptyArray {0} []"?
shared_vector(const shared_vector& o); shared_vector_base& operator=(const shared_vector_base& o);
These create a vector by coping the contents of an existing shared_vector of the same type into the newly created vector. Note that the complete raw array is not copied but just the std::tr1:: shared_ptr that holds the array.
The following:
cout << "***exampleCopyConstructors***" << endl;
size_t max = 16;
Int32Array int32Array(max);
for(size_t i=0; i<max; ++i) int32Array[i] = i+1;
Int32Array xxx(int32Array); //copy constructor
Int32Array yyy = int32Array; //copy assignment
cout << "dataPtr int32Array " << int32Array.dataPtr();
cout << " xxx " << xxx.dataPtr();
cout << " yyy " << yyy.dataPtr() << endl;
dumpArray("int32Array",emptyArray);
dumpArray("xxx",emptyArray);
dumpArray("yyy",emptyArray);
produces
***exampleConstructors***
dataPtr int32Array 0x136ea90 xxx 0x136ea90 yyy 0x136ea90
int32Array {16}[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...]
xxx {16}[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...]
yyy {16}[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...]
shared_vector(A v, size_t o, size_t c) shared_vector(A d, B b, size_t o, size_t c)
NOTE EXISTING: Are these constructors necessary? If code wants to wrap an existing raw array then a std::tr1::shared_ptr can first be created and the constructor in the next section can be called.
These "wrap" an existing raw pointer. They allows access to a sub-array starting at offset o> and has size c The second provides a destructor and the first has a default deleter.
The default deleter does the following: When the shared_vector is deleted, i. e. when no code references it, the statement "delete[] a;" is executed.
An example of wrapping a raw array without using these constructors is:
class Int32ArrayDeleter
{
//Note that this an example that does nothing.
//But it could have private data
public:
Int32ArrayDeleter() {}
virtual ~Int32ArrayDeleter() {}
void operator()(int32* a){
// MUST HANDLE DELETION
// default is "delete[] a;"
}
};
...
int32 *pother; // something managed by other code
size_t capacity; // size of array managed by other code
Int32Array int32Array(pother,int32array_deleter,0,capacity);
This is used to wrap arrays that are managed by other code. This should only be used if You understand the other code and know what your deleter has to do. An example, exampleShareRawArray, gives a more complete example.
shared_vector(const std::tr1::shared_ptr<E1>& d, size_t o, size_t c)
This creates a vector from an existing smart pointer. Thus the vector will share reference counting with the existing smart pointer. This is useful for creating "windows" into the array that the smart pointer references.
template<typename E1>
shared_vector(const shared_vector<E1>& o) :base_t(o) {}
NOTE EXISTING: I do not understand how this works or what it does.
This create a vector by coping the contents of an existing shared_vector of the same or a different but related type into the newly created vector. This constructor creates a new raw array and copies the elements from the existing array to the new array.
Brief Summary
size() Return the number of elements in the window empty() Is the window empty? this means shared_ptr is empty max_size() The maximum possible number of elements. capacity() The number of possible elements without re-allocating raw array. reserve(n) Reserve at least n elements in raw array; May cause reallocation. resize(n) Change size to n; May cause reallocation clear() shared_ptr is reset, Window will be empty. not implemented resize(n,v) shrink_to_fit()
Details
size_t size() const; bool empty() const; size_t max_size() const; size_t capacity() const; void reserve(size_t n); void resize(size_t n); void clear();
The following:
static void exampleSizeEtc()
{
cout << "***exampleSizeEtc***" << endl;
size_t max = 16;
Int32Array int32Array(max);
size_t capacity = int32Array.capacity();
size_t size = int32Array.size();
bool empty = int32Array.empty();
size_t max_size = int32Array.max_size();
cout<< "capacity" << capacity;
cout<< " size " << size;
cout<< " empty " << (empty ? true : false) ;
cout<< " max_size " << max_size << endl;
}
produces:
***exampleSizeEtc*** capacity16 size 16 empty 0 max_size 18446744073709551615
Brief Summary
begin() First element end() One past last element cbegin() Constant first element cend() Constant last element rbegin() First element of reverse sequence rend() One past last element of reverse sequence crbegin() Constant first element of reverse sequence crend() Constant last element of reverse sequence
shared_vector supports both iterators and reverse iterators as defined by the STL. For both constant iterators are also defined. A constant iterator does not allow an array elemnent to be modified.
The following is an example of a constant iterator.
int32 sum = 0;
for(Int32Array::const_iterator iter=int32Array.begin(); iter<int32Array.end(); ++iter )
{
sum += *iter;
}
The following is an example of a non constant iterator.
int32 value = 0;
for(Int32Array::iterator iter=int32Array.begin(); iter<int32Array.end(); ++iter )
{
*iter += ++value;
}
Brief Summary
operator[i] random element access data() return the raw array implemented by std::vector but not implemented front() back() at()
Note that:
Int32Array::pointer pint32= int32Array.data();is guaranteed to be the same as
int32 * pint32 = int32Array.data();
NOTE EXISTING: data() should be defined to return a const_pointer. It is currently defined to return a plain pointer.
Brief Summary
push_back(x) Add an element after the last element pop_back(x) Remove the last element.
shared_vector does not support the standard list operations like:
implemented by std::vector but not by shared_vector insert(p,x) Add x before p ...
Brief Summary
operator== Do all elements of both containers compare equal. operator!= Does any element not compare equal. operator<< ostream operator swap(c2) Swap the contents of two shared_vectors of the same type. swap(c1,c2) Swap the contents of two shared_vectors of the same type. not implemented operator< operator<= operator> operator>=
template<typename A, typename B>
bool operator==(const epics::pvData::shared_vector<A>& a,
const epics::pvData::shared_vector<B>& b);
template<typename A, typename B>
bool operator!=(const epics::pvData::shared_vector<A>& a,
const epics::pvData::shared_vector<B>& b);
template<typename E>
std::ostream& operator<<(
std::ostream& strm, const epics::pvData::shared_vector<E>& arr);
Swap the contents of two shared_vectors. The following code:
cout << "***exampleSwap***" << endl;
Int32Array first(8);
Int32Array second(16);
cout << " before swap size ";
cout<< "first " << first.size() << " second " << second.size() << endl;
first.swap(second);
cout << " after swap size ";
cout<< "first " << first.size() << " second " << second.size() << endl;
swap(first,second);
cout << " swap again size ";
cout<< "first " << first.size() << " second " << second.size() << endl;
produces:
***exampleSwap*** before swap size first 8 second 16 after swap size first 16 second 8 swap again size first 8 second 16
Brief Summary
void make_unique() Make caller the only user of std::tr1::shared_ptr
bool unique() Is the caller the only user of std::tr1::shared_ptr
void slice(offset,length) Change window offset andsize
// following should only be used for debugging
const std::tr1::shared_ptr<E>&
dataPtr() Return const shared_ptr
size_t dataOffset() Return offset.
size_t dataCount() Return count which is also the size
size_t dataTotal() Return total number of elements between
offset and end of the raw array
// following converts from type FROM to type TO
shared_vector static_shared_vector_cast(const shared_vector<FROM>& src);
// following casts from const Type to Type
shared_vector
const_shared_vector_cast(const shared_vector<const TYPE>& src)
NOTE EXISTING: The C++ standard library considers a slice to be every nth element of some part of an array, i. e., slice has arguments (offset,length,stride). shared_vector only has offset and length. Perhaps it should have another name like rewindow.
void make_unique(); bool unique() const;
void slice(size_t offset, size_t length=(size_t)-1);
This modifies the "window" into the raw array starting at offset and of the specified length. The offset and length are forced to be within the raw array. Note that this method never reallocates the underlying raw array.
The following code:
static void exampleSlice()
{
cout << "***exampleSlice***" << endl;
size_t max = 16;
Int32Array int32Array(max);
int32 value = 0;
for(Int32Array::iterator iter = int32Array.begin(); iter!=int32Array.end(); ++iter)
{
*iter = ++value;
}
dumpArray("int32Array",int32Array);
size_t offset = 0;
size_t length = 8;
Int32Array window1(int32Array);
window1.slice(offset,length);
dumpArray("window1",window1);
offset = 8;
length = 8;
Int32Array window2(int32Array);
window2.slice(offset,length);
dumpArray("window2",window2);
offset = 2;
length = 4;
Int32Array window3(window2);
window3.slice(offset,length);
dumpArray("window3",window3);
}
produces the following output:
***exampleSlice*** int32Array 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 window1 1 2 3 4 5 6 7 8 window2 9 10 11 12 13 14 15 16 window3 11 12 13 14
The following:
static void exampleDataEtc()
{
cout << "***exampleDataEtc***" << endl;
size_t max = 16;
Int32Array int32Array(max);
long use_count = int32Array.dataPtr().use_count();
long offset = int32Array.dataOffset();
long count = int32Array.dataCount();
long total = int32Array.dataTotal();
cout << "use_count " << use_count;
cout << " offset " << offset;
cout << " count " << count;
cout << " total " << total << endl;
}
produces:
***exampleDataEtc*** use_count 1 offset 0 count 16 total 16
Not yet documented
Not yet documented
Not yet documented